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

Archive Download this file



interactive