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