omap24xx: Fix missing symbols for mac80211
[openwrt.git] / target / linux / omap24xx / patches-2.6.38 / 200-omap-platform.patch
1 Index: linux-2.6.38-rc6/arch/arm/plat-omap/bootreason.c
2 ===================================================================
3 --- /dev/null   1970-01-01 00:00:00.000000000 +0000
4 +++ linux-2.6.38-rc6/arch/arm/plat-omap/bootreason.c    2011-02-25 01:11:27.298563256 +0100
5 @@ -0,0 +1,79 @@
6 +/*
7 + * linux/arch/arm/plat-omap/bootreason.c
8 + *
9 + * OMAP Bootreason passing
10 + *
11 + * Copyright (c) 2004 Nokia
12 + *
13 + * Written by David Weinehall <david.weinehall@nokia.com>
14 + *
15 + * This program is free software; you can redistribute it and/or modify it
16 + * under the terms of the GNU General Public License as published by the
17 + * Free Software Foundation; either version 2 of the License, or (at your
18 + * option) any later version.
19 + *
20 + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
21 + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
22 + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
23 + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24 + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
26 + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
27 + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 + *
31 + * You should have received a copy of the GNU General Public License along
32 + * with this program; if not, write to the Free Software Foundation, Inc.,
33 + * 675 Mass Ave, Cambridge, MA 02139, USA.
34 + */
35 +#include <linux/proc_fs.h>
36 +#include <linux/errno.h>
37 +#include <plat/board.h>
38 +
39 +static char boot_reason[16];
40 +
41 +static int omap_bootreason_read_proc(char *page, char **start, off_t off,
42 +                                        int count, int *eof, void *data)
43 +{
44 +       int len = 0;
45 +
46 +       len += sprintf(page + len, "%s\n", boot_reason);
47 +
48 +       *start = page + off;
49 +
50 +       if (len > off)
51 +               len -= off;
52 +       else
53 +               len = 0;
54 +
55 +       return len < count ? len  : count;
56 +}
57 +
58 +static int __init bootreason_init(void)
59 +{
60 +       const struct omap_boot_reason_config *cfg;
61 +       int reason_valid = 0;
62 +
63 +       cfg = omap_get_config(OMAP_TAG_BOOT_REASON, struct omap_boot_reason_config);
64 +       if (cfg != NULL) {
65 +               strncpy(boot_reason, cfg->reason_str, sizeof(cfg->reason_str));
66 +               boot_reason[sizeof(cfg->reason_str)] = 0;
67 +               reason_valid = 1;
68 +       } else {
69 +               /* Read the boot reason from the OMAP registers */
70 +       }
71 +
72 +       if (!reason_valid)
73 +               return -ENOENT;
74 +
75 +       printk(KERN_INFO "Bootup reason: %s\n", boot_reason);
76 +
77 +       if (!create_proc_read_entry("bootreason", S_IRUGO, NULL,
78 +                                       omap_bootreason_read_proc, NULL))
79 +               return -ENOMEM;
80 +
81 +       return 0;
82 +}
83 +
84 +late_initcall(bootreason_init);
85 Index: linux-2.6.38-rc6/arch/arm/plat-omap/common.c
86 ===================================================================
87 --- linux-2.6.38-rc6.orig/arch/arm/plat-omap/common.c   2011-02-25 01:10:25.645115298 +0100
88 +++ linux-2.6.38-rc6/arch/arm/plat-omap/common.c        2011-02-25 01:11:27.298563256 +0100
89 @@ -21,17 +21,89 @@
90  #include <plat/vram.h>
91  #include <plat/dsp.h>
92  
93 +#include <asm/setup.h>
94 +
95  
96  #define NO_LENGTH_CHECK 0xffffffff
97  
98  struct omap_board_config_kernel *omap_board_config;
99  int omap_board_config_size;
100  
101 +unsigned char omap_bootloader_tag[1024];
102 +int omap_bootloader_tag_len;
103 +
104 +/* used by omap-smp.c and board-4430sdp.c */
105 +void __iomem *gic_cpu_base_addr;
106 +
107 +#ifdef CONFIG_OMAP_BOOT_TAG
108 +
109 +static int __init parse_tag_omap(const struct tag *tag)
110 +{
111 +       u32 size = tag->hdr.size - (sizeof(tag->hdr) >> 2);
112 +
113 +        size <<= 2;
114 +       if (size > sizeof(omap_bootloader_tag))
115 +               return -1;
116 +
117 +       memcpy(omap_bootloader_tag, tag->u.omap.data, size);
118 +       omap_bootloader_tag_len = size;
119 +
120 +        return 0;
121 +}
122 +
123 +__tagtable(ATAG_BOARD, parse_tag_omap);
124 +
125 +#endif
126 +
127  static const void *get_config(u16 tag, size_t len, int skip, size_t *len_out)
128  {
129         struct omap_board_config_kernel *kinfo = NULL;
130         int i;
131  
132 +#ifdef CONFIG_OMAP_BOOT_TAG
133 +       struct omap_board_config_entry *info = NULL;
134 +
135 +       if (omap_bootloader_tag_len > 4)
136 +               info = (struct omap_board_config_entry *) omap_bootloader_tag;
137 +       while (info != NULL) {
138 +               u8 *next;
139 +
140 +               if (info->tag == tag) {
141 +                       if (skip == 0)
142 +                               break;
143 +                       skip--;
144 +               }
145 +
146 +               if ((info->len & 0x03) != 0) {
147 +                       /* We bail out to avoid an alignment fault */
148 +                       printk(KERN_ERR "OMAP peripheral config: Length (%d) not word-aligned (tag %04x)\n",
149 +                              info->len, info->tag);
150 +                       return NULL;
151 +               }
152 +               next = (u8 *) info + sizeof(*info) + info->len;
153 +               if (next >= omap_bootloader_tag + omap_bootloader_tag_len)
154 +                       info = NULL;
155 +               else
156 +                       info = (struct omap_board_config_entry *) next;
157 +       }
158 +       if (info != NULL) {
159 +               /* Check the length as a lame attempt to check for
160 +                * binary inconsistency. */
161 +               if (len != NO_LENGTH_CHECK) {
162 +                       /* Word-align len */
163 +                       if (len & 0x03)
164 +                               len = (len + 3) & ~0x03;
165 +                       if (info->len != len) {
166 +                               printk(KERN_ERR "OMAP peripheral config: Length mismatch with tag %x (want %d, got %d)\n",
167 +                                      tag, len, info->len);
168 +                               return NULL;
169 +                       }
170 +               }
171 +               if (len_out != NULL)
172 +                       *len_out = info->len;
173 +               return info->data;
174 +       }
175 +#endif
176         /* Try to find the config from the board-specific structures
177          * in the kernel. */
178         for (i = 0; i < omap_board_config_size; i++) {
179 Index: linux-2.6.38-rc6/arch/arm/plat-omap/component-version.c
180 ===================================================================
181 --- /dev/null   1970-01-01 00:00:00.000000000 +0000
182 +++ linux-2.6.38-rc6/arch/arm/plat-omap/component-version.c     2011-02-25 01:11:27.299563117 +0100
183 @@ -0,0 +1,64 @@
184 +/*
185 + *  linux/arch/arm/plat-omap/component-version.c
186 + *
187 + *  Copyright (C) 2005 Nokia Corporation
188 + *  Written by Juha Yrjölä <juha.yrjola@nokia.com>
189 + *
190 + * This program is free software; you can redistribute it and/or modify
191 + * it under the terms of the GNU General Public License version 2 as
192 + * published by the Free Software Foundation.
193 + */
194 +
195 +#include <linux/init.h>
196 +#include <linux/module.h>
197 +#include <linux/err.h>
198 +#include <linux/proc_fs.h>
199 +#include <plat/board.h>
200 +
201 +static int component_version_read_proc(char *page, char **start, off_t off,
202 +                                      int count, int *eof, void *data)
203 +{
204 +       int len, i;
205 +       const struct omap_version_config *ver;
206 +       char *p;
207 +
208 +       i = 0;
209 +       p = page;
210 +       while ((ver = omap_get_nr_config(OMAP_TAG_VERSION_STR,
211 +                                        struct omap_version_config, i)) != NULL) {
212 +               p += sprintf(p, "%-12s%s\n", ver->component, ver->version);
213 +               i++;
214 +       }
215 +
216 +       len = (p - page) - off;
217 +       if (len < 0)
218 +               len = 0;
219 +
220 +       *eof = (len <= count) ? 1 : 0;
221 +       *start = page + off;
222 +
223 +       return len;
224 +}
225 +
226 +static int __init component_version_init(void)
227 +{
228 +       if (omap_get_config(OMAP_TAG_VERSION_STR, struct omap_version_config) == NULL)
229 +               return -ENODEV;
230 +       if (!create_proc_read_entry("component_version", S_IRUGO, NULL,
231 +                                   component_version_read_proc, NULL))
232 +               return -ENOMEM;
233 +
234 +       return 0;
235 +}
236 +
237 +static void __exit component_version_exit(void)
238 +{
239 +       remove_proc_entry("component_version", NULL);
240 +}
241 +
242 +late_initcall(component_version_init);
243 +module_exit(component_version_exit);
244 +
245 +MODULE_AUTHOR("Juha Yrjölä <juha.yrjola@nokia.com>");
246 +MODULE_DESCRIPTION("Component version driver");
247 +MODULE_LICENSE("GPL");
248 Index: linux-2.6.38-rc6/arch/arm/plat-omap/Kconfig
249 ===================================================================
250 --- linux-2.6.38-rc6.orig/arch/arm/plat-omap/Kconfig    2011-02-25 01:10:25.621118611 +0100
251 +++ linux-2.6.38-rc6/arch/arm/plat-omap/Kconfig 2011-02-25 01:11:27.299563117 +0100
252 @@ -79,6 +79,38 @@
253           probably do not want this option enabled until your
254           device drivers work properly.
255  
256 +config OMAP_BOOT_TAG
257 +       bool "OMAP bootloader information passing"
258 +        depends on ARCH_OMAP
259 +        default n
260 +        help
261 +          Say Y, if you have a bootloader which passes information
262 +          about your board and its peripheral configuration.
263 +
264 +config OMAP_BOOT_REASON
265 +       bool "Support for boot reason"
266 +        depends on OMAP_BOOT_TAG
267 +        default n
268 +        help
269 +          Say Y, if you want to have a procfs entry for reading the boot
270 +          reason in user-space.
271 +
272 +config OMAP_COMPONENT_VERSION
273 +       bool "Support for component version display"
274 +       depends on OMAP_BOOT_TAG && PROC_FS
275 +       default n
276 +       help
277 +         Say Y, if you want to have a procfs entry for reading component
278 +         versions (supplied by the bootloader) in user-space.
279 +
280 +config OMAP_GPIO_SWITCH
281 +       bool "GPIO switch support"
282 +       help
283 +         Say Y, if you want to have support for reporting of GPIO
284 +         switches (e.g. cover switches) via sysfs. Your bootloader has
285 +         to provide information about the switches to the kernel via the
286 +         ATAG_BOARD mechanism if they're not defined by the board config.
287 +
288  config OMAP_MUX
289         bool "OMAP multiplexing support"
290         depends on ARCH_OMAP
291 Index: linux-2.6.38-rc6/arch/arm/plat-omap/Makefile
292 ===================================================================
293 --- linux-2.6.38-rc6.orig/arch/arm/plat-omap/Makefile   2011-02-25 01:10:25.604120958 +0100
294 +++ linux-2.6.38-rc6/arch/arm/plat-omap/Makefile        2011-02-25 01:11:27.299563117 +0100
295 @@ -23,6 +23,9 @@
296  
297  obj-$(CONFIG_CPU_FREQ) += cpu-omap.o
298  obj-$(CONFIG_OMAP_DM_TIMER) += dmtimer.o
299 +obj-$(CONFIG_OMAP_BOOT_REASON) += bootreason.o
300 +obj-$(CONFIG_OMAP_COMPONENT_VERSION) += component-version.o
301 +obj-$(CONFIG_OMAP_GPIO_SWITCH) += gpio-switch.o
302  obj-$(CONFIG_OMAP_DEBUG_DEVICES) += debug-devices.o
303  obj-$(CONFIG_OMAP_DEBUG_LEDS) += debug-leds.o
304  i2c-omap-$(CONFIG_I2C_OMAP) := i2c.o
305 Index: linux-2.6.38-rc6/arch/arm/include/asm/setup.h
306 ===================================================================
307 --- linux-2.6.38-rc6.orig/arch/arm/include/asm/setup.h  2011-02-25 01:10:25.523132140 +0100
308 +++ linux-2.6.38-rc6/arch/arm/include/asm/setup.h       2011-02-25 01:11:27.300562978 +0100
309 @@ -136,6 +136,13 @@
310         __u8 adfsdrives;
311  };
312  
313 +/* TI OMAP specific information */
314 +#define ATAG_BOARD       0x414f4d50
315 +
316 +struct tag_omap {
317 +       u8 data[0];
318 +};
319 +
320  /* footbridge memory clock, see arch/arm/mach-footbridge/arch.c */
321  #define ATAG_MEMCLK    0x41000402
322  
323 @@ -162,6 +169,11 @@
324                 struct tag_acorn        acorn;
325  
326                 /*
327 +                * OMAP specific
328 +                 */
329 +                struct tag_omap         omap;
330 +
331 +               /*
332                  * DC21285 specific
333                  */
334                 struct tag_memclk       memclk;
335 Index: linux-2.6.38-rc6/arch/arm/plat-omap/gpio-switch.c
336 ===================================================================
337 --- /dev/null   1970-01-01 00:00:00.000000000 +0000
338 +++ linux-2.6.38-rc6/arch/arm/plat-omap/gpio-switch.c   2011-02-25 01:11:27.301562839 +0100
339 @@ -0,0 +1,554 @@
340 +/*
341 + *  linux/arch/arm/plat-omap/gpio-switch.c
342 + *
343 + *  Copyright (C) 2004-2006 Nokia Corporation
344 + *  Written by Juha Yrjölä <juha.yrjola@nokia.com>
345 + *         and Paul Mundt <paul.mundt@nokia.com>
346 + *
347 + * This program is free software; you can redistribute it and/or modify
348 + * it under the terms of the GNU General Public License version 2 as
349 + * published by the Free Software Foundation.
350 + */
351 +
352 +#include <linux/sched.h>
353 +#include <linux/init.h>
354 +#include <linux/list.h>
355 +#include <linux/irq.h>
356 +#include <linux/interrupt.h>
357 +#include <linux/module.h>
358 +#include <linux/platform_device.h>
359 +#include <linux/timer.h>
360 +#include <linux/err.h>
361 +#include <linux/slab.h>
362 +#include <linux/gpio.h>
363 +#include <plat/hardware.h>
364 +#include <plat/irqs.h>
365 +#include <plat/mux.h>
366 +#include <plat/board.h>
367 +#include <plat/gpio-switch.h>
368 +
369 +struct gpio_switch {
370 +       char            name[14];
371 +       u16             gpio;
372 +       unsigned        flags:4;
373 +       unsigned        type:4;
374 +       unsigned        state:1;
375 +       unsigned        both_edges:1;
376 +
377 +       u16             debounce_rising;
378 +       u16             debounce_falling;
379 +
380 +       void (* notify)(void *data, int state);
381 +       void *notify_data;
382 +
383 +       struct work_struct      work;
384 +       struct timer_list       timer;
385 +       struct platform_device  pdev;
386 +
387 +       struct list_head        node;
388 +};
389 +
390 +static LIST_HEAD(gpio_switches);
391 +static struct platform_device *gpio_sw_platform_dev;
392 +static struct platform_driver gpio_sw_driver;
393 +
394 +static const struct omap_gpio_switch *board_gpio_sw_table;
395 +static int board_gpio_sw_count;
396 +
397 +static const char *cover_str[2] = { "open", "closed" };
398 +static const char *connection_str[2] = { "disconnected", "connected" };
399 +static const char *activity_str[2] = { "inactive", "active" };
400 +
401 +/*
402 + * GPIO switch state default debounce delay in ms
403 + */
404 +#define OMAP_GPIO_SW_DEFAULT_DEBOUNCE          10
405 +
406 +static const char **get_sw_str(struct gpio_switch *sw)
407 +{
408 +       switch (sw->type) {
409 +       case OMAP_GPIO_SWITCH_TYPE_COVER:
410 +               return cover_str;
411 +       case OMAP_GPIO_SWITCH_TYPE_CONNECTION:
412 +               return connection_str;
413 +       case OMAP_GPIO_SWITCH_TYPE_ACTIVITY:
414 +               return activity_str;
415 +       default:
416 +               BUG();
417 +               return NULL;
418 +       }
419 +}
420 +
421 +static const char *get_sw_type(struct gpio_switch *sw)
422 +{
423 +       switch (sw->type) {
424 +       case OMAP_GPIO_SWITCH_TYPE_COVER:
425 +               return "cover";
426 +       case OMAP_GPIO_SWITCH_TYPE_CONNECTION:
427 +               return "connection";
428 +       case OMAP_GPIO_SWITCH_TYPE_ACTIVITY:
429 +               return "activity";
430 +       default:
431 +               BUG();
432 +               return NULL;
433 +       }
434 +}
435 +
436 +static void print_sw_state(struct gpio_switch *sw, int state)
437 +{
438 +       const char **str;
439 +
440 +       str = get_sw_str(sw);
441 +       if (str != NULL)
442 +               printk(KERN_INFO "%s (GPIO %d) is now %s\n", sw->name, sw->gpio, str[state]);
443 +}
444 +
445 +static int gpio_sw_get_state(struct gpio_switch *sw)
446 +{
447 +       int state;
448 +
449 +       state = gpio_get_value(sw->gpio);
450 +       if (sw->flags & OMAP_GPIO_SWITCH_FLAG_INVERTED)
451 +               state = !state;
452 +
453 +       return state;
454 +}
455 +
456 +static ssize_t gpio_sw_state_store(struct device *dev,
457 +                                  struct device_attribute *attr,
458 +                                  const char *buf,
459 +                                  size_t count)
460 +{
461 +       struct gpio_switch *sw = dev_get_drvdata(dev);
462 +       const char **str;
463 +       char state[16];
464 +       int enable;
465 +
466 +       if (!(sw->flags & OMAP_GPIO_SWITCH_FLAG_OUTPUT))
467 +               return -EPERM;
468 +
469 +       if (sscanf(buf, "%15s", state) != 1)
470 +               return -EINVAL;
471 +
472 +       str = get_sw_str(sw);
473 +       if (strcmp(state, str[0]) == 0)
474 +               sw->state = enable = 0;
475 +       else if (strcmp(state, str[1]) == 0)
476 +               sw->state = enable = 1;
477 +       else
478 +               return -EINVAL;
479 +
480 +       if (sw->flags & OMAP_GPIO_SWITCH_FLAG_INVERTED)
481 +               enable = !enable;
482 +       gpio_set_value(sw->gpio, enable);
483 +
484 +       return count;
485 +}
486 +
487 +static ssize_t gpio_sw_state_show(struct device *dev,
488 +                                 struct device_attribute *attr,
489 +                                 char *buf)
490 +{
491 +       struct gpio_switch *sw = dev_get_drvdata(dev);
492 +       const char **str;
493 +
494 +       str = get_sw_str(sw);
495 +       return sprintf(buf, "%s\n", str[sw->state]);
496 +}
497 +
498 +static DEVICE_ATTR(state, S_IRUGO | S_IWUSR, gpio_sw_state_show,
499 +                  gpio_sw_state_store);
500 +
501 +static ssize_t gpio_sw_type_show(struct device *dev,
502 +                                struct device_attribute *attr,
503 +                                char *buf)
504 +{
505 +       struct gpio_switch *sw = dev_get_drvdata(dev);
506 +
507 +       return sprintf(buf, "%s\n", get_sw_type(sw));
508 +}
509 +
510 +static DEVICE_ATTR(type, S_IRUGO, gpio_sw_type_show, NULL);
511 +
512 +static ssize_t gpio_sw_direction_show(struct device *dev,
513 +                                     struct device_attribute *attr,
514 +                                     char *buf)
515 +{
516 +       struct gpio_switch *sw = dev_get_drvdata(dev);
517 +       int is_output;
518 +
519 +       is_output = sw->flags & OMAP_GPIO_SWITCH_FLAG_OUTPUT;
520 +       return sprintf(buf, "%s\n", is_output ? "output" : "input");
521 +}
522 +
523 +static DEVICE_ATTR(direction, S_IRUGO, gpio_sw_direction_show, NULL);
524 +
525 +
526 +static irqreturn_t gpio_sw_irq_handler(int irq, void *arg)
527 +{
528 +       struct gpio_switch *sw = arg;
529 +       unsigned long timeout;
530 +       int state;
531 +
532 +       if (!sw->both_edges) {
533 +               if (gpio_get_value(sw->gpio))
534 +                       set_irq_type(OMAP_GPIO_IRQ(sw->gpio), IRQ_TYPE_EDGE_FALLING);
535 +               else
536 +                       set_irq_type(OMAP_GPIO_IRQ(sw->gpio), IRQ_TYPE_EDGE_RISING);
537 +       }
538 +
539 +       state = gpio_sw_get_state(sw);
540 +       if (sw->state == state)
541 +               return IRQ_HANDLED;
542 +
543 +       if (state)
544 +               timeout = sw->debounce_rising;
545 +       else
546 +               timeout = sw->debounce_falling;
547 +       if (!timeout)
548 +               schedule_work(&sw->work);
549 +       else
550 +               mod_timer(&sw->timer, jiffies + msecs_to_jiffies(timeout));
551 +
552 +       return IRQ_HANDLED;
553 +}
554 +
555 +static void gpio_sw_timer(unsigned long arg)
556 +{
557 +       struct gpio_switch *sw = (struct gpio_switch *) arg;
558 +
559 +       schedule_work(&sw->work);
560 +}
561 +
562 +static void gpio_sw_handler(struct work_struct *work)
563 +{
564 +       struct gpio_switch *sw = container_of(work, struct gpio_switch, work);
565 +       int state;
566 +
567 +       state = gpio_sw_get_state(sw);
568 +       if (sw->state == state)
569 +               return;
570 +
571 +       sw->state = state;
572 +       if (sw->notify != NULL)
573 +               sw->notify(sw->notify_data, state);
574 +       sysfs_notify(&sw->pdev.dev.kobj, NULL, "state");
575 +       print_sw_state(sw, state);
576 +}
577 +
578 +static int __init can_do_both_edges(struct gpio_switch *sw)
579 +{
580 +       if (!cpu_class_is_omap1())
581 +               return 1;
582 +       if (OMAP_GPIO_IS_MPUIO(sw->gpio))
583 +               return 0;
584 +       else
585 +               return 1;
586 +}
587 +
588 +static void gpio_sw_release(struct device *dev)
589 +{
590 +}
591 +
592 +static int __init new_switch(struct gpio_switch *sw)
593 +{
594 +       int r, direction, trigger;
595 +
596 +       switch (sw->type) {
597 +       case OMAP_GPIO_SWITCH_TYPE_COVER:
598 +       case OMAP_GPIO_SWITCH_TYPE_CONNECTION:
599 +       case OMAP_GPIO_SWITCH_TYPE_ACTIVITY:
600 +               break;
601 +       default:
602 +               printk(KERN_ERR "invalid GPIO switch type: %d\n", sw->type);
603 +               return -EINVAL;
604 +       }
605 +
606 +       sw->pdev.name   = sw->name;
607 +       sw->pdev.id     = -1;
608 +
609 +       sw->pdev.dev.parent = &gpio_sw_platform_dev->dev;
610 +       sw->pdev.dev.driver = &gpio_sw_driver.driver;
611 +       sw->pdev.dev.release = gpio_sw_release;
612 +
613 +       r = platform_device_register(&sw->pdev);
614 +       if (r) {
615 +               printk(KERN_ERR "gpio-switch: platform device registration "
616 +                      "failed for %s", sw->name);
617 +               return r;
618 +       }
619 +       dev_set_drvdata(&sw->pdev.dev, sw);
620 +
621 +       r = gpio_request(sw->gpio, "gpio-switch");
622 +       if (r < 0) {
623 +               platform_device_unregister(&sw->pdev);
624 +               return r;
625 +       }
626 +
627 +       /* input: 1, output: 0 */
628 +       direction = !(sw->flags & OMAP_GPIO_SWITCH_FLAG_OUTPUT);
629 +       if (direction)
630 +               gpio_direction_input(sw->gpio);
631 +       else
632 +               gpio_direction_output(sw->gpio, 0);
633 +
634 +       sw->state = gpio_sw_get_state(sw);
635 +
636 +       r = 0;
637 +       r |= device_create_file(&sw->pdev.dev, &dev_attr_state);
638 +       r |= device_create_file(&sw->pdev.dev, &dev_attr_type);
639 +       r |= device_create_file(&sw->pdev.dev, &dev_attr_direction);
640 +       if (r)
641 +               printk(KERN_ERR "gpio-switch: attribute file creation "
642 +                      "failed for %s\n", sw->name);
643 +
644 +       if (!direction)
645 +               return 0;
646 +
647 +       if (can_do_both_edges(sw)) {
648 +               trigger = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING;
649 +               sw->both_edges = 1;
650 +       } else {
651 +               if (gpio_get_value(sw->gpio))
652 +                       trigger = IRQF_TRIGGER_FALLING;
653 +               else
654 +                       trigger = IRQF_TRIGGER_RISING;
655 +       }
656 +       r = request_irq(OMAP_GPIO_IRQ(sw->gpio), gpio_sw_irq_handler,
657 +                       IRQF_SHARED | trigger, sw->name, sw);
658 +       if (r < 0) {
659 +               printk(KERN_ERR "gpio-switch: request_irq() failed "
660 +                      "for GPIO %d\n", sw->gpio);
661 +               platform_device_unregister(&sw->pdev);
662 +               gpio_free(sw->gpio);
663 +               return r;
664 +       }
665 +
666 +       INIT_WORK(&sw->work, gpio_sw_handler);
667 +       init_timer(&sw->timer);
668 +
669 +       sw->timer.function = gpio_sw_timer;
670 +       sw->timer.data = (unsigned long)sw;
671 +
672 +       list_add(&sw->node, &gpio_switches);
673 +
674 +       return 0;
675 +}
676 +
677 +static int __init add_atag_switches(void)
678 +{
679 +       const struct omap_gpio_switch_config *cfg;
680 +       struct gpio_switch *sw;
681 +       int i, r;
682 +
683 +       for (i = 0; ; i++) {
684 +               cfg = omap_get_nr_config(OMAP_TAG_GPIO_SWITCH,
685 +                                        struct omap_gpio_switch_config, i);
686 +               if (cfg == NULL)
687 +                       break;
688 +               sw = kzalloc(sizeof(*sw), GFP_KERNEL);
689 +               if (sw == NULL) {
690 +                       printk(KERN_ERR "gpio-switch: kmalloc failed\n");
691 +                       return -ENOMEM;
692 +               }
693 +               strncpy(sw->name, cfg->name, sizeof(cfg->name));
694 +               sw->gpio = cfg->gpio;
695 +               sw->flags = cfg->flags;
696 +               sw->type = cfg->type;
697 +               sw->debounce_rising = OMAP_GPIO_SW_DEFAULT_DEBOUNCE;
698 +               sw->debounce_falling = OMAP_GPIO_SW_DEFAULT_DEBOUNCE;
699 +               if ((r = new_switch(sw)) < 0) {
700 +                       kfree(sw);
701 +                       return r;
702 +               }
703 +       }
704 +       return 0;
705 +}
706 +
707 +static struct gpio_switch * __init find_switch(int gpio, const char *name)
708 +{
709 +       struct gpio_switch *sw;
710 +
711 +       list_for_each_entry(sw, &gpio_switches, node) {
712 +               if ((gpio < 0 || sw->gpio != gpio) &&
713 +                   (name == NULL || strcmp(sw->name, name) != 0))
714 +                       continue;
715 +
716 +               if (gpio < 0 || name == NULL)
717 +                       goto no_check;
718 +
719 +               if (strcmp(sw->name, name) != 0)
720 +                       printk("gpio-switch: name mismatch for %d (%s, %s)\n",
721 +                              gpio, name, sw->name);
722 +               else if (sw->gpio != gpio)
723 +                       printk("gpio-switch: GPIO mismatch for %s (%d, %d)\n",
724 +                              name, gpio, sw->gpio);
725 +no_check:
726 +               return sw;
727 +       }
728 +       return NULL;
729 +}
730 +
731 +static int __init add_board_switches(void)
732 +{
733 +       int i;
734 +
735 +       for (i = 0; i < board_gpio_sw_count; i++) {
736 +               const struct omap_gpio_switch *cfg;
737 +               struct gpio_switch *sw;
738 +               int r;
739 +
740 +               cfg = board_gpio_sw_table + i;
741 +               if (strlen(cfg->name) > sizeof(sw->name) - 1)
742 +                       return -EINVAL;
743 +               /* Check whether we only update an existing switch
744 +                * or add a new switch. */
745 +               sw = find_switch(cfg->gpio, cfg->name);
746 +               if (sw != NULL) {
747 +                       sw->debounce_rising = cfg->debounce_rising;
748 +                       sw->debounce_falling = cfg->debounce_falling;
749 +                       sw->notify = cfg->notify;
750 +                       sw->notify_data = cfg->notify_data;
751 +                       continue;
752 +               } else {
753 +                       if (cfg->gpio < 0 || cfg->name == NULL) {
754 +                               printk("gpio-switch: required switch not "
755 +                                      "found (%d, %s)\n", cfg->gpio,
756 +                                      cfg->name);
757 +                               continue;
758 +                       }
759 +               }
760 +               sw = kzalloc(sizeof(*sw), GFP_KERNEL);
761 +               if (sw == NULL) {
762 +                       printk(KERN_ERR "gpio-switch: kmalloc failed\n");
763 +                       return -ENOMEM;
764 +               }
765 +               strlcpy(sw->name, cfg->name, sizeof(sw->name));
766 +               sw->gpio = cfg->gpio;
767 +               sw->flags = cfg->flags;
768 +               sw->type = cfg->type;
769 +               sw->debounce_rising = cfg->debounce_rising;
770 +               sw->debounce_falling = cfg->debounce_falling;
771 +               sw->notify = cfg->notify;
772 +               sw->notify_data = cfg->notify_data;
773 +               if ((r = new_switch(sw)) < 0) {
774 +                       kfree(sw);
775 +                       return r;
776 +               }
777 +       }
778 +       return 0;
779 +}
780 +
781 +static void gpio_sw_cleanup(void)
782 +{
783 +       struct gpio_switch *sw = NULL, *old = NULL;
784 +
785 +       list_for_each_entry(sw, &gpio_switches, node) {
786 +               if (old != NULL)
787 +                       kfree(old);
788 +               flush_scheduled_work();
789 +               del_timer_sync(&sw->timer);
790 +
791 +               free_irq(OMAP_GPIO_IRQ(sw->gpio), sw);
792 +
793 +               device_remove_file(&sw->pdev.dev, &dev_attr_state);
794 +               device_remove_file(&sw->pdev.dev, &dev_attr_type);
795 +               device_remove_file(&sw->pdev.dev, &dev_attr_direction);
796 +
797 +               platform_device_unregister(&sw->pdev);
798 +               gpio_free(sw->gpio);
799 +               old = sw;
800 +       }
801 +       kfree(old);
802 +}
803 +
804 +static void __init report_initial_state(void)
805 +{
806 +       struct gpio_switch *sw;
807 +
808 +       list_for_each_entry(sw, &gpio_switches, node) {
809 +               int state;
810 +
811 +               state = gpio_get_value(sw->gpio);
812 +               if (sw->flags & OMAP_GPIO_SWITCH_FLAG_INVERTED)
813 +                       state = !state;
814 +               if (sw->notify != NULL)
815 +                       sw->notify(sw->notify_data, state);
816 +               print_sw_state(sw, state);
817 +       }
818 +}
819 +
820 +static int gpio_sw_remove(struct platform_device *dev)
821 +{
822 +       return 0;
823 +}
824 +
825 +static struct platform_driver gpio_sw_driver = {
826 +       .remove         = gpio_sw_remove,
827 +       .driver         = {
828 +               .name   = "gpio-switch",
829 +       },
830 +};
831 +
832 +void __init omap_register_gpio_switches(const struct omap_gpio_switch *tbl,
833 +                                       int count)
834 +{
835 +       BUG_ON(board_gpio_sw_table != NULL);
836 +
837 +       board_gpio_sw_table = tbl;
838 +       board_gpio_sw_count = count;
839 +}
840 +
841 +static int __init gpio_sw_init(void)
842 +{
843 +       int r;
844 +
845 +       printk(KERN_INFO "OMAP GPIO switch handler initializing\n");
846 +
847 +       r = platform_driver_register(&gpio_sw_driver);
848 +       if (r)
849 +               return r;
850 +
851 +       gpio_sw_platform_dev = platform_device_register_simple("gpio-switch",
852 +                                                              -1, NULL, 0);
853 +       if (IS_ERR(gpio_sw_platform_dev)) {
854 +               r = PTR_ERR(gpio_sw_platform_dev);
855 +               goto err1;
856 +       }
857 +
858 +       r = add_atag_switches();
859 +       if (r < 0)
860 +               goto err2;
861 +
862 +       r = add_board_switches();
863 +       if (r < 0)
864 +               goto err2;
865 +
866 +       report_initial_state();
867 +
868 +       return 0;
869 +err2:
870 +       gpio_sw_cleanup();
871 +       platform_device_unregister(gpio_sw_platform_dev);
872 +err1:
873 +       platform_driver_unregister(&gpio_sw_driver);
874 +       return r;
875 +}
876 +
877 +static void __exit gpio_sw_exit(void)
878 +{
879 +       gpio_sw_cleanup();
880 +       platform_device_unregister(gpio_sw_platform_dev);
881 +       platform_driver_unregister(&gpio_sw_driver);
882 +}
883 +
884 +#ifndef MODULE
885 +late_initcall(gpio_sw_init);
886 +#else
887 +module_init(gpio_sw_init);
888 +#endif
889 +module_exit(gpio_sw_exit);
890 +
891 +MODULE_AUTHOR("Juha Yrjölä <juha.yrjola@nokia.com>, Paul Mundt <paul.mundt@nokia.com");
892 +MODULE_DESCRIPTION("GPIO switch driver");
893 +MODULE_LICENSE("GPL");
894 Index: linux-2.6.38-rc6/arch/arm/plat-omap/include/plat/board.h
895 ===================================================================
896 --- linux-2.6.38-rc6.orig/arch/arm/plat-omap/include/plat/board.h       2011-02-25 01:10:25.553127998 +0100
897 +++ linux-2.6.38-rc6/arch/arm/plat-omap/include/plat/board.h    2011-02-25 01:11:27.301562839 +0100
898 @@ -151,6 +151,14 @@
899         const void *data;
900  };
901  
902 +struct omap_gpio_switch_config {
903 +       char name[12];
904 +       u16 gpio;
905 +       int flags:4;
906 +       int type:4;
907 +       int key_code:24; /* Linux key code */
908 +};
909 +
910  extern const void *__omap_get_config(u16 tag, size_t len, int nr);
911  
912  #define omap_get_config(tag, type) \