mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-11-03 22:44:27 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			898 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			898 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
--- /dev/null
 | 
						||
+++ b/arch/arm/plat-omap/bootreason.c
 | 
						||
@@ -0,0 +1,79 @@
 | 
						||
+/*
 | 
						||
+ * linux/arch/arm/plat-omap/bootreason.c
 | 
						||
+ *
 | 
						||
+ * OMAP Bootreason passing
 | 
						||
+ *
 | 
						||
+ * Copyright (c) 2004 Nokia
 | 
						||
+ *
 | 
						||
+ * Written by David Weinehall <david.weinehall@nokia.com>
 | 
						||
+ *
 | 
						||
+ * This program is free software; you can redistribute it and/or modify it
 | 
						||
+ * under the terms of the GNU General Public License as published by the
 | 
						||
+ * Free Software Foundation; either version 2 of the License, or (at your
 | 
						||
+ * option) any later version.
 | 
						||
+ *
 | 
						||
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
 | 
						||
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 | 
						||
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
 | 
						||
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 | 
						||
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 | 
						||
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 | 
						||
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 | 
						||
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | 
						||
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 | 
						||
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
						||
+ *
 | 
						||
+ * You should have received a copy of the GNU General Public License along
 | 
						||
+ * with this program; if not, write to the Free Software Foundation, Inc.,
 | 
						||
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
 | 
						||
+ */
 | 
						||
+#include <linux/proc_fs.h>
 | 
						||
+#include <linux/errno.h>
 | 
						||
+#include <plat/board.h>
 | 
						||
+
 | 
						||
+static char boot_reason[16];
 | 
						||
+
 | 
						||
+static int omap_bootreason_read_proc(char *page, char **start, off_t off,
 | 
						||
+					 int count, int *eof, void *data)
 | 
						||
+{
 | 
						||
+	int len = 0;
 | 
						||
+
 | 
						||
+	len += sprintf(page + len, "%s\n", boot_reason);
 | 
						||
+
 | 
						||
+	*start = page + off;
 | 
						||
+
 | 
						||
+	if (len > off)
 | 
						||
+		len -= off;
 | 
						||
+	else
 | 
						||
+		len = 0;
 | 
						||
+
 | 
						||
+	return len < count ? len  : count;
 | 
						||
+}
 | 
						||
+
 | 
						||
+static int __init bootreason_init(void)
 | 
						||
+{
 | 
						||
+	const struct omap_boot_reason_config *cfg;
 | 
						||
+	int reason_valid = 0;
 | 
						||
+
 | 
						||
+	cfg = omap_get_config(OMAP_TAG_BOOT_REASON, struct omap_boot_reason_config);
 | 
						||
+	if (cfg != NULL) {
 | 
						||
+		strncpy(boot_reason, cfg->reason_str, sizeof(cfg->reason_str));
 | 
						||
+		boot_reason[sizeof(cfg->reason_str)] = 0;
 | 
						||
+		reason_valid = 1;
 | 
						||
+	} else {
 | 
						||
+		/* Read the boot reason from the OMAP registers */
 | 
						||
+	}
 | 
						||
+
 | 
						||
+	if (!reason_valid)
 | 
						||
+		return -ENOENT;
 | 
						||
+
 | 
						||
+	printk(KERN_INFO "Bootup reason: %s\n", boot_reason);
 | 
						||
+
 | 
						||
+	if (!create_proc_read_entry("bootreason", S_IRUGO, NULL,
 | 
						||
+					omap_bootreason_read_proc, NULL))
 | 
						||
+		return -ENOMEM;
 | 
						||
+
 | 
						||
+	return 0;
 | 
						||
+}
 | 
						||
+
 | 
						||
+late_initcall(bootreason_init);
 | 
						||
--- a/arch/arm/plat-omap/common.c
 | 
						||
+++ b/arch/arm/plat-omap/common.c
 | 
						||
@@ -24,18 +24,90 @@
 | 
						||
 
 | 
						||
 #include <plat/omap-secure.h>
 | 
						||
 
 | 
						||
+#include <asm/setup.h>
 | 
						||
+
 | 
						||
 
 | 
						||
 #define NO_LENGTH_CHECK 0xffffffff
 | 
						||
 
 | 
						||
 struct omap_board_config_kernel *omap_board_config __initdata;
 | 
						||
 int omap_board_config_size;
 | 
						||
 
 | 
						||
+unsigned char omap_bootloader_tag[1024];
 | 
						||
+int omap_bootloader_tag_len;
 | 
						||
+
 | 
						||
+/* used by omap-smp.c and board-4430sdp.c */
 | 
						||
+void __iomem *gic_cpu_base_addr;
 | 
						||
+
 | 
						||
+#ifdef CONFIG_OMAP_BOOT_TAG
 | 
						||
+
 | 
						||
+static int __init parse_tag_omap(const struct tag *tag)
 | 
						||
+{
 | 
						||
+	u32 size = tag->hdr.size - (sizeof(tag->hdr) >> 2);
 | 
						||
+
 | 
						||
+        size <<= 2;
 | 
						||
+	if (size > sizeof(omap_bootloader_tag))
 | 
						||
+		return -1;
 | 
						||
+
 | 
						||
+	memcpy(omap_bootloader_tag, tag->u.omap.data, size);
 | 
						||
+	omap_bootloader_tag_len = size;
 | 
						||
+
 | 
						||
+        return 0;
 | 
						||
+}
 | 
						||
+
 | 
						||
+__tagtable(ATAG_BOARD, parse_tag_omap);
 | 
						||
+
 | 
						||
+#endif
 | 
						||
+
 | 
						||
 static const void *__init get_config(u16 tag, size_t len,
 | 
						||
 		int skip, size_t *len_out)
 | 
						||
 {
 | 
						||
 	struct omap_board_config_kernel *kinfo = NULL;
 | 
						||
 	int i;
 | 
						||
 
 | 
						||
+#ifdef CONFIG_OMAP_BOOT_TAG
 | 
						||
+	struct omap_board_config_entry *info = NULL;
 | 
						||
+
 | 
						||
+	if (omap_bootloader_tag_len > 4)
 | 
						||
+		info = (struct omap_board_config_entry *) omap_bootloader_tag;
 | 
						||
+	while (info != NULL) {
 | 
						||
+		u8 *next;
 | 
						||
+
 | 
						||
+		if (info->tag == tag) {
 | 
						||
+			if (skip == 0)
 | 
						||
+				break;
 | 
						||
+			skip--;
 | 
						||
+		}
 | 
						||
+
 | 
						||
+		if ((info->len & 0x03) != 0) {
 | 
						||
+			/* We bail out to avoid an alignment fault */
 | 
						||
+			printk(KERN_ERR "OMAP peripheral config: Length (%d) not word-aligned (tag %04x)\n",
 | 
						||
+			       info->len, info->tag);
 | 
						||
+			return NULL;
 | 
						||
+		}
 | 
						||
+		next = (u8 *) info + sizeof(*info) + info->len;
 | 
						||
+		if (next >= omap_bootloader_tag + omap_bootloader_tag_len)
 | 
						||
+			info = NULL;
 | 
						||
+		else
 | 
						||
+			info = (struct omap_board_config_entry *) next;
 | 
						||
+	}
 | 
						||
+	if (info != NULL) {
 | 
						||
+		/* Check the length as a lame attempt to check for
 | 
						||
+		 * binary inconsistency. */
 | 
						||
+		if (len != NO_LENGTH_CHECK) {
 | 
						||
+			/* Word-align len */
 | 
						||
+			if (len & 0x03)
 | 
						||
+				len = (len + 3) & ~0x03;
 | 
						||
+			if (info->len != len) {
 | 
						||
+				printk(KERN_ERR "OMAP peripheral config: Length mismatch with tag %x (want %d, got %d)\n",
 | 
						||
+				       tag, len, info->len);
 | 
						||
+				return NULL;
 | 
						||
+			}
 | 
						||
+		}
 | 
						||
+		if (len_out != NULL)
 | 
						||
+			*len_out = info->len;
 | 
						||
+		return info->data;
 | 
						||
+	}
 | 
						||
+#endif
 | 
						||
 	/* Try to find the config from the board-specific structures
 | 
						||
 	 * in the kernel. */
 | 
						||
 	for (i = 0; i < omap_board_config_size; i++) {
 | 
						||
--- /dev/null
 | 
						||
+++ b/arch/arm/plat-omap/component-version.c
 | 
						||
@@ -0,0 +1,64 @@
 | 
						||
+/*
 | 
						||
+ *  linux/arch/arm/plat-omap/component-version.c
 | 
						||
+ *
 | 
						||
+ *  Copyright (C) 2005 Nokia Corporation
 | 
						||
+ *  Written by Juha Yrj<72>l<EFBFBD> <juha.yrjola@nokia.com>
 | 
						||
+ *
 | 
						||
+ * This program is free software; you can redistribute it and/or modify
 | 
						||
+ * it under the terms of the GNU General Public License version 2 as
 | 
						||
+ * published by the Free Software Foundation.
 | 
						||
+ */
 | 
						||
+
 | 
						||
+#include <linux/init.h>
 | 
						||
+#include <linux/module.h>
 | 
						||
+#include <linux/err.h>
 | 
						||
+#include <linux/proc_fs.h>
 | 
						||
+#include <plat/board.h>
 | 
						||
+
 | 
						||
+static int component_version_read_proc(char *page, char **start, off_t off,
 | 
						||
+				       int count, int *eof, void *data)
 | 
						||
+{
 | 
						||
+	int len, i;
 | 
						||
+	const struct omap_version_config *ver;
 | 
						||
+	char *p;
 | 
						||
+
 | 
						||
+	i = 0;
 | 
						||
+	p = page;
 | 
						||
+	while ((ver = omap_get_nr_config(OMAP_TAG_VERSION_STR,
 | 
						||
+					 struct omap_version_config, i)) != NULL) {
 | 
						||
+		p += sprintf(p, "%-12s%s\n", ver->component, ver->version);
 | 
						||
+		i++;
 | 
						||
+	}
 | 
						||
+
 | 
						||
+	len = (p - page) - off;
 | 
						||
+	if (len < 0)
 | 
						||
+		len = 0;
 | 
						||
+
 | 
						||
+	*eof = (len <= count) ? 1 : 0;
 | 
						||
+	*start = page + off;
 | 
						||
+
 | 
						||
+	return len;
 | 
						||
+}
 | 
						||
+
 | 
						||
+static int __init component_version_init(void)
 | 
						||
+{
 | 
						||
+	if (omap_get_config(OMAP_TAG_VERSION_STR, struct omap_version_config) == NULL)
 | 
						||
+		return -ENODEV;
 | 
						||
+	if (!create_proc_read_entry("component_version", S_IRUGO, NULL,
 | 
						||
+				    component_version_read_proc, NULL))
 | 
						||
+		return -ENOMEM;
 | 
						||
+
 | 
						||
+	return 0;
 | 
						||
+}
 | 
						||
+
 | 
						||
+static void __exit component_version_exit(void)
 | 
						||
+{
 | 
						||
+	remove_proc_entry("component_version", NULL);
 | 
						||
+}
 | 
						||
+
 | 
						||
+late_initcall(component_version_init);
 | 
						||
+module_exit(component_version_exit);
 | 
						||
+
 | 
						||
+MODULE_AUTHOR("Juha Yrj<72>l<EFBFBD> <juha.yrjola@nokia.com>");
 | 
						||
+MODULE_DESCRIPTION("Component version driver");
 | 
						||
+MODULE_LICENSE("GPL");
 | 
						||
--- a/arch/arm/plat-omap/Kconfig
 | 
						||
+++ b/arch/arm/plat-omap/Kconfig
 | 
						||
@@ -84,6 +84,38 @@ config OMAP_RESET_CLOCKS
 | 
						||
 	  probably do not want this option enabled until your
 | 
						||
 	  device drivers work properly.
 | 
						||
 
 | 
						||
+config OMAP_BOOT_TAG
 | 
						||
+	bool "OMAP bootloader information passing"
 | 
						||
+        depends on ARCH_OMAP
 | 
						||
+        default n
 | 
						||
+        help
 | 
						||
+          Say Y, if you have a bootloader which passes information
 | 
						||
+          about your board and its peripheral configuration.
 | 
						||
+
 | 
						||
+config OMAP_BOOT_REASON
 | 
						||
+	bool "Support for boot reason"
 | 
						||
+        depends on OMAP_BOOT_TAG
 | 
						||
+        default n
 | 
						||
+        help
 | 
						||
+          Say Y, if you want to have a procfs entry for reading the boot
 | 
						||
+          reason in user-space.
 | 
						||
+
 | 
						||
+config OMAP_COMPONENT_VERSION
 | 
						||
+	bool "Support for component version display"
 | 
						||
+	depends on OMAP_BOOT_TAG && PROC_FS
 | 
						||
+	default n
 | 
						||
+	help
 | 
						||
+	  Say Y, if you want to have a procfs entry for reading component
 | 
						||
+	  versions (supplied by the bootloader) in user-space.
 | 
						||
+
 | 
						||
+config OMAP_GPIO_SWITCH
 | 
						||
+	bool "GPIO switch support"
 | 
						||
+	help
 | 
						||
+	  Say Y, if you want to have support for reporting of GPIO
 | 
						||
+	  switches (e.g. cover switches) via sysfs. Your bootloader has
 | 
						||
+	  to provide information about the switches to the kernel via the
 | 
						||
+	  ATAG_BOARD mechanism if they're not defined by the board config.
 | 
						||
+
 | 
						||
 config OMAP_MUX
 | 
						||
 	bool "OMAP multiplexing support"
 | 
						||
 	depends on ARCH_OMAP
 | 
						||
--- a/arch/arm/plat-omap/Makefile
 | 
						||
+++ b/arch/arm/plat-omap/Makefile
 | 
						||
@@ -20,6 +20,9 @@ obj-$(CONFIG_ARCH_OMAP4) += omap_device.
 | 
						||
 obj-$(CONFIG_OMAP_MCBSP) += mcbsp.o
 | 
						||
 
 | 
						||
 obj-$(CONFIG_OMAP_DM_TIMER) += dmtimer.o
 | 
						||
+obj-$(CONFIG_OMAP_BOOT_REASON) += bootreason.o
 | 
						||
+obj-$(CONFIG_OMAP_COMPONENT_VERSION) += component-version.o
 | 
						||
+obj-$(CONFIG_OMAP_GPIO_SWITCH) += gpio-switch.o
 | 
						||
 obj-$(CONFIG_OMAP_DEBUG_DEVICES) += debug-devices.o
 | 
						||
 obj-$(CONFIG_OMAP_DEBUG_LEDS) += debug-leds.o
 | 
						||
 i2c-omap-$(CONFIG_I2C_OMAP) := i2c.o
 | 
						||
--- a/arch/arm/include/asm/setup.h
 | 
						||
+++ b/arch/arm/include/asm/setup.h
 | 
						||
@@ -136,6 +136,13 @@ struct tag_acorn {
 | 
						||
 	__u8 adfsdrives;
 | 
						||
 };
 | 
						||
 
 | 
						||
+/* TI OMAP specific information */
 | 
						||
+#define ATAG_BOARD       0x414f4d50
 | 
						||
+
 | 
						||
+struct tag_omap {
 | 
						||
+	u8 data[0];
 | 
						||
+};
 | 
						||
+
 | 
						||
 /* footbridge memory clock, see arch/arm/mach-footbridge/arch.c */
 | 
						||
 #define ATAG_MEMCLK	0x41000402
 | 
						||
 
 | 
						||
@@ -162,6 +169,11 @@ struct tag {
 | 
						||
 		struct tag_acorn	acorn;
 | 
						||
 
 | 
						||
 		/*
 | 
						||
+		 * OMAP specific
 | 
						||
+                 */
 | 
						||
+                struct tag_omap         omap;
 | 
						||
+
 | 
						||
+		/*
 | 
						||
 		 * DC21285 specific
 | 
						||
 		 */
 | 
						||
 		struct tag_memclk	memclk;
 | 
						||
--- /dev/null
 | 
						||
+++ b/arch/arm/plat-omap/gpio-switch.c
 | 
						||
@@ -0,0 +1,554 @@
 | 
						||
+/*
 | 
						||
+ *  linux/arch/arm/plat-omap/gpio-switch.c
 | 
						||
+ *
 | 
						||
+ *  Copyright (C) 2004-2006 Nokia Corporation
 | 
						||
+ *  Written by Juha Yrj<72>l<EFBFBD> <juha.yrjola@nokia.com>
 | 
						||
+ *         and Paul Mundt <paul.mundt@nokia.com>
 | 
						||
+ *
 | 
						||
+ * This program is free software; you can redistribute it and/or modify
 | 
						||
+ * it under the terms of the GNU General Public License version 2 as
 | 
						||
+ * published by the Free Software Foundation.
 | 
						||
+ */
 | 
						||
+
 | 
						||
+#include <linux/sched.h>
 | 
						||
+#include <linux/init.h>
 | 
						||
+#include <linux/list.h>
 | 
						||
+#include <linux/irq.h>
 | 
						||
+#include <linux/interrupt.h>
 | 
						||
+#include <linux/module.h>
 | 
						||
+#include <linux/platform_device.h>
 | 
						||
+#include <linux/timer.h>
 | 
						||
+#include <linux/err.h>
 | 
						||
+#include <linux/slab.h>
 | 
						||
+#include <linux/gpio.h>
 | 
						||
+#include <plat/hardware.h>
 | 
						||
+#include <plat/irqs.h>
 | 
						||
+#include <plat/mux.h>
 | 
						||
+#include <plat/board.h>
 | 
						||
+#include <plat/gpio-switch.h>
 | 
						||
+
 | 
						||
+struct gpio_switch {
 | 
						||
+	char		name[14];
 | 
						||
+	u16		gpio;
 | 
						||
+	unsigned	flags:4;
 | 
						||
+	unsigned	type:4;
 | 
						||
+	unsigned	state:1;
 | 
						||
+	unsigned	both_edges:1;
 | 
						||
+
 | 
						||
+	u16		debounce_rising;
 | 
						||
+	u16		debounce_falling;
 | 
						||
+
 | 
						||
+	void (* notify)(void *data, int state);
 | 
						||
+	void *notify_data;
 | 
						||
+
 | 
						||
+	struct work_struct	work;
 | 
						||
+	struct timer_list	timer;
 | 
						||
+	struct platform_device	pdev;
 | 
						||
+
 | 
						||
+	struct list_head	node;
 | 
						||
+};
 | 
						||
+
 | 
						||
+static LIST_HEAD(gpio_switches);
 | 
						||
+static struct platform_device *gpio_sw_platform_dev;
 | 
						||
+static struct platform_driver gpio_sw_driver;
 | 
						||
+
 | 
						||
+static const struct omap_gpio_switch *board_gpio_sw_table;
 | 
						||
+static int board_gpio_sw_count;
 | 
						||
+
 | 
						||
+static const char *cover_str[2] = { "open", "closed" };
 | 
						||
+static const char *connection_str[2] = { "disconnected", "connected" };
 | 
						||
+static const char *activity_str[2] = { "inactive", "active" };
 | 
						||
+
 | 
						||
+/*
 | 
						||
+ * GPIO switch state default debounce delay in ms
 | 
						||
+ */
 | 
						||
+#define OMAP_GPIO_SW_DEFAULT_DEBOUNCE		10
 | 
						||
+
 | 
						||
+static const char **get_sw_str(struct gpio_switch *sw)
 | 
						||
+{
 | 
						||
+	switch (sw->type) {
 | 
						||
+	case OMAP_GPIO_SWITCH_TYPE_COVER:
 | 
						||
+		return cover_str;
 | 
						||
+	case OMAP_GPIO_SWITCH_TYPE_CONNECTION:
 | 
						||
+		return connection_str;
 | 
						||
+	case OMAP_GPIO_SWITCH_TYPE_ACTIVITY:
 | 
						||
+		return activity_str;
 | 
						||
+	default:
 | 
						||
+		BUG();
 | 
						||
+		return NULL;
 | 
						||
+	}
 | 
						||
+}
 | 
						||
+
 | 
						||
+static const char *get_sw_type(struct gpio_switch *sw)
 | 
						||
+{
 | 
						||
+	switch (sw->type) {
 | 
						||
+	case OMAP_GPIO_SWITCH_TYPE_COVER:
 | 
						||
+		return "cover";
 | 
						||
+	case OMAP_GPIO_SWITCH_TYPE_CONNECTION:
 | 
						||
+		return "connection";
 | 
						||
+	case OMAP_GPIO_SWITCH_TYPE_ACTIVITY:
 | 
						||
+		return "activity";
 | 
						||
+	default:
 | 
						||
+		BUG();
 | 
						||
+		return NULL;
 | 
						||
+	}
 | 
						||
+}
 | 
						||
+
 | 
						||
+static void print_sw_state(struct gpio_switch *sw, int state)
 | 
						||
+{
 | 
						||
+	const char **str;
 | 
						||
+
 | 
						||
+	str = get_sw_str(sw);
 | 
						||
+	if (str != NULL)
 | 
						||
+		printk(KERN_INFO "%s (GPIO %d) is now %s\n", sw->name, sw->gpio, str[state]);
 | 
						||
+}
 | 
						||
+
 | 
						||
+static int gpio_sw_get_state(struct gpio_switch *sw)
 | 
						||
+{
 | 
						||
+	int state;
 | 
						||
+
 | 
						||
+	state = gpio_get_value(sw->gpio);
 | 
						||
+	if (sw->flags & OMAP_GPIO_SWITCH_FLAG_INVERTED)
 | 
						||
+		state = !state;
 | 
						||
+
 | 
						||
+	return state;
 | 
						||
+}
 | 
						||
+
 | 
						||
+static ssize_t gpio_sw_state_store(struct device *dev,
 | 
						||
+				   struct device_attribute *attr,
 | 
						||
+				   const char *buf,
 | 
						||
+				   size_t count)
 | 
						||
+{
 | 
						||
+	struct gpio_switch *sw = dev_get_drvdata(dev);
 | 
						||
+	const char **str;
 | 
						||
+	char state[16];
 | 
						||
+	int enable;
 | 
						||
+
 | 
						||
+	if (!(sw->flags & OMAP_GPIO_SWITCH_FLAG_OUTPUT))
 | 
						||
+		return -EPERM;
 | 
						||
+
 | 
						||
+	if (sscanf(buf, "%15s", state) != 1)
 | 
						||
+		return -EINVAL;
 | 
						||
+
 | 
						||
+	str = get_sw_str(sw);
 | 
						||
+	if (strcmp(state, str[0]) == 0)
 | 
						||
+		sw->state = enable = 0;
 | 
						||
+	else if (strcmp(state, str[1]) == 0)
 | 
						||
+		sw->state = enable = 1;
 | 
						||
+	else
 | 
						||
+		return -EINVAL;
 | 
						||
+
 | 
						||
+	if (sw->flags & OMAP_GPIO_SWITCH_FLAG_INVERTED)
 | 
						||
+		enable = !enable;
 | 
						||
+	gpio_set_value(sw->gpio, enable);
 | 
						||
+
 | 
						||
+	return count;
 | 
						||
+}
 | 
						||
+
 | 
						||
+static ssize_t gpio_sw_state_show(struct device *dev,
 | 
						||
+				  struct device_attribute *attr,
 | 
						||
+				  char *buf)
 | 
						||
+{
 | 
						||
+	struct gpio_switch *sw = dev_get_drvdata(dev);
 | 
						||
+	const char **str;
 | 
						||
+
 | 
						||
+	str = get_sw_str(sw);
 | 
						||
+	return sprintf(buf, "%s\n", str[sw->state]);
 | 
						||
+}
 | 
						||
+
 | 
						||
+static DEVICE_ATTR(state, S_IRUGO | S_IWUSR, gpio_sw_state_show,
 | 
						||
+		   gpio_sw_state_store);
 | 
						||
+
 | 
						||
+static ssize_t gpio_sw_type_show(struct device *dev,
 | 
						||
+				 struct device_attribute *attr,
 | 
						||
+				 char *buf)
 | 
						||
+{
 | 
						||
+	struct gpio_switch *sw = dev_get_drvdata(dev);
 | 
						||
+
 | 
						||
+	return sprintf(buf, "%s\n", get_sw_type(sw));
 | 
						||
+}
 | 
						||
+
 | 
						||
+static DEVICE_ATTR(type, S_IRUGO, gpio_sw_type_show, NULL);
 | 
						||
+
 | 
						||
+static ssize_t gpio_sw_direction_show(struct device *dev,
 | 
						||
+				      struct device_attribute *attr,
 | 
						||
+				      char *buf)
 | 
						||
+{
 | 
						||
+	struct gpio_switch *sw = dev_get_drvdata(dev);
 | 
						||
+	int is_output;
 | 
						||
+
 | 
						||
+	is_output = sw->flags & OMAP_GPIO_SWITCH_FLAG_OUTPUT;
 | 
						||
+	return sprintf(buf, "%s\n", is_output ? "output" : "input");
 | 
						||
+}
 | 
						||
+
 | 
						||
+static DEVICE_ATTR(direction, S_IRUGO, gpio_sw_direction_show, NULL);
 | 
						||
+
 | 
						||
+
 | 
						||
+static irqreturn_t gpio_sw_irq_handler(int irq, void *arg)
 | 
						||
+{
 | 
						||
+	struct gpio_switch *sw = arg;
 | 
						||
+	unsigned long timeout;
 | 
						||
+	int state;
 | 
						||
+
 | 
						||
+	if (!sw->both_edges) {
 | 
						||
+		if (gpio_get_value(sw->gpio))
 | 
						||
+			irq_set_irq_type(OMAP_GPIO_IRQ(sw->gpio), IRQ_TYPE_EDGE_FALLING);
 | 
						||
+		else
 | 
						||
+			irq_set_irq_type(OMAP_GPIO_IRQ(sw->gpio), IRQ_TYPE_EDGE_RISING);
 | 
						||
+	}
 | 
						||
+
 | 
						||
+	state = gpio_sw_get_state(sw);
 | 
						||
+	if (sw->state == state)
 | 
						||
+		return IRQ_HANDLED;
 | 
						||
+
 | 
						||
+	if (state)
 | 
						||
+		timeout = sw->debounce_rising;
 | 
						||
+	else
 | 
						||
+		timeout = sw->debounce_falling;
 | 
						||
+	if (!timeout)
 | 
						||
+		schedule_work(&sw->work);
 | 
						||
+	else
 | 
						||
+		mod_timer(&sw->timer, jiffies + msecs_to_jiffies(timeout));
 | 
						||
+
 | 
						||
+	return IRQ_HANDLED;
 | 
						||
+}
 | 
						||
+
 | 
						||
+static void gpio_sw_timer(unsigned long arg)
 | 
						||
+{
 | 
						||
+	struct gpio_switch *sw = (struct gpio_switch *) arg;
 | 
						||
+
 | 
						||
+	schedule_work(&sw->work);
 | 
						||
+}
 | 
						||
+
 | 
						||
+static void gpio_sw_handler(struct work_struct *work)
 | 
						||
+{
 | 
						||
+	struct gpio_switch *sw = container_of(work, struct gpio_switch, work);
 | 
						||
+	int state;
 | 
						||
+
 | 
						||
+	state = gpio_sw_get_state(sw);
 | 
						||
+	if (sw->state == state)
 | 
						||
+		return;
 | 
						||
+
 | 
						||
+	sw->state = state;
 | 
						||
+	if (sw->notify != NULL)
 | 
						||
+		sw->notify(sw->notify_data, state);
 | 
						||
+	sysfs_notify(&sw->pdev.dev.kobj, NULL, "state");
 | 
						||
+	print_sw_state(sw, state);
 | 
						||
+}
 | 
						||
+
 | 
						||
+static int __init can_do_both_edges(struct gpio_switch *sw)
 | 
						||
+{
 | 
						||
+	if (!cpu_class_is_omap1())
 | 
						||
+		return 1;
 | 
						||
+	if (OMAP_GPIO_IS_MPUIO(sw->gpio))
 | 
						||
+		return 0;
 | 
						||
+	else
 | 
						||
+		return 1;
 | 
						||
+}
 | 
						||
+
 | 
						||
+static void gpio_sw_release(struct device *dev)
 | 
						||
+{
 | 
						||
+}
 | 
						||
+
 | 
						||
+static int __init new_switch(struct gpio_switch *sw)
 | 
						||
+{
 | 
						||
+	int r, direction, trigger;
 | 
						||
+
 | 
						||
+	switch (sw->type) {
 | 
						||
+	case OMAP_GPIO_SWITCH_TYPE_COVER:
 | 
						||
+	case OMAP_GPIO_SWITCH_TYPE_CONNECTION:
 | 
						||
+	case OMAP_GPIO_SWITCH_TYPE_ACTIVITY:
 | 
						||
+		break;
 | 
						||
+	default:
 | 
						||
+		printk(KERN_ERR "invalid GPIO switch type: %d\n", sw->type);
 | 
						||
+		return -EINVAL;
 | 
						||
+	}
 | 
						||
+
 | 
						||
+	sw->pdev.name	= sw->name;
 | 
						||
+	sw->pdev.id	= -1;
 | 
						||
+
 | 
						||
+	sw->pdev.dev.parent = &gpio_sw_platform_dev->dev;
 | 
						||
+	sw->pdev.dev.driver = &gpio_sw_driver.driver;
 | 
						||
+	sw->pdev.dev.release = gpio_sw_release;
 | 
						||
+
 | 
						||
+	r = platform_device_register(&sw->pdev);
 | 
						||
+	if (r) {
 | 
						||
+		printk(KERN_ERR "gpio-switch: platform device registration "
 | 
						||
+		       "failed for %s", sw->name);
 | 
						||
+		return r;
 | 
						||
+	}
 | 
						||
+	dev_set_drvdata(&sw->pdev.dev, sw);
 | 
						||
+
 | 
						||
+	r = gpio_request(sw->gpio, "gpio-switch");
 | 
						||
+	if (r < 0) {
 | 
						||
+		platform_device_unregister(&sw->pdev);
 | 
						||
+		return r;
 | 
						||
+	}
 | 
						||
+
 | 
						||
+	/* input: 1, output: 0 */
 | 
						||
+	direction = !(sw->flags & OMAP_GPIO_SWITCH_FLAG_OUTPUT);
 | 
						||
+	if (direction)
 | 
						||
+		gpio_direction_input(sw->gpio);
 | 
						||
+	else
 | 
						||
+		gpio_direction_output(sw->gpio, 0);
 | 
						||
+
 | 
						||
+	sw->state = gpio_sw_get_state(sw);
 | 
						||
+
 | 
						||
+	r = 0;
 | 
						||
+	r |= device_create_file(&sw->pdev.dev, &dev_attr_state);
 | 
						||
+	r |= device_create_file(&sw->pdev.dev, &dev_attr_type);
 | 
						||
+	r |= device_create_file(&sw->pdev.dev, &dev_attr_direction);
 | 
						||
+	if (r)
 | 
						||
+		printk(KERN_ERR "gpio-switch: attribute file creation "
 | 
						||
+		       "failed for %s\n", sw->name);
 | 
						||
+
 | 
						||
+	if (!direction)
 | 
						||
+		return 0;
 | 
						||
+
 | 
						||
+	if (can_do_both_edges(sw)) {
 | 
						||
+		trigger = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING;
 | 
						||
+		sw->both_edges = 1;
 | 
						||
+	} else {
 | 
						||
+		if (gpio_get_value(sw->gpio))
 | 
						||
+			trigger = IRQF_TRIGGER_FALLING;
 | 
						||
+		else
 | 
						||
+			trigger = IRQF_TRIGGER_RISING;
 | 
						||
+	}
 | 
						||
+	r = request_irq(OMAP_GPIO_IRQ(sw->gpio), gpio_sw_irq_handler,
 | 
						||
+			IRQF_SHARED | trigger, sw->name, sw);
 | 
						||
+	if (r < 0) {
 | 
						||
+		printk(KERN_ERR "gpio-switch: request_irq() failed "
 | 
						||
+		       "for GPIO %d\n", sw->gpio);
 | 
						||
+		platform_device_unregister(&sw->pdev);
 | 
						||
+		gpio_free(sw->gpio);
 | 
						||
+		return r;
 | 
						||
+	}
 | 
						||
+
 | 
						||
+	INIT_WORK(&sw->work, gpio_sw_handler);
 | 
						||
+	init_timer(&sw->timer);
 | 
						||
+
 | 
						||
+	sw->timer.function = gpio_sw_timer;
 | 
						||
+	sw->timer.data = (unsigned long)sw;
 | 
						||
+
 | 
						||
+	list_add(&sw->node, &gpio_switches);
 | 
						||
+
 | 
						||
+	return 0;
 | 
						||
+}
 | 
						||
+
 | 
						||
+static int __init add_atag_switches(void)
 | 
						||
+{
 | 
						||
+	const struct omap_gpio_switch_config *cfg;
 | 
						||
+	struct gpio_switch *sw;
 | 
						||
+	int i, r;
 | 
						||
+
 | 
						||
+	for (i = 0; ; i++) {
 | 
						||
+		cfg = omap_get_nr_config(OMAP_TAG_GPIO_SWITCH,
 | 
						||
+					 struct omap_gpio_switch_config, i);
 | 
						||
+		if (cfg == NULL)
 | 
						||
+			break;
 | 
						||
+		sw = kzalloc(sizeof(*sw), GFP_KERNEL);
 | 
						||
+		if (sw == NULL) {
 | 
						||
+			printk(KERN_ERR "gpio-switch: kmalloc failed\n");
 | 
						||
+			return -ENOMEM;
 | 
						||
+		}
 | 
						||
+		strncpy(sw->name, cfg->name, sizeof(cfg->name));
 | 
						||
+		sw->gpio = cfg->gpio;
 | 
						||
+		sw->flags = cfg->flags;
 | 
						||
+		sw->type = cfg->type;
 | 
						||
+		sw->debounce_rising = OMAP_GPIO_SW_DEFAULT_DEBOUNCE;
 | 
						||
+		sw->debounce_falling = OMAP_GPIO_SW_DEFAULT_DEBOUNCE;
 | 
						||
+		if ((r = new_switch(sw)) < 0) {
 | 
						||
+			kfree(sw);
 | 
						||
+			return r;
 | 
						||
+		}
 | 
						||
+	}
 | 
						||
+	return 0;
 | 
						||
+}
 | 
						||
+
 | 
						||
+static struct gpio_switch * __init find_switch(int gpio, const char *name)
 | 
						||
+{
 | 
						||
+	struct gpio_switch *sw;
 | 
						||
+
 | 
						||
+	list_for_each_entry(sw, &gpio_switches, node) {
 | 
						||
+		if ((gpio < 0 || sw->gpio != gpio) &&
 | 
						||
+		    (name == NULL || strcmp(sw->name, name) != 0))
 | 
						||
+			continue;
 | 
						||
+
 | 
						||
+		if (gpio < 0 || name == NULL)
 | 
						||
+			goto no_check;
 | 
						||
+
 | 
						||
+		if (strcmp(sw->name, name) != 0)
 | 
						||
+			printk("gpio-switch: name mismatch for %d (%s, %s)\n",
 | 
						||
+			       gpio, name, sw->name);
 | 
						||
+		else if (sw->gpio != gpio)
 | 
						||
+			printk("gpio-switch: GPIO mismatch for %s (%d, %d)\n",
 | 
						||
+			       name, gpio, sw->gpio);
 | 
						||
+no_check:
 | 
						||
+		return sw;
 | 
						||
+	}
 | 
						||
+	return NULL;
 | 
						||
+}
 | 
						||
+
 | 
						||
+static int __init add_board_switches(void)
 | 
						||
+{
 | 
						||
+	int i;
 | 
						||
+
 | 
						||
+	for (i = 0; i < board_gpio_sw_count; i++) {
 | 
						||
+		const struct omap_gpio_switch *cfg;
 | 
						||
+		struct gpio_switch *sw;
 | 
						||
+		int r;
 | 
						||
+
 | 
						||
+		cfg = board_gpio_sw_table + i;
 | 
						||
+		if (strlen(cfg->name) > sizeof(sw->name) - 1)
 | 
						||
+			return -EINVAL;
 | 
						||
+		/* Check whether we only update an existing switch
 | 
						||
+		 * or add a new switch. */
 | 
						||
+		sw = find_switch(cfg->gpio, cfg->name);
 | 
						||
+		if (sw != NULL) {
 | 
						||
+			sw->debounce_rising = cfg->debounce_rising;
 | 
						||
+			sw->debounce_falling = cfg->debounce_falling;
 | 
						||
+			sw->notify = cfg->notify;
 | 
						||
+			sw->notify_data = cfg->notify_data;
 | 
						||
+			continue;
 | 
						||
+		} else {
 | 
						||
+			if (cfg->gpio < 0 || cfg->name == NULL) {
 | 
						||
+				printk("gpio-switch: required switch not "
 | 
						||
+				       "found (%d, %s)\n", cfg->gpio,
 | 
						||
+				       cfg->name);
 | 
						||
+				continue;
 | 
						||
+			}
 | 
						||
+		}
 | 
						||
+		sw = kzalloc(sizeof(*sw), GFP_KERNEL);
 | 
						||
+		if (sw == NULL) {
 | 
						||
+			printk(KERN_ERR "gpio-switch: kmalloc failed\n");
 | 
						||
+			return -ENOMEM;
 | 
						||
+		}
 | 
						||
+		strlcpy(sw->name, cfg->name, sizeof(sw->name));
 | 
						||
+		sw->gpio = cfg->gpio;
 | 
						||
+		sw->flags = cfg->flags;
 | 
						||
+		sw->type = cfg->type;
 | 
						||
+		sw->debounce_rising = cfg->debounce_rising;
 | 
						||
+		sw->debounce_falling = cfg->debounce_falling;
 | 
						||
+		sw->notify = cfg->notify;
 | 
						||
+		sw->notify_data = cfg->notify_data;
 | 
						||
+		if ((r = new_switch(sw)) < 0) {
 | 
						||
+			kfree(sw);
 | 
						||
+			return r;
 | 
						||
+		}
 | 
						||
+	}
 | 
						||
+	return 0;
 | 
						||
+}
 | 
						||
+
 | 
						||
+static void gpio_sw_cleanup(void)
 | 
						||
+{
 | 
						||
+	struct gpio_switch *sw = NULL, *old = NULL;
 | 
						||
+
 | 
						||
+	list_for_each_entry(sw, &gpio_switches, node) {
 | 
						||
+		if (old != NULL)
 | 
						||
+			kfree(old);
 | 
						||
+		flush_scheduled_work();
 | 
						||
+		del_timer_sync(&sw->timer);
 | 
						||
+
 | 
						||
+		free_irq(OMAP_GPIO_IRQ(sw->gpio), sw);
 | 
						||
+
 | 
						||
+		device_remove_file(&sw->pdev.dev, &dev_attr_state);
 | 
						||
+		device_remove_file(&sw->pdev.dev, &dev_attr_type);
 | 
						||
+		device_remove_file(&sw->pdev.dev, &dev_attr_direction);
 | 
						||
+
 | 
						||
+		platform_device_unregister(&sw->pdev);
 | 
						||
+		gpio_free(sw->gpio);
 | 
						||
+		old = sw;
 | 
						||
+	}
 | 
						||
+	kfree(old);
 | 
						||
+}
 | 
						||
+
 | 
						||
+static void __init report_initial_state(void)
 | 
						||
+{
 | 
						||
+	struct gpio_switch *sw;
 | 
						||
+
 | 
						||
+	list_for_each_entry(sw, &gpio_switches, node) {
 | 
						||
+		int state;
 | 
						||
+
 | 
						||
+		state = gpio_get_value(sw->gpio);
 | 
						||
+		if (sw->flags & OMAP_GPIO_SWITCH_FLAG_INVERTED)
 | 
						||
+			state = !state;
 | 
						||
+		if (sw->notify != NULL)
 | 
						||
+			sw->notify(sw->notify_data, state);
 | 
						||
+		print_sw_state(sw, state);
 | 
						||
+	}
 | 
						||
+}
 | 
						||
+
 | 
						||
+static int gpio_sw_remove(struct platform_device *dev)
 | 
						||
+{
 | 
						||
+	return 0;
 | 
						||
+}
 | 
						||
+
 | 
						||
+static struct platform_driver gpio_sw_driver = {
 | 
						||
+	.remove		= gpio_sw_remove,
 | 
						||
+	.driver		= {
 | 
						||
+		.name	= "gpio-switch",
 | 
						||
+	},
 | 
						||
+};
 | 
						||
+
 | 
						||
+void __init omap_register_gpio_switches(const struct omap_gpio_switch *tbl,
 | 
						||
+					int count)
 | 
						||
+{
 | 
						||
+	BUG_ON(board_gpio_sw_table != NULL);
 | 
						||
+
 | 
						||
+	board_gpio_sw_table = tbl;
 | 
						||
+	board_gpio_sw_count = count;
 | 
						||
+}
 | 
						||
+
 | 
						||
+static int __init gpio_sw_init(void)
 | 
						||
+{
 | 
						||
+	int r;
 | 
						||
+
 | 
						||
+	printk(KERN_INFO "OMAP GPIO switch handler initializing\n");
 | 
						||
+
 | 
						||
+	r = platform_driver_register(&gpio_sw_driver);
 | 
						||
+	if (r)
 | 
						||
+		return r;
 | 
						||
+
 | 
						||
+	gpio_sw_platform_dev = platform_device_register_simple("gpio-switch",
 | 
						||
+							       -1, NULL, 0);
 | 
						||
+	if (IS_ERR(gpio_sw_platform_dev)) {
 | 
						||
+		r = PTR_ERR(gpio_sw_platform_dev);
 | 
						||
+		goto err1;
 | 
						||
+	}
 | 
						||
+
 | 
						||
+	r = add_atag_switches();
 | 
						||
+	if (r < 0)
 | 
						||
+		goto err2;
 | 
						||
+
 | 
						||
+	r = add_board_switches();
 | 
						||
+	if (r < 0)
 | 
						||
+		goto err2;
 | 
						||
+
 | 
						||
+	report_initial_state();
 | 
						||
+
 | 
						||
+	return 0;
 | 
						||
+err2:
 | 
						||
+	gpio_sw_cleanup();
 | 
						||
+	platform_device_unregister(gpio_sw_platform_dev);
 | 
						||
+err1:
 | 
						||
+	platform_driver_unregister(&gpio_sw_driver);
 | 
						||
+	return r;
 | 
						||
+}
 | 
						||
+
 | 
						||
+static void __exit gpio_sw_exit(void)
 | 
						||
+{
 | 
						||
+	gpio_sw_cleanup();
 | 
						||
+	platform_device_unregister(gpio_sw_platform_dev);
 | 
						||
+	platform_driver_unregister(&gpio_sw_driver);
 | 
						||
+}
 | 
						||
+
 | 
						||
+#ifndef MODULE
 | 
						||
+late_initcall(gpio_sw_init);
 | 
						||
+#else
 | 
						||
+module_init(gpio_sw_init);
 | 
						||
+#endif
 | 
						||
+module_exit(gpio_sw_exit);
 | 
						||
+
 | 
						||
+MODULE_AUTHOR("Juha Yrj<72>l<EFBFBD> <juha.yrjola@nokia.com>, Paul Mundt <paul.mundt@nokia.com");
 | 
						||
+MODULE_DESCRIPTION("GPIO switch driver");
 | 
						||
+MODULE_LICENSE("GPL");
 | 
						||
--- a/arch/arm/plat-omap/include/plat/board.h
 | 
						||
+++ b/arch/arm/plat-omap/include/plat/board.h
 | 
						||
@@ -151,6 +151,14 @@ struct omap_board_config_kernel {
 | 
						||
 	const void *data;
 | 
						||
 };
 | 
						||
 
 | 
						||
+struct omap_gpio_switch_config {
 | 
						||
+	char name[12];
 | 
						||
+	u16 gpio;
 | 
						||
+	int flags:4;
 | 
						||
+	int type:4;
 | 
						||
+	int key_code:24; /* Linux key code */
 | 
						||
+};
 | 
						||
+
 | 
						||
 extern const void *__init __omap_get_config(u16 tag, size_t len, int nr);
 | 
						||
 
 | 
						||
 #define omap_get_config(tag, type) \
 |