public inbox for linux-omap@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH] OMAP3: PM: Add the wakeup source driver, v3
@ 2009-04-03 10:43 Kim Kyuwon
  2009-04-14  5:20 ` Kim Kyuwon
  2009-04-21  0:15 ` Kevin Hilman
  0 siblings, 2 replies; 6+ messages in thread
From: Kim Kyuwon @ 2009-04-03 10:43 UTC (permalink / raw)
  To: Kevin Hilman; +Cc: OMAP, Tony Lindgren, 박경민, riverful

Sometimes, it is necessary to find out "what does wake up my board
from suspend?". Notifying wake-up source feature may be used to blame
unexpected wake-up events which increase power consumption. And user
mode applications can act smartly according to the wake-up event from
Suspend-to-RAM state to minimize power consumption. Note that this
driver can't inform wake-up events from idle state. This driver uses
sysfs interface to give information to user mode applications like:

cat /sys/power/omap_resume_irq
cat /sys/power/omap_resume_event

This driver also privides the unified GPIO wake-up source
configuration. specific GPIO settings in the board files are:

/* Wakeup source configuration */
static struct gpio_wake boardname_gpio_wake[] = {
	{ 23,	IRQF_TRIGGER_RISING,	"BT_WAKEUP",	1},
	{ 24,	IRQF_TRIGGER_RISING,	"USB_DETECT",	1},
};

static struct omap_wake_platform_data boardname_wake_data = {
	.gpio_wakes		= boardname_gpio_wake,
	.gpio_wake_num		= ARRAY_SIZE(boardname_gpio_wake),
};

static struct platform_device boardname_wakeup = {
	.name			= "omap-wake",
	.id			= -1,
	.dev			= {
		.platform_data	= &boardname_wake_data,
	},
};

The patch adds Kconfig options "OMAP34xx wakeup source support" under
"System type"->"TI OMAP implementations" menu.

Signed-off-by: Kim Kyuwon <q1.kim@samsung.com>
---
 arch/arm/mach-omap2/Makefile           |    1 +
 arch/arm/mach-omap2/irq.c              |   21 +-
 arch/arm/mach-omap2/prcm-common.h      |    4 +
 arch/arm/mach-omap2/prm-regbits-34xx.h |    5 +
 arch/arm/mach-omap2/wake34xx.c         |  681 ++++++++++++++++++++++++++++++++
 arch/arm/plat-omap/Kconfig             |    9 +
 arch/arm/plat-omap/include/mach/irqs.h |    4 +
 arch/arm/plat-omap/include/mach/wake.h |   30 ++
 8 files changed, 752 insertions(+), 3 deletions(-)
 create mode 100644 arch/arm/mach-omap2/wake34xx.c
 create mode 100644 arch/arm/plat-omap/include/mach/wake.h

diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index e693efd..4d7dbca 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_ARCH_OMAP2)		+= pm24xx.o
 obj-$(CONFIG_ARCH_OMAP24XX)		+= sleep24xx.o
 obj-$(CONFIG_ARCH_OMAP3)		+= pm34xx.o sleep34xx.o cpuidle34xx.o
 obj-$(CONFIG_PM_DEBUG)			+= pm-debug.o
+obj-$(CONFIG_OMAP_WAKE)			+= wake34xx.o
 endif

 # SmartReflex driver
diff --git a/arch/arm/mach-omap2/irq.c b/arch/arm/mach-omap2/irq.c
index 700fc3d..18ac725 100644
--- a/arch/arm/mach-omap2/irq.c
+++ b/arch/arm/mach-omap2/irq.c
@@ -33,9 +33,6 @@
 #define INTC_MIR_SET0		0x008c
 #define INTC_PENDING_IRQ0	0x0098

-/* Number of IRQ state bits in each MIR register */
-#define IRQ_BITS_PER_REG	32
-
 /*
  * OMAP2 has a number of different interrupt controllers, each interrupt
  * controller is identified as its own "bank". Register definitions are
@@ -193,6 +190,24 @@ int omap_irq_pending(void)
 	return 0;
 }

+void omap_get_pending_irqs(u32 *pending_irqs, unsigned len)
+{
+	int i, j = 0;
+
+	for (i = 0; i < ARRAY_SIZE(irq_banks); i++) {
+		struct omap_irq_bank *bank = irq_banks + i;
+		int irq;
+
+		for (irq = 0; irq < bank->nr_irqs && j < len;
+						irq += IRQ_BITS_PER_REG) {
+			int offset = irq & (~(IRQ_BITS_PER_REG - 1));
+
+			pending_irqs[j++] = intc_bank_read_reg(bank,
+					(INTC_PENDING_IRQ0 + offset));
+		}
+	}
+}
+
 void __init omap_init_irq(void)
 {
 	unsigned long nr_of_irqs = 0;
diff --git a/arch/arm/mach-omap2/prcm-common.h
b/arch/arm/mach-omap2/prcm-common.h
index cb1ae84..1f340aa 100644
--- a/arch/arm/mach-omap2/prcm-common.h
+++ b/arch/arm/mach-omap2/prcm-common.h
@@ -273,6 +273,10 @@
 #define OMAP3430_ST_D2D_SHIFT				3
 #define OMAP3430_ST_D2D_MASK				(1 << 3)

+/* PM_WKST3_CORE, CM_IDLEST3_CORE shared bits */
+#define OMAP3430_ST_USBTLL_SHIFT			2
+#define OMAP3430_ST_USBTLL_MASK				(1 << 2)
+
 /* CM_FCLKEN_WKUP, CM_ICLKEN_WKUP, PM_WKEN_WKUP shared bits */
 #define OMAP3430_EN_GPIO1				(1 << 3)
 #define OMAP3430_EN_GPIO1_SHIFT				3
diff --git a/arch/arm/mach-omap2/prm-regbits-34xx.h
b/arch/arm/mach-omap2/prm-regbits-34xx.h
index 06fee29..f0a6395 100644
--- a/arch/arm/mach-omap2/prm-regbits-34xx.h
+++ b/arch/arm/mach-omap2/prm-regbits-34xx.h
@@ -332,6 +332,8 @@
 /* PM_IVA2GRPSEL1_CORE specific bits */

 /* PM_WKST1_CORE specific bits */
+#define OMAP3430_ST_MMC3_SHIFT				30
+#define OMAP3430_ST_MMC3_MASK				(1 << 30)

 /* PM_PWSTCTRL_CORE specific bits */
 #define OMAP3430_MEM2ONSTATE_SHIFT			18
@@ -432,6 +434,9 @@

 /* PM_PREPWSTST_PER specific bits */

+/* PM_WKST_USBHOST specific bits */
+#define OMAP3430_ST_USBHOST				(1 << 0)
+
 /* RM_RSTST_EMU specific bits */

 /* PM_PWSTST_EMU specific bits */
diff --git a/arch/arm/mach-omap2/wake34xx.c b/arch/arm/mach-omap2/wake34xx.c
new file mode 100644
index 0000000..86aac4f
--- /dev/null
+++ b/arch/arm/mach-omap2/wake34xx.c
@@ -0,0 +1,681 @@
+/*
+ * wake34xx.c
+ *
+ * Copyright (c) 2009 Samsung Eletronics
+ *
+ * Author: Kim Kyuwon <q1.kim@samsung.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/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+
+#include <mach/pm.h>
+#include <mach/gpio.h>
+#include <mach/wake.h>
+
+#include "prm-regbits-34xx.h"
+
+/*
+ * Sometimes, it is necessary to find out "what does wake up my board from
+ * suspend?". Notifying wake-up source feature may be used to blame
+ * unexpected wake-up events which increase power consumption. And user
+ * mode applications can act smartly according to the wake-up event from
+ * Suspend-to-RAM state to minimize power consumption. Note that this
+ * driver can't inform wake-up events from idle state. This driver uses
+ * sysfs interface to give information to user mode applications.
+ */
+
+#define DOMAIN_IS_WKUP		(1 << 0)
+#define DOMAIN_IS_PER		(1 << 1)
+#define DOMAIN_IS_CORE1		(1 << 2)
+#define DOMAIN_IS_CORE3		(1 << 3)
+#define DOMAIN_IS_USBHOST	(1 << 4)
+
+#define WAKE_STR_LEN		64
+#define WAKE_BUF_LEN		32
+
+static char wakeup_gpio[WAKE_STR_LEN];
+
+struct pm_wakeup_status {
+	u32	wkup;
+	u32	per;
+	u32	core1;
+	u32	core3;
+	u32	usbhost;
+};
+
+struct omap_wake {
+	u32			pending_irqs[INTCPS_NR_MIR_REGS];
+
+	struct pm_wakeup_status	pm_wkst;
+};
+
+struct wake_event {
+	const u32			mask;
+	const u32			domain;
+	const char			*name;
+
+	/* OMAP chip types that this wakeup status is valid on */
+	const struct omap_chip_id	omap_chip;
+};
+
+
+/* Note: Allowed to use Only in the wakeup_source_show() function */
+static struct omap_wake *g_wake;
+
+static struct wake_event omap3_wake_events[] = {
+	{	/* WKUP */
+		.mask = OMAP3430_ST_IO_CHAIN,
+		.domain = DOMAIN_IS_WKUP,
+		.name = "ST_IO_CHAIN",
+		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+	}, {
+		.mask = OMAP3430_ST_IO,
+		.domain = DOMAIN_IS_WKUP,
+		.name = "ST_IO",
+		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+	}, {
+		.mask = OMAP3430_ST_SR2_MASK,
+		.domain = DOMAIN_IS_WKUP,
+		.name = "ST_SR2",
+		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+	}, {
+		.mask = OMAP3430_ST_SR1_MASK,
+		.domain = DOMAIN_IS_WKUP,
+		.name = "ST_SR1",
+		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+	}, {
+		.mask = OMAP3430_ST_GPIO1_MASK,
+		.domain = DOMAIN_IS_WKUP,
+		.name = "ST_GPIO1",
+		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+	}, {
+		.mask = OMAP3430_ST_GPT12_MASK,
+		.domain = DOMAIN_IS_WKUP,
+		.name = "ST_GPT12",
+		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+	}, {
+		.mask = OMAP3430_ST_GPT1_MASK,
+		.domain = DOMAIN_IS_WKUP,
+		.name = "ST_GPT1",
+		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+	}, {	/* PER */
+		.mask = OMAP3430_ST_GPIO6_MASK,
+		.domain = DOMAIN_IS_PER,
+		.name = "ST_GPIO6",
+		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+	}, {
+		.mask = OMAP3430_ST_GPIO5_MASK,
+		.domain = DOMAIN_IS_PER,
+		.name = "ST_GPIO5",
+		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+	}, {
+		.mask = OMAP3430_ST_GPIO4_MASK,
+		.domain = DOMAIN_IS_PER,
+		.name = "ST_GPIO4",
+		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+	}, {
+		.mask = OMAP3430_ST_GPIO3_MASK,
+		.domain = DOMAIN_IS_PER,
+		.name = "ST_GPIO3",
+		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+	}, {
+		.mask = OMAP3430_ST_GPIO2_MASK,
+		.domain = DOMAIN_IS_PER,
+		.name = "ST_GPIO2",
+		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+	}, {
+		.mask = OMAP3430_ST_UART3_MASK,
+		.domain = DOMAIN_IS_PER,
+		.name = "ST_UART3",
+		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+	}, {
+		.mask = OMAP3430_ST_GPT9_MASK,
+		.domain = DOMAIN_IS_PER,
+		.name = "ST_GPT9",
+		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+	}, {
+		.mask = OMAP3430_ST_GPT8_MASK,
+		.domain = DOMAIN_IS_PER,
+		.name = "ST_GPT8",
+		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+	}, {
+		.mask = OMAP3430_ST_GPT7_MASK,
+		.domain = DOMAIN_IS_PER,
+		.name = "ST_GPT7",
+		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+	}, {
+		.mask = OMAP3430_ST_GPT6_MASK,
+		.domain = DOMAIN_IS_PER,
+		.name = "ST_GPT6",
+		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+	}, {
+		.mask = OMAP3430_ST_GPT5_MASK,
+		.domain = DOMAIN_IS_PER,
+		.name = "ST_GPT5",
+		.omap_chip =  OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+	},  {
+		.mask = OMAP3430_ST_GPT4_MASK,
+		.domain = DOMAIN_IS_PER,
+		.name = "ST_GPT4",
+		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+	}, {
+		.mask = OMAP3430_ST_GPT3_MASK,
+		.domain = DOMAIN_IS_PER,
+		.name = "ST_GPT3",
+		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+	}, {
+		.mask = OMAP3430_ST_GPT2_MASK,
+		.domain = DOMAIN_IS_PER,
+		.name = "ST_GPT2",
+		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+	}, {
+		.mask = OMAP3430_EN_MCBSP4,
+		.domain = DOMAIN_IS_PER,
+		.name = "EN_MCBSP4",
+		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+	}, {
+		.mask = OMAP3430_EN_MCBSP3,
+		.domain = DOMAIN_IS_PER,
+		.name = "EN_MCBSP3",
+		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+	}, {
+		.mask = OMAP3430_EN_MCBSP2,
+		.domain = DOMAIN_IS_PER,
+		.name = "EN_MCBSP2",
+		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+	}, {	/* CORE1 */
+		.mask = OMAP3430_ST_MMC3_MASK,
+		.domain = DOMAIN_IS_CORE1,
+		.name = "ST_MMC3",
+		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+	}, {
+		.mask = OMAP3430_ST_MMC2_MASK,
+		.domain = DOMAIN_IS_CORE1,
+		.name = "ST_MMC2",
+		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+	}, {
+		.mask = OMAP3430_ST_MMC1_MASK,
+		.domain = DOMAIN_IS_CORE1,
+		.name = "ST_MMC1",
+		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+	}, {
+		.mask = OMAP3430_ST_MCSPI4_MASK,
+		.domain = DOMAIN_IS_CORE1,
+		.name = "ST_MCSPI4",
+		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+	}, {
+		.mask = OMAP3430_ST_MCSPI3_MASK,
+		.domain = DOMAIN_IS_CORE1,
+		.name = "ST_MCSPI3",
+		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+	}, {
+		.mask = OMAP3430_ST_MCSPI2_MASK,
+		.domain = DOMAIN_IS_CORE1,
+		.name = "ST_MCSPI2",
+		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+	}, {
+		.mask = OMAP3430_ST_MCSPI1_MASK,
+		.domain = DOMAIN_IS_CORE1,
+		.name = "ST_MCSPI1",
+		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+	}, {
+		.mask = OMAP3430_ST_I2C3_MASK,
+		.domain = DOMAIN_IS_CORE1,
+		.name = "ST_I2C3",
+		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+	}, {
+		.mask = OMAP3430_ST_I2C2_MASK,
+		.domain = DOMAIN_IS_CORE1,
+		.name = "ST_I2C2",
+		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+	}, {
+		.mask = OMAP3430_ST_I2C1_MASK,
+		.domain = DOMAIN_IS_CORE1,
+		.name = "ST_I2C1",
+		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+	}, {
+		.mask = OMAP3430_ST_UART1_MASK,
+		.domain = DOMAIN_IS_CORE1,
+		.name = "ST_UART1",
+		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+	}, {
+		.mask = OMAP3430_ST_GPT11_MASK,
+		.domain = DOMAIN_IS_CORE1,
+		.name = "ST_GPT11",
+		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+	}, {
+		.mask = OMAP3430_ST_GPT10_MASK,
+		.domain = DOMAIN_IS_CORE1,
+		.name = "ST_GPT10",
+		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+	}, {
+		.mask = OMAP3430_ST_MCBSP5_MASK,
+		.domain = DOMAIN_IS_CORE1,
+		.name = "ST_MCBSP5",
+		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+	}, {
+		.mask = OMAP3430_ST_MCBSP1_MASK,
+		.domain = DOMAIN_IS_CORE1,
+		.name = "ST_MCBSP1",
+		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+	}, {
+		.mask = OMAP3430ES1_ST_FSHOSTUSB_MASK,
+		.domain = DOMAIN_IS_CORE1,
+		.name = "ST_FSHOSTUSB",
+		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430ES1),
+	}, {
+		.mask = OMAP3430ES1_ST_HSOTGUSB_MASK,
+		.domain = DOMAIN_IS_CORE1,
+		.name = "ST_HSOTGUSB",
+		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430ES1),
+	}, {
+		.mask = OMAP3430_ST_D2D_MASK,
+		.domain = DOMAIN_IS_CORE1,
+		.name = "ST_D2D",
+		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430ES1),
+	}, {
+		.mask = OMAP3430ES2_ST_HSOTGUSB_STDBY_MASK,
+		.domain = DOMAIN_IS_CORE1,
+		.name = "ST_HSOTGUSB",
+		.omap_chip = OMAP_CHIP_INIT(CHIP_GE_OMAP3430ES2),
+	}, {	/* CORE3 */
+		.mask = OMAP3430_ST_USBTLL_MASK,
+		.domain = DOMAIN_IS_CORE3,
+		.name = "ST_USBTLL",
+		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
+	}, {	/* USBHOST */
+		.mask = OMAP3430_ST_USBHOST,
+		.domain = DOMAIN_IS_USBHOST,
+		.name = "ST_USBHOST",
+		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430ES1),
+	},
+};
+
+static ssize_t wakeup_source_show(struct kobject *kobj,
+				struct kobj_attribute *attr, char *buf);
+
+/*
+ * Get the first pending MPU IRQ number from 'irq_start'.
+ * If none, return -EINVAL.
+ */
+static int omap3_wake_get_pending_irq(struct omap_wake *wake,
+						unsigned int irq_start)
+{
+	int i, bits_skip, idx_start;
+
+	if (irq_start >= INTCPS_NR_IRQS)
+		return -EINVAL;
+
+	bits_skip = irq_start % IRQ_BITS_PER_REG;
+	idx_start = irq_start / IRQ_BITS_PER_REG;
+
+	for (i = idx_start; i < ARRAY_SIZE(wake->pending_irqs); i++) {
+		unsigned long val, bit;
+
+		val = wake->pending_irqs[i];
+		if (!val)
+			continue;
+
+		if (idx_start == i)
+			bit = find_next_bit(&val, IRQ_BITS_PER_REG, bits_skip);
+		else
+			bit = find_first_bit(&val, IRQ_BITS_PER_REG);
+
+		if (bit < IRQ_BITS_PER_REG)
+			return i * IRQ_BITS_PER_REG + bit;
+	}
+
+	return -EINVAL;
+}
+
+static void omap3_wake_strncat(char *dest, char *src, size_t count)
+{
+	int len;
+
+	if (!src[0])
+		return;
+
+	if (dest[0])
+		len = strlen(dest) + strlen(src) + 2;
+	else
+		len = strlen(dest) + strlen(src);
+
+	if (len > count) {
+		pr_err("Can't strncat: %s\n", src);
+		return;
+	}
+
+	if (dest[0])
+		strcat(dest, ", ");
+	strcat(dest, src);
+}
+
+static u32 omap3_wake_get_domain_wkst(struct omap_wake *wake, u32 domain)
+{
+	struct pm_wakeup_status	*pm_wkst = &wake->pm_wkst;
+
+	switch (domain) {
+	case DOMAIN_IS_WKUP:
+		return pm_wkst->wkup;
+	case DOMAIN_IS_PER:
+		return pm_wkst->per;
+	case DOMAIN_IS_CORE1:
+		return pm_wkst->core1;
+	case DOMAIN_IS_CORE3:
+		return pm_wkst->core3;
+	case DOMAIN_IS_USBHOST:
+		return pm_wkst->usbhost;
+	default:
+		pr_err("Invalid domain ID: %d\n", domain);
+		break;
+	}
+
+	return -EINVAL;
+}
+
+static void omap3_wake_lookup_event(struct omap_wake *wake, char *event,
+					char *wake_event, size_t count)
+{
+	struct wake_event *events = omap3_wake_events;
+	int i, len = ARRAY_SIZE(omap3_wake_events);
+	u32 wkst;
+
+	for (i = 0; i < len; i++) {
+		if (!omap_chip_is(events[i].omap_chip))
+			continue;
+
+		wkst = omap3_wake_get_domain_wkst(wake, events[i].domain);
+		if (wkst <= 0)
+			continue;
+
+		if (wkst & events[i].mask) {
+			omap3_wake_strncat(wake_event,
+					(char *)events[i].name, count);
+		}
+	}
+}
+
+/* Detect wake-up events */
+static void omap3_wake_dump_wakeup(struct omap_wake *wake,
+					char *wake_irq, char *wake_event,
+					size_t irq_size, size_t event_size)
+{
+	char buf[WAKE_BUF_LEN] = {0, };
+	int irq, len, gpio_irq = 0, prcm_irq = 0;
+
+	/* IRQ */
+	irq = omap3_wake_get_pending_irq(wake, 0);
+	while (irq >= 0) {
+		if (irq == INT_34XX_SYS_NIRQ)
+			omap3_wake_strncat(wake_event, "sys_nirq",
+							event_size - 1);
+		else if (irq == INT_34XX_PRCM_MPU_IRQ)
+			prcm_irq = 1;
+		else if (irq >= INT_34XX_GPIO_BANK1 &&
+					irq <= INT_34XX_GPIO_BANK6)
+			gpio_irq = 1;
+
+		len = strlen(wake_irq) +
+			snprintf(buf, WAKE_BUF_LEN, "%d", irq);
+		if (len > irq_size - 1)
+			break;
+
+		strcat(wake_irq, buf);
+
+		irq = omap3_wake_get_pending_irq(wake, irq + 1);
+		if (irq >= 0) {
+			len = strlen(wake_irq) + 2;
+			if (len > irq_size - 1)
+				break;
+
+			strcat(wake_irq, ", ");
+		}
+	}
+	if (gpio_irq)
+		omap3_wake_strncat(wake_event, wakeup_gpio, event_size - 1);
+
+	if (prcm_irq)
+		omap3_wake_lookup_event(wake, buf, wake_event, event_size - 1);
+
+	if (!wake_irq[0])
+		strncpy(wake_irq, "Unknown", irq_size - 1);
+
+	if (!wake_event[0])
+		strncpy(wake_event, "Unknown", event_size - 1);
+}
+
+static struct kobj_attribute wakeup_irq_attr =
+	__ATTR(omap_resume_irq, 0644, wakeup_source_show, NULL);
+
+static struct kobj_attribute wakeup_event_attr =
+	__ATTR(omap_resume_event, 0644, wakeup_source_show, NULL);
+
+static ssize_t wakeup_source_show(struct kobject *kobj,
+				struct kobj_attribute *attr, char *buf)
+{
+	char wakeup_irq[WAKE_STR_LEN] = {0, };
+	char wakeup_event[WAKE_STR_LEN] = {0, };
+
+	if (!g_wake)
+		return -EINVAL;
+
+	omap3_wake_dump_wakeup(g_wake, wakeup_irq, wakeup_event,
+				sizeof(wakeup_irq), sizeof(wakeup_event));
+
+	if (attr == &wakeup_irq_attr)
+		return sprintf(buf, "%s\n", wakeup_irq);
+	else if (attr == &wakeup_event_attr)
+		return sprintf(buf, "%s\n", wakeup_event);
+	else
+		return -EINVAL;
+}
+
+static irqreturn_t omap3_wake_gpio_interrupt(int irq, void *dev_id)
+{
+	omap3_wake_strncat(wakeup_gpio, dev_id, sizeof(wakeup_gpio) - 1);
+
+	return IRQ_HANDLED;
+}
+
+static int __devinit omap3_wake_probe(struct platform_device *pdev)
+{
+	struct omap_wake *wake;
+	struct omap_wake_platform_data *pdata = pdev->dev.platform_data;
+	struct gpio_wake *gw;
+	int i, ret;
+
+	wake = kzalloc(sizeof(struct omap_wake), GFP_KERNEL);
+	if (wake == NULL) {
+		dev_err(&pdev->dev, "failed to allocate driver data\n");
+		return -ENOMEM;
+	}
+
+	platform_set_drvdata(pdev, wake);
+
+	/*
+	 * It may be good to configure GPIO wake-up sources in each driver.
+	 * Buf if the specific device driver doesn't exist, you can use
+	 * omap-wake driver to configure gpio wake-up sources.
+	 */
+	for (i = 0; i < pdata->gpio_wake_num; i++) {
+		gw = pdata->gpio_wakes + i;
+
+		if (gw->request) {
+			ret = gpio_request(gw->gpio, gw->name);
+			if (ret) {
+				dev_err(&pdev->dev, "can't request gpio%d"
+					", return %d\n", gw->gpio, ret);
+				goto failed_free_gpio;
+			}
+		}
+		gpio_direction_input(gw->gpio);
+		enable_irq_wake(gpio_to_irq(gw->gpio));
+	}
+
+	/*
+	 * In wakeup_source_show(), we can't access platform_device
+	 * or omap_wake structure without a global variable. so 'g_wake' is
+	 * needed, but please use it carefully.
+	 */
+	g_wake = wake;
+
+	ret = sysfs_create_file(power_kobj, &wakeup_irq_attr.attr);
+	if (ret)
+		dev_err(&pdev->dev, "sysfs_create_file %s failed: %d\n",
+					wakeup_irq_attr.attr.name, ret);
+
+	ret = sysfs_create_file(power_kobj, &wakeup_event_attr.attr);
+	if (ret)
+		dev_err(&pdev->dev, "sysfs_create_file %s failed: %d\n",
+					wakeup_event_attr.attr.name, ret);
+
+	return 0;
+
+failed_free_gpio:
+	for (i--; i >= 0; i--) {
+		gw = pdata->gpio_wakes + i;
+
+		if (gw->request)
+			gpio_free(gw->gpio);
+	}
+	kfree(wake);
+
+	return ret;
+}
+
+static int __devexit omap3_wake_remove(struct platform_device *pdev)
+{
+	struct omap_wake *wake = platform_get_drvdata(pdev);
+	struct omap_wake_platform_data *pdata = pdev->dev.platform_data;
+	struct gpio_wake *gw;
+	int i;
+
+	for (i = 0; i < pdata->gpio_wake_num; i++) {
+		gw = pdata->gpio_wakes + i;
+
+		if (gw->request)
+			gpio_free(gw->gpio);
+
+		disable_irq_wake(gpio_to_irq(gw->gpio));
+	}
+	kfree(wake);
+
+	return 0;
+}
+
+static int omap3_wake_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	struct omap_wake_platform_data *pdata = pdev->dev.platform_data;
+	struct gpio_wake *gw;
+	int i, ret;
+
+	for (i = 0; i < pdata->gpio_wake_num; i++) {
+		gw = pdata->gpio_wakes + i;
+
+		ret = request_irq(gpio_to_irq(gw->gpio),
+						omap3_wake_gpio_interrupt,
+						gw->irqflag | IRQF_SHARED,
+						gw->name, (void *)gw->name);
+		if (ret) {
+			dev_err(&pdev->dev, "can't get IRQ%d, return %d\n",
+						gpio_to_irq(gw->gpio), ret);
+			goto failed_free_irq;
+		}
+	}
+
+	memset(wakeup_gpio, 0x0, WAKE_STR_LEN);
+
+	return 0;
+
+failed_free_irq:
+	for (i--; i >= 0; i--) {
+		gw = pdata->gpio_wakes + i;
+		free_irq(gpio_to_irq(gw->gpio),  (void *)gw->name);
+	}
+
+	return ret;
+}
+
+static int omap3_wake_resume_early(struct platform_device *pdev)
+{
+	struct omap_wake *wake = platform_get_drvdata(pdev);
+	struct pm_wakeup_status *pm_wkst = &wake->pm_wkst;
+
+	omap_get_pending_irqs(wake->pending_irqs,
+					ARRAY_SIZE(wake->pending_irqs));
+
+	/* If PRCM interrupt generates this system wake-up event, */
+	if (wake->pending_irqs[0] & (0x1 << INT_34XX_PRCM_MPU_IRQ)) {
+		pm_wkst->wkup = prm_read_mod_reg(WKUP_MOD, PM_WKST);
+		pm_wkst->core1 = prm_read_mod_reg(CORE_MOD, PM_WKST1);
+		pm_wkst->core3 =
+			prm_read_mod_reg(CORE_MOD, OMAP3430ES2_PM_WKST3);
+		pm_wkst->per = prm_read_mod_reg(OMAP3430_PER_MOD, PM_WKST);
+		pm_wkst->usbhost =
+			prm_read_mod_reg(OMAP3430ES2_USBHOST_MOD, PM_WKST);
+	}
+
+	return 0;
+}
+
+static int omap3_wake_resume(struct platform_device *pdev)
+{
+	struct omap_wake_platform_data *pdata = pdev->dev.platform_data;
+	struct gpio_wake *gw;
+	int i;
+
+#ifdef CONFIG_PM_DEBUG
+	struct omap_wake *wake = platform_get_drvdata(pdev);
+	char wakeup_irq[WAKE_STR_LEN] = {0, };
+	char wakeup_event[WAKE_STR_LEN] = {0, };
+
+	omap3_wake_dump_wakeup(wake, wakeup_irq, wakeup_event,
+				sizeof(wakeup_irq), sizeof(wakeup_event));
+	pr_info("OMAP resume IRQ: %s\n", wakeup_irq);
+	pr_info("OMAP resume event: %s\n", wakeup_event);
+#endif
+
+	for (i = 0; i < pdata->gpio_wake_num; i++) {
+		gw = pdata->gpio_wakes + i;
+		free_irq(gpio_to_irq(gw->gpio), (void *)gw->name);
+	}
+
+	return 0;
+}
+
+static struct platform_driver omap3_wake_driver = {
+	.probe          = omap3_wake_probe,
+	.remove		= __devexit_p(omap3_wake_remove),
+	.suspend	= omap3_wake_suspend,
+	.resume_early	= omap3_wake_resume_early,
+	.resume		= omap3_wake_resume,
+	.driver         = {
+		.name   = "omap-wake",
+		.owner  = THIS_MODULE,
+	},
+};
+
+static int __init omap3_wake_init(void)
+{
+	return platform_driver_register(&omap3_wake_driver);
+}
+
+module_init(omap3_wake_init);
+
+static void __exit omap3_wake_exit(void)
+{
+	platform_driver_unregister(&omap3_wake_driver);
+}
+module_exit(omap3_wake_exit);
+
+MODULE_AUTHOR("Kim Kyuwon <q1.kim@samsung.com>");
+MODULE_DESCRIPTION("OMAP34xx wakeup driver");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/plat-omap/Kconfig b/arch/arm/plat-omap/Kconfig
index c5e8239..ddeeae2 100644
--- a/arch/arm/plat-omap/Kconfig
+++ b/arch/arm/plat-omap/Kconfig
@@ -211,6 +211,15 @@ config OMAP_IOMMU
 	  Say Y here if you want to use OMAP IOMMU support for IVA2 and
 	  Camera in OMAP3.

+config OMAP_WAKE
+	tristate "OMAP34xx wakeup source support"
+	depends on ARCH_OMAP34XX && PM
+	default n
+	help
+	  Select this option if you want to know what kind of wake-up event
+	  wakes up your board from suspend. And this option provides the
+	  unified GPIO wake-up source configuration.
+
 choice
         prompt "System timer"
 	default OMAP_MPU_TIMER
diff --git a/arch/arm/plat-omap/include/mach/irqs.h
b/arch/arm/plat-omap/include/mach/irqs.h
index c9a5b19..ee15402 100644
--- a/arch/arm/plat-omap/include/mach/irqs.h
+++ b/arch/arm/plat-omap/include/mach/irqs.h
@@ -385,9 +385,13 @@
 #define INTCPS_NR_MIR_REGS	3
 #define INTCPS_NR_IRQS		96

+/* Number of IRQ state bits in each MIR register */
+#define IRQ_BITS_PER_REG	32
+
 #ifndef __ASSEMBLY__
 extern void omap_init_irq(void);
 extern int omap_irq_pending(void);
+extern void omap_get_pending_irqs(u32 *pending_irqs, unsigned len);
 void omap3_intc_save_context(void);
 void omap3_intc_restore_context(void);
 #endif
diff --git a/arch/arm/plat-omap/include/mach/wake.h
b/arch/arm/plat-omap/include/mach/wake.h
new file mode 100644
index 0000000..7da8ec8
--- /dev/null
+++ b/arch/arm/plat-omap/include/mach/wake.h
@@ -0,0 +1,30 @@
+/*
+ * wake.h
+ *
+ * Copyright (c) 2009 Samsung Eletronics
+ *
+ * Author: Kim Kyuwon <q1.kim@samsung.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.
+ *
+ */
+
+#ifndef _WAKE_H_
+#define _WAKE_H_
+
+struct gpio_wake {
+	unsigned int 	gpio;
+	unsigned long	irqflag;
+	const char 	*name;
+	int		request;
+};
+
+struct omap_wake_platform_data{
+	struct gpio_wake	*gpio_wakes;
+	int			gpio_wake_num;
+};
+
+#endif /* _WAKE_H_ */
+
-- 
1.5.2.5


-- 
Kyuwon

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH] OMAP3: PM: Add the wakeup source driver, v3
  2009-04-03 10:43 [PATCH] OMAP3: PM: Add the wakeup source driver, v3 Kim Kyuwon
@ 2009-04-14  5:20 ` Kim Kyuwon
  2009-04-21  0:15 ` Kevin Hilman
  1 sibling, 0 replies; 6+ messages in thread
From: Kim Kyuwon @ 2009-04-14  5:20 UTC (permalink / raw)
  To: Kevin Hilman; +Cc: OMAP

Hi Kevin,

Have you had chance to review this new version of wakeup driver? :)

Regards,
Kyuwon

On Fri, Apr 3, 2009 at 7:43 PM, Kim Kyuwon <chammoru@gmail.com> wrote:
> Sometimes, it is necessary to find out "what does wake up my board
> from suspend?". Notifying wake-up source feature may be used to blame
> unexpected wake-up events which increase power consumption. And user
> mode applications can act smartly according to the wake-up event from
> Suspend-to-RAM state to minimize power consumption. Note that this
> driver can't inform wake-up events from idle state. This driver uses
> sysfs interface to give information to user mode applications like:
>
> cat /sys/power/omap_resume_irq
> cat /sys/power/omap_resume_event
>
> This driver also privides the unified GPIO wake-up source
> configuration. specific GPIO settings in the board files are:
>
> /* Wakeup source configuration */
> static struct gpio_wake boardname_gpio_wake[] = {
>        { 23,   IRQF_TRIGGER_RISING,    "BT_WAKEUP",    1},
>        { 24,   IRQF_TRIGGER_RISING,    "USB_DETECT",   1},
> };
>
> static struct omap_wake_platform_data boardname_wake_data = {
>        .gpio_wakes             = boardname_gpio_wake,
>        .gpio_wake_num          = ARRAY_SIZE(boardname_gpio_wake),
> };
>
> static struct platform_device boardname_wakeup = {
>        .name                   = "omap-wake",
>        .id                     = -1,
>        .dev                    = {
>                .platform_data  = &boardname_wake_data,
>        },
> };
>
> The patch adds Kconfig options "OMAP34xx wakeup source support" under
> "System type"->"TI OMAP implementations" menu.
>
> Signed-off-by: Kim Kyuwon <q1.kim@samsung.com>
> ---
>  arch/arm/mach-omap2/Makefile           |    1 +
>  arch/arm/mach-omap2/irq.c              |   21 +-
>  arch/arm/mach-omap2/prcm-common.h      |    4 +
>  arch/arm/mach-omap2/prm-regbits-34xx.h |    5 +
>  arch/arm/mach-omap2/wake34xx.c         |  681 ++++++++++++++++++++++++++++++++
>  arch/arm/plat-omap/Kconfig             |    9 +
>  arch/arm/plat-omap/include/mach/irqs.h |    4 +
>  arch/arm/plat-omap/include/mach/wake.h |   30 ++
>  8 files changed, 752 insertions(+), 3 deletions(-)
>  create mode 100644 arch/arm/mach-omap2/wake34xx.c
>  create mode 100644 arch/arm/plat-omap/include/mach/wake.h
>
> diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
> index e693efd..4d7dbca 100644
> --- a/arch/arm/mach-omap2/Makefile
> +++ b/arch/arm/mach-omap2/Makefile
> @@ -25,6 +25,7 @@ obj-$(CONFIG_ARCH_OMAP2)              += pm24xx.o
>  obj-$(CONFIG_ARCH_OMAP24XX)            += sleep24xx.o
>  obj-$(CONFIG_ARCH_OMAP3)               += pm34xx.o sleep34xx.o cpuidle34xx.o
>  obj-$(CONFIG_PM_DEBUG)                 += pm-debug.o
> +obj-$(CONFIG_OMAP_WAKE)                        += wake34xx.o
>  endif
>
>  # SmartReflex driver
> diff --git a/arch/arm/mach-omap2/irq.c b/arch/arm/mach-omap2/irq.c
> index 700fc3d..18ac725 100644
> --- a/arch/arm/mach-omap2/irq.c
> +++ b/arch/arm/mach-omap2/irq.c
> @@ -33,9 +33,6 @@
>  #define INTC_MIR_SET0          0x008c
>  #define INTC_PENDING_IRQ0      0x0098
>
> -/* Number of IRQ state bits in each MIR register */
> -#define IRQ_BITS_PER_REG       32
> -
>  /*
>  * OMAP2 has a number of different interrupt controllers, each interrupt
>  * controller is identified as its own "bank". Register definitions are
> @@ -193,6 +190,24 @@ int omap_irq_pending(void)
>        return 0;
>  }
>
> +void omap_get_pending_irqs(u32 *pending_irqs, unsigned len)
> +{
> +       int i, j = 0;
> +
> +       for (i = 0; i < ARRAY_SIZE(irq_banks); i++) {
> +               struct omap_irq_bank *bank = irq_banks + i;
> +               int irq;
> +
> +               for (irq = 0; irq < bank->nr_irqs && j < len;
> +                                               irq += IRQ_BITS_PER_REG) {
> +                       int offset = irq & (~(IRQ_BITS_PER_REG - 1));
> +
> +                       pending_irqs[j++] = intc_bank_read_reg(bank,
> +                                       (INTC_PENDING_IRQ0 + offset));
> +               }
> +       }
> +}
> +
>  void __init omap_init_irq(void)
>  {
>        unsigned long nr_of_irqs = 0;
> diff --git a/arch/arm/mach-omap2/prcm-common.h
> b/arch/arm/mach-omap2/prcm-common.h
> index cb1ae84..1f340aa 100644
> --- a/arch/arm/mach-omap2/prcm-common.h
> +++ b/arch/arm/mach-omap2/prcm-common.h
> @@ -273,6 +273,10 @@
>  #define OMAP3430_ST_D2D_SHIFT                          3
>  #define OMAP3430_ST_D2D_MASK                           (1 << 3)
>
> +/* PM_WKST3_CORE, CM_IDLEST3_CORE shared bits */
> +#define OMAP3430_ST_USBTLL_SHIFT                       2
> +#define OMAP3430_ST_USBTLL_MASK                                (1 << 2)
> +
>  /* CM_FCLKEN_WKUP, CM_ICLKEN_WKUP, PM_WKEN_WKUP shared bits */
>  #define OMAP3430_EN_GPIO1                              (1 << 3)
>  #define OMAP3430_EN_GPIO1_SHIFT                                3
> diff --git a/arch/arm/mach-omap2/prm-regbits-34xx.h
> b/arch/arm/mach-omap2/prm-regbits-34xx.h
> index 06fee29..f0a6395 100644
> --- a/arch/arm/mach-omap2/prm-regbits-34xx.h
> +++ b/arch/arm/mach-omap2/prm-regbits-34xx.h
> @@ -332,6 +332,8 @@
>  /* PM_IVA2GRPSEL1_CORE specific bits */
>
>  /* PM_WKST1_CORE specific bits */
> +#define OMAP3430_ST_MMC3_SHIFT                         30
> +#define OMAP3430_ST_MMC3_MASK                          (1 << 30)
>
>  /* PM_PWSTCTRL_CORE specific bits */
>  #define OMAP3430_MEM2ONSTATE_SHIFT                     18
> @@ -432,6 +434,9 @@
>
>  /* PM_PREPWSTST_PER specific bits */
>
> +/* PM_WKST_USBHOST specific bits */
> +#define OMAP3430_ST_USBHOST                            (1 << 0)
> +
>  /* RM_RSTST_EMU specific bits */
>
>  /* PM_PWSTST_EMU specific bits */
> diff --git a/arch/arm/mach-omap2/wake34xx.c b/arch/arm/mach-omap2/wake34xx.c
> new file mode 100644
> index 0000000..86aac4f
> --- /dev/null
> +++ b/arch/arm/mach-omap2/wake34xx.c
> @@ -0,0 +1,681 @@
> +/*
> + * wake34xx.c
> + *
> + * Copyright (c) 2009 Samsung Eletronics
> + *
> + * Author: Kim Kyuwon <q1.kim@samsung.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/kernel.h>
> +#include <linux/init.h>
> +#include <linux/platform_device.h>
> +#include <linux/interrupt.h>
> +
> +#include <mach/pm.h>
> +#include <mach/gpio.h>
> +#include <mach/wake.h>
> +
> +#include "prm-regbits-34xx.h"
> +
> +/*
> + * Sometimes, it is necessary to find out "what does wake up my board from
> + * suspend?". Notifying wake-up source feature may be used to blame
> + * unexpected wake-up events which increase power consumption. And user
> + * mode applications can act smartly according to the wake-up event from
> + * Suspend-to-RAM state to minimize power consumption. Note that this
> + * driver can't inform wake-up events from idle state. This driver uses
> + * sysfs interface to give information to user mode applications.
> + */
> +
> +#define DOMAIN_IS_WKUP         (1 << 0)
> +#define DOMAIN_IS_PER          (1 << 1)
> +#define DOMAIN_IS_CORE1                (1 << 2)
> +#define DOMAIN_IS_CORE3                (1 << 3)
> +#define DOMAIN_IS_USBHOST      (1 << 4)
> +
> +#define WAKE_STR_LEN           64
> +#define WAKE_BUF_LEN           32
> +
> +static char wakeup_gpio[WAKE_STR_LEN];
> +
> +struct pm_wakeup_status {
> +       u32     wkup;
> +       u32     per;
> +       u32     core1;
> +       u32     core3;
> +       u32     usbhost;
> +};
> +
> +struct omap_wake {
> +       u32                     pending_irqs[INTCPS_NR_MIR_REGS];
> +
> +       struct pm_wakeup_status pm_wkst;
> +};
> +
> +struct wake_event {
> +       const u32                       mask;
> +       const u32                       domain;
> +       const char                      *name;
> +
> +       /* OMAP chip types that this wakeup status is valid on */
> +       const struct omap_chip_id       omap_chip;
> +};
> +
> +
> +/* Note: Allowed to use Only in the wakeup_source_show() function */
> +static struct omap_wake *g_wake;
> +
> +static struct wake_event omap3_wake_events[] = {
> +       {       /* WKUP */
> +               .mask = OMAP3430_ST_IO_CHAIN,
> +               .domain = DOMAIN_IS_WKUP,
> +               .name = "ST_IO_CHAIN",
> +               .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +       }, {
> +               .mask = OMAP3430_ST_IO,
> +               .domain = DOMAIN_IS_WKUP,
> +               .name = "ST_IO",
> +               .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +       }, {
> +               .mask = OMAP3430_ST_SR2_MASK,
> +               .domain = DOMAIN_IS_WKUP,
> +               .name = "ST_SR2",
> +               .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +       }, {
> +               .mask = OMAP3430_ST_SR1_MASK,
> +               .domain = DOMAIN_IS_WKUP,
> +               .name = "ST_SR1",
> +               .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +       }, {
> +               .mask = OMAP3430_ST_GPIO1_MASK,
> +               .domain = DOMAIN_IS_WKUP,
> +               .name = "ST_GPIO1",
> +               .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +       }, {
> +               .mask = OMAP3430_ST_GPT12_MASK,
> +               .domain = DOMAIN_IS_WKUP,
> +               .name = "ST_GPT12",
> +               .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +       }, {
> +               .mask = OMAP3430_ST_GPT1_MASK,
> +               .domain = DOMAIN_IS_WKUP,
> +               .name = "ST_GPT1",
> +               .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +       }, {    /* PER */
> +               .mask = OMAP3430_ST_GPIO6_MASK,
> +               .domain = DOMAIN_IS_PER,
> +               .name = "ST_GPIO6",
> +               .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +       }, {
> +               .mask = OMAP3430_ST_GPIO5_MASK,
> +               .domain = DOMAIN_IS_PER,
> +               .name = "ST_GPIO5",
> +               .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +       }, {
> +               .mask = OMAP3430_ST_GPIO4_MASK,
> +               .domain = DOMAIN_IS_PER,
> +               .name = "ST_GPIO4",
> +               .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +       }, {
> +               .mask = OMAP3430_ST_GPIO3_MASK,
> +               .domain = DOMAIN_IS_PER,
> +               .name = "ST_GPIO3",
> +               .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +       }, {
> +               .mask = OMAP3430_ST_GPIO2_MASK,
> +               .domain = DOMAIN_IS_PER,
> +               .name = "ST_GPIO2",
> +               .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +       }, {
> +               .mask = OMAP3430_ST_UART3_MASK,
> +               .domain = DOMAIN_IS_PER,
> +               .name = "ST_UART3",
> +               .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +       }, {
> +               .mask = OMAP3430_ST_GPT9_MASK,
> +               .domain = DOMAIN_IS_PER,
> +               .name = "ST_GPT9",
> +               .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +       }, {
> +               .mask = OMAP3430_ST_GPT8_MASK,
> +               .domain = DOMAIN_IS_PER,
> +               .name = "ST_GPT8",
> +               .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +       }, {
> +               .mask = OMAP3430_ST_GPT7_MASK,
> +               .domain = DOMAIN_IS_PER,
> +               .name = "ST_GPT7",
> +               .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +       }, {
> +               .mask = OMAP3430_ST_GPT6_MASK,
> +               .domain = DOMAIN_IS_PER,
> +               .name = "ST_GPT6",
> +               .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +       }, {
> +               .mask = OMAP3430_ST_GPT5_MASK,
> +               .domain = DOMAIN_IS_PER,
> +               .name = "ST_GPT5",
> +               .omap_chip =  OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +       },  {
> +               .mask = OMAP3430_ST_GPT4_MASK,
> +               .domain = DOMAIN_IS_PER,
> +               .name = "ST_GPT4",
> +               .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +       }, {
> +               .mask = OMAP3430_ST_GPT3_MASK,
> +               .domain = DOMAIN_IS_PER,
> +               .name = "ST_GPT3",
> +               .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +       }, {
> +               .mask = OMAP3430_ST_GPT2_MASK,
> +               .domain = DOMAIN_IS_PER,
> +               .name = "ST_GPT2",
> +               .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +       }, {
> +               .mask = OMAP3430_EN_MCBSP4,
> +               .domain = DOMAIN_IS_PER,
> +               .name = "EN_MCBSP4",
> +               .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +       }, {
> +               .mask = OMAP3430_EN_MCBSP3,
> +               .domain = DOMAIN_IS_PER,
> +               .name = "EN_MCBSP3",
> +               .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +       }, {
> +               .mask = OMAP3430_EN_MCBSP2,
> +               .domain = DOMAIN_IS_PER,
> +               .name = "EN_MCBSP2",
> +               .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +       }, {    /* CORE1 */
> +               .mask = OMAP3430_ST_MMC3_MASK,
> +               .domain = DOMAIN_IS_CORE1,
> +               .name = "ST_MMC3",
> +               .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +       }, {
> +               .mask = OMAP3430_ST_MMC2_MASK,
> +               .domain = DOMAIN_IS_CORE1,
> +               .name = "ST_MMC2",
> +               .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +       }, {
> +               .mask = OMAP3430_ST_MMC1_MASK,
> +               .domain = DOMAIN_IS_CORE1,
> +               .name = "ST_MMC1",
> +               .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +       }, {
> +               .mask = OMAP3430_ST_MCSPI4_MASK,
> +               .domain = DOMAIN_IS_CORE1,
> +               .name = "ST_MCSPI4",
> +               .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +       }, {
> +               .mask = OMAP3430_ST_MCSPI3_MASK,
> +               .domain = DOMAIN_IS_CORE1,
> +               .name = "ST_MCSPI3",
> +               .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +       }, {
> +               .mask = OMAP3430_ST_MCSPI2_MASK,
> +               .domain = DOMAIN_IS_CORE1,
> +               .name = "ST_MCSPI2",
> +               .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +       }, {
> +               .mask = OMAP3430_ST_MCSPI1_MASK,
> +               .domain = DOMAIN_IS_CORE1,
> +               .name = "ST_MCSPI1",
> +               .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +       }, {
> +               .mask = OMAP3430_ST_I2C3_MASK,
> +               .domain = DOMAIN_IS_CORE1,
> +               .name = "ST_I2C3",
> +               .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +       }, {
> +               .mask = OMAP3430_ST_I2C2_MASK,
> +               .domain = DOMAIN_IS_CORE1,
> +               .name = "ST_I2C2",
> +               .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +       }, {
> +               .mask = OMAP3430_ST_I2C1_MASK,
> +               .domain = DOMAIN_IS_CORE1,
> +               .name = "ST_I2C1",
> +               .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +       }, {
> +               .mask = OMAP3430_ST_UART1_MASK,
> +               .domain = DOMAIN_IS_CORE1,
> +               .name = "ST_UART1",
> +               .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +       }, {
> +               .mask = OMAP3430_ST_GPT11_MASK,
> +               .domain = DOMAIN_IS_CORE1,
> +               .name = "ST_GPT11",
> +               .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +       }, {
> +               .mask = OMAP3430_ST_GPT10_MASK,
> +               .domain = DOMAIN_IS_CORE1,
> +               .name = "ST_GPT10",
> +               .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +       }, {
> +               .mask = OMAP3430_ST_MCBSP5_MASK,
> +               .domain = DOMAIN_IS_CORE1,
> +               .name = "ST_MCBSP5",
> +               .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +       }, {
> +               .mask = OMAP3430_ST_MCBSP1_MASK,
> +               .domain = DOMAIN_IS_CORE1,
> +               .name = "ST_MCBSP1",
> +               .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +       }, {
> +               .mask = OMAP3430ES1_ST_FSHOSTUSB_MASK,
> +               .domain = DOMAIN_IS_CORE1,
> +               .name = "ST_FSHOSTUSB",
> +               .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430ES1),
> +       }, {
> +               .mask = OMAP3430ES1_ST_HSOTGUSB_MASK,
> +               .domain = DOMAIN_IS_CORE1,
> +               .name = "ST_HSOTGUSB",
> +               .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430ES1),
> +       }, {
> +               .mask = OMAP3430_ST_D2D_MASK,
> +               .domain = DOMAIN_IS_CORE1,
> +               .name = "ST_D2D",
> +               .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430ES1),
> +       }, {
> +               .mask = OMAP3430ES2_ST_HSOTGUSB_STDBY_MASK,
> +               .domain = DOMAIN_IS_CORE1,
> +               .name = "ST_HSOTGUSB",
> +               .omap_chip = OMAP_CHIP_INIT(CHIP_GE_OMAP3430ES2),
> +       }, {    /* CORE3 */
> +               .mask = OMAP3430_ST_USBTLL_MASK,
> +               .domain = DOMAIN_IS_CORE3,
> +               .name = "ST_USBTLL",
> +               .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +       }, {    /* USBHOST */
> +               .mask = OMAP3430_ST_USBHOST,
> +               .domain = DOMAIN_IS_USBHOST,
> +               .name = "ST_USBHOST",
> +               .omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430ES1),
> +       },
> +};
> +
> +static ssize_t wakeup_source_show(struct kobject *kobj,
> +                               struct kobj_attribute *attr, char *buf);
> +
> +/*
> + * Get the first pending MPU IRQ number from 'irq_start'.
> + * If none, return -EINVAL.
> + */
> +static int omap3_wake_get_pending_irq(struct omap_wake *wake,
> +                                               unsigned int irq_start)
> +{
> +       int i, bits_skip, idx_start;
> +
> +       if (irq_start >= INTCPS_NR_IRQS)
> +               return -EINVAL;
> +
> +       bits_skip = irq_start % IRQ_BITS_PER_REG;
> +       idx_start = irq_start / IRQ_BITS_PER_REG;
> +
> +       for (i = idx_start; i < ARRAY_SIZE(wake->pending_irqs); i++) {
> +               unsigned long val, bit;
> +
> +               val = wake->pending_irqs[i];
> +               if (!val)
> +                       continue;
> +
> +               if (idx_start == i)
> +                       bit = find_next_bit(&val, IRQ_BITS_PER_REG, bits_skip);
> +               else
> +                       bit = find_first_bit(&val, IRQ_BITS_PER_REG);
> +
> +               if (bit < IRQ_BITS_PER_REG)
> +                       return i * IRQ_BITS_PER_REG + bit;
> +       }
> +
> +       return -EINVAL;
> +}
> +
> +static void omap3_wake_strncat(char *dest, char *src, size_t count)
> +{
> +       int len;
> +
> +       if (!src[0])
> +               return;
> +
> +       if (dest[0])
> +               len = strlen(dest) + strlen(src) + 2;
> +       else
> +               len = strlen(dest) + strlen(src);
> +
> +       if (len > count) {
> +               pr_err("Can't strncat: %s\n", src);
> +               return;
> +       }
> +
> +       if (dest[0])
> +               strcat(dest, ", ");
> +       strcat(dest, src);
> +}
> +
> +static u32 omap3_wake_get_domain_wkst(struct omap_wake *wake, u32 domain)
> +{
> +       struct pm_wakeup_status *pm_wkst = &wake->pm_wkst;
> +
> +       switch (domain) {
> +       case DOMAIN_IS_WKUP:
> +               return pm_wkst->wkup;
> +       case DOMAIN_IS_PER:
> +               return pm_wkst->per;
> +       case DOMAIN_IS_CORE1:
> +               return pm_wkst->core1;
> +       case DOMAIN_IS_CORE3:
> +               return pm_wkst->core3;
> +       case DOMAIN_IS_USBHOST:
> +               return pm_wkst->usbhost;
> +       default:
> +               pr_err("Invalid domain ID: %d\n", domain);
> +               break;
> +       }
> +
> +       return -EINVAL;
> +}
> +
> +static void omap3_wake_lookup_event(struct omap_wake *wake, char *event,
> +                                       char *wake_event, size_t count)
> +{
> +       struct wake_event *events = omap3_wake_events;
> +       int i, len = ARRAY_SIZE(omap3_wake_events);
> +       u32 wkst;
> +
> +       for (i = 0; i < len; i++) {
> +               if (!omap_chip_is(events[i].omap_chip))
> +                       continue;
> +
> +               wkst = omap3_wake_get_domain_wkst(wake, events[i].domain);
> +               if (wkst <= 0)
> +                       continue;
> +
> +               if (wkst & events[i].mask) {
> +                       omap3_wake_strncat(wake_event,
> +                                       (char *)events[i].name, count);
> +               }
> +       }
> +}
> +
> +/* Detect wake-up events */
> +static void omap3_wake_dump_wakeup(struct omap_wake *wake,
> +                                       char *wake_irq, char *wake_event,
> +                                       size_t irq_size, size_t event_size)
> +{
> +       char buf[WAKE_BUF_LEN] = {0, };
> +       int irq, len, gpio_irq = 0, prcm_irq = 0;
> +
> +       /* IRQ */
> +       irq = omap3_wake_get_pending_irq(wake, 0);
> +       while (irq >= 0) {
> +               if (irq == INT_34XX_SYS_NIRQ)
> +                       omap3_wake_strncat(wake_event, "sys_nirq",
> +                                                       event_size - 1);
> +               else if (irq == INT_34XX_PRCM_MPU_IRQ)
> +                       prcm_irq = 1;
> +               else if (irq >= INT_34XX_GPIO_BANK1 &&
> +                                       irq <= INT_34XX_GPIO_BANK6)
> +                       gpio_irq = 1;
> +
> +               len = strlen(wake_irq) +
> +                       snprintf(buf, WAKE_BUF_LEN, "%d", irq);
> +               if (len > irq_size - 1)
> +                       break;
> +
> +               strcat(wake_irq, buf);
> +
> +               irq = omap3_wake_get_pending_irq(wake, irq + 1);
> +               if (irq >= 0) {
> +                       len = strlen(wake_irq) + 2;
> +                       if (len > irq_size - 1)
> +                               break;
> +
> +                       strcat(wake_irq, ", ");
> +               }
> +       }
> +       if (gpio_irq)
> +               omap3_wake_strncat(wake_event, wakeup_gpio, event_size - 1);
> +
> +       if (prcm_irq)
> +               omap3_wake_lookup_event(wake, buf, wake_event, event_size - 1);
> +
> +       if (!wake_irq[0])
> +               strncpy(wake_irq, "Unknown", irq_size - 1);
> +
> +       if (!wake_event[0])
> +               strncpy(wake_event, "Unknown", event_size - 1);
> +}
> +
> +static struct kobj_attribute wakeup_irq_attr =
> +       __ATTR(omap_resume_irq, 0644, wakeup_source_show, NULL);
> +
> +static struct kobj_attribute wakeup_event_attr =
> +       __ATTR(omap_resume_event, 0644, wakeup_source_show, NULL);
> +
> +static ssize_t wakeup_source_show(struct kobject *kobj,
> +                               struct kobj_attribute *attr, char *buf)
> +{
> +       char wakeup_irq[WAKE_STR_LEN] = {0, };
> +       char wakeup_event[WAKE_STR_LEN] = {0, };
> +
> +       if (!g_wake)
> +               return -EINVAL;
> +
> +       omap3_wake_dump_wakeup(g_wake, wakeup_irq, wakeup_event,
> +                               sizeof(wakeup_irq), sizeof(wakeup_event));
> +
> +       if (attr == &wakeup_irq_attr)
> +               return sprintf(buf, "%s\n", wakeup_irq);
> +       else if (attr == &wakeup_event_attr)
> +               return sprintf(buf, "%s\n", wakeup_event);
> +       else
> +               return -EINVAL;
> +}
> +
> +static irqreturn_t omap3_wake_gpio_interrupt(int irq, void *dev_id)
> +{
> +       omap3_wake_strncat(wakeup_gpio, dev_id, sizeof(wakeup_gpio) - 1);
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static int __devinit omap3_wake_probe(struct platform_device *pdev)
> +{
> +       struct omap_wake *wake;
> +       struct omap_wake_platform_data *pdata = pdev->dev.platform_data;
> +       struct gpio_wake *gw;
> +       int i, ret;
> +
> +       wake = kzalloc(sizeof(struct omap_wake), GFP_KERNEL);
> +       if (wake == NULL) {
> +               dev_err(&pdev->dev, "failed to allocate driver data\n");
> +               return -ENOMEM;
> +       }
> +
> +       platform_set_drvdata(pdev, wake);
> +
> +       /*
> +        * It may be good to configure GPIO wake-up sources in each driver.
> +        * Buf if the specific device driver doesn't exist, you can use
> +        * omap-wake driver to configure gpio wake-up sources.
> +        */
> +       for (i = 0; i < pdata->gpio_wake_num; i++) {
> +               gw = pdata->gpio_wakes + i;
> +
> +               if (gw->request) {
> +                       ret = gpio_request(gw->gpio, gw->name);
> +                       if (ret) {
> +                               dev_err(&pdev->dev, "can't request gpio%d"
> +                                       ", return %d\n", gw->gpio, ret);
> +                               goto failed_free_gpio;
> +                       }
> +               }
> +               gpio_direction_input(gw->gpio);
> +               enable_irq_wake(gpio_to_irq(gw->gpio));
> +       }
> +
> +       /*
> +        * In wakeup_source_show(), we can't access platform_device
> +        * or omap_wake structure without a global variable. so 'g_wake' is
> +        * needed, but please use it carefully.
> +        */
> +       g_wake = wake;
> +
> +       ret = sysfs_create_file(power_kobj, &wakeup_irq_attr.attr);
> +       if (ret)
> +               dev_err(&pdev->dev, "sysfs_create_file %s failed: %d\n",
> +                                       wakeup_irq_attr.attr.name, ret);
> +
> +       ret = sysfs_create_file(power_kobj, &wakeup_event_attr.attr);
> +       if (ret)
> +               dev_err(&pdev->dev, "sysfs_create_file %s failed: %d\n",
> +                                       wakeup_event_attr.attr.name, ret);
> +
> +       return 0;
> +
> +failed_free_gpio:
> +       for (i--; i >= 0; i--) {
> +               gw = pdata->gpio_wakes + i;
> +
> +               if (gw->request)
> +                       gpio_free(gw->gpio);
> +       }
> +       kfree(wake);
> +
> +       return ret;
> +}
> +
> +static int __devexit omap3_wake_remove(struct platform_device *pdev)
> +{
> +       struct omap_wake *wake = platform_get_drvdata(pdev);
> +       struct omap_wake_platform_data *pdata = pdev->dev.platform_data;
> +       struct gpio_wake *gw;
> +       int i;
> +
> +       for (i = 0; i < pdata->gpio_wake_num; i++) {
> +               gw = pdata->gpio_wakes + i;
> +
> +               if (gw->request)
> +                       gpio_free(gw->gpio);
> +
> +               disable_irq_wake(gpio_to_irq(gw->gpio));
> +       }
> +       kfree(wake);
> +
> +       return 0;
> +}
> +
> +static int omap3_wake_suspend(struct platform_device *pdev, pm_message_t state)
> +{
> +       struct omap_wake_platform_data *pdata = pdev->dev.platform_data;
> +       struct gpio_wake *gw;
> +       int i, ret;
> +
> +       for (i = 0; i < pdata->gpio_wake_num; i++) {
> +               gw = pdata->gpio_wakes + i;
> +
> +               ret = request_irq(gpio_to_irq(gw->gpio),
> +                                               omap3_wake_gpio_interrupt,
> +                                               gw->irqflag | IRQF_SHARED,
> +                                               gw->name, (void *)gw->name);
> +               if (ret) {
> +                       dev_err(&pdev->dev, "can't get IRQ%d, return %d\n",
> +                                               gpio_to_irq(gw->gpio), ret);
> +                       goto failed_free_irq;
> +               }
> +       }
> +
> +       memset(wakeup_gpio, 0x0, WAKE_STR_LEN);
> +
> +       return 0;
> +
> +failed_free_irq:
> +       for (i--; i >= 0; i--) {
> +               gw = pdata->gpio_wakes + i;
> +               free_irq(gpio_to_irq(gw->gpio),  (void *)gw->name);
> +       }
> +
> +       return ret;
> +}
> +
> +static int omap3_wake_resume_early(struct platform_device *pdev)
> +{
> +       struct omap_wake *wake = platform_get_drvdata(pdev);
> +       struct pm_wakeup_status *pm_wkst = &wake->pm_wkst;
> +
> +       omap_get_pending_irqs(wake->pending_irqs,
> +                                       ARRAY_SIZE(wake->pending_irqs));
> +
> +       /* If PRCM interrupt generates this system wake-up event, */
> +       if (wake->pending_irqs[0] & (0x1 << INT_34XX_PRCM_MPU_IRQ)) {
> +               pm_wkst->wkup = prm_read_mod_reg(WKUP_MOD, PM_WKST);
> +               pm_wkst->core1 = prm_read_mod_reg(CORE_MOD, PM_WKST1);
> +               pm_wkst->core3 =
> +                       prm_read_mod_reg(CORE_MOD, OMAP3430ES2_PM_WKST3);
> +               pm_wkst->per = prm_read_mod_reg(OMAP3430_PER_MOD, PM_WKST);
> +               pm_wkst->usbhost =
> +                       prm_read_mod_reg(OMAP3430ES2_USBHOST_MOD, PM_WKST);
> +       }
> +
> +       return 0;
> +}
> +
> +static int omap3_wake_resume(struct platform_device *pdev)
> +{
> +       struct omap_wake_platform_data *pdata = pdev->dev.platform_data;
> +       struct gpio_wake *gw;
> +       int i;
> +
> +#ifdef CONFIG_PM_DEBUG
> +       struct omap_wake *wake = platform_get_drvdata(pdev);
> +       char wakeup_irq[WAKE_STR_LEN] = {0, };
> +       char wakeup_event[WAKE_STR_LEN] = {0, };
> +
> +       omap3_wake_dump_wakeup(wake, wakeup_irq, wakeup_event,
> +                               sizeof(wakeup_irq), sizeof(wakeup_event));
> +       pr_info("OMAP resume IRQ: %s\n", wakeup_irq);
> +       pr_info("OMAP resume event: %s\n", wakeup_event);
> +#endif
> +
> +       for (i = 0; i < pdata->gpio_wake_num; i++) {
> +               gw = pdata->gpio_wakes + i;
> +               free_irq(gpio_to_irq(gw->gpio), (void *)gw->name);
> +       }
> +
> +       return 0;
> +}
> +
> +static struct platform_driver omap3_wake_driver = {
> +       .probe          = omap3_wake_probe,
> +       .remove         = __devexit_p(omap3_wake_remove),
> +       .suspend        = omap3_wake_suspend,
> +       .resume_early   = omap3_wake_resume_early,
> +       .resume         = omap3_wake_resume,
> +       .driver         = {
> +               .name   = "omap-wake",
> +               .owner  = THIS_MODULE,
> +       },
> +};
> +
> +static int __init omap3_wake_init(void)
> +{
> +       return platform_driver_register(&omap3_wake_driver);
> +}
> +
> +module_init(omap3_wake_init);
> +
> +static void __exit omap3_wake_exit(void)
> +{
> +       platform_driver_unregister(&omap3_wake_driver);
> +}
> +module_exit(omap3_wake_exit);
> +
> +MODULE_AUTHOR("Kim Kyuwon <q1.kim@samsung.com>");
> +MODULE_DESCRIPTION("OMAP34xx wakeup driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/arch/arm/plat-omap/Kconfig b/arch/arm/plat-omap/Kconfig
> index c5e8239..ddeeae2 100644
> --- a/arch/arm/plat-omap/Kconfig
> +++ b/arch/arm/plat-omap/Kconfig
> @@ -211,6 +211,15 @@ config OMAP_IOMMU
>          Say Y here if you want to use OMAP IOMMU support for IVA2 and
>          Camera in OMAP3.
>
> +config OMAP_WAKE
> +       tristate "OMAP34xx wakeup source support"
> +       depends on ARCH_OMAP34XX && PM
> +       default n
> +       help
> +         Select this option if you want to know what kind of wake-up event
> +         wakes up your board from suspend. And this option provides the
> +         unified GPIO wake-up source configuration.
> +
>  choice
>         prompt "System timer"
>        default OMAP_MPU_TIMER
> diff --git a/arch/arm/plat-omap/include/mach/irqs.h
> b/arch/arm/plat-omap/include/mach/irqs.h
> index c9a5b19..ee15402 100644
> --- a/arch/arm/plat-omap/include/mach/irqs.h
> +++ b/arch/arm/plat-omap/include/mach/irqs.h
> @@ -385,9 +385,13 @@
>  #define INTCPS_NR_MIR_REGS     3
>  #define INTCPS_NR_IRQS         96
>
> +/* Number of IRQ state bits in each MIR register */
> +#define IRQ_BITS_PER_REG       32
> +
>  #ifndef __ASSEMBLY__
>  extern void omap_init_irq(void);
>  extern int omap_irq_pending(void);
> +extern void omap_get_pending_irqs(u32 *pending_irqs, unsigned len);
>  void omap3_intc_save_context(void);
>  void omap3_intc_restore_context(void);
>  #endif
> diff --git a/arch/arm/plat-omap/include/mach/wake.h
> b/arch/arm/plat-omap/include/mach/wake.h
> new file mode 100644
> index 0000000..7da8ec8
> --- /dev/null
> +++ b/arch/arm/plat-omap/include/mach/wake.h
> @@ -0,0 +1,30 @@
> +/*
> + * wake.h
> + *
> + * Copyright (c) 2009 Samsung Eletronics
> + *
> + * Author: Kim Kyuwon <q1.kim@samsung.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.
> + *
> + */
> +
> +#ifndef _WAKE_H_
> +#define _WAKE_H_
> +
> +struct gpio_wake {
> +       unsigned int    gpio;
> +       unsigned long   irqflag;
> +       const char      *name;
> +       int             request;
> +};
> +
> +struct omap_wake_platform_data{
> +       struct gpio_wake        *gpio_wakes;
> +       int                     gpio_wake_num;
> +};
> +
> +#endif /* _WAKE_H_ */
> +
> --
> 1.5.2.5
>
>
> --
> Kyuwon
>

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] OMAP3: PM: Add the wakeup source driver, v3
  2009-04-03 10:43 [PATCH] OMAP3: PM: Add the wakeup source driver, v3 Kim Kyuwon
  2009-04-14  5:20 ` Kim Kyuwon
@ 2009-04-21  0:15 ` Kevin Hilman
  2009-04-30  7:17   ` Kim Kyuwon
  1 sibling, 1 reply; 6+ messages in thread
From: Kevin Hilman @ 2009-04-21  0:15 UTC (permalink / raw)
  To: Kim Kyuwon; +Cc: OMAP, Tony Lindgren, 박경민, riverful

[-- Attachment #1: Type: text/plain, Size: 15498 bytes --]

Kim Kyuwon <chammoru@gmail.com> writes:

> Sometimes, it is necessary to find out "what does wake up my board
> from suspend?". Notifying wake-up source feature may be used to blame
> unexpected wake-up events which increase power consumption. And user
> mode applications can act smartly according to the wake-up event from
> Suspend-to-RAM state to minimize power consumption. Note that this
> driver can't inform wake-up events from idle state. This driver uses
> sysfs interface to give information to user mode applications like:
>
> cat /sys/power/omap_resume_irq
> cat /sys/power/omap_resume_event
>
> This driver also privides the unified GPIO wake-up source
> configuration. specific GPIO settings in the board files are:
>
> /* Wakeup source configuration */
> static struct gpio_wake boardname_gpio_wake[] = {
> 	{ 23,	IRQF_TRIGGER_RISING,	"BT_WAKEUP",	1},
> 	{ 24,	IRQF_TRIGGER_RISING,	"USB_DETECT",	1},
> };
>
> static struct omap_wake_platform_data boardname_wake_data = {
> 	.gpio_wakes		= boardname_gpio_wake,
> 	.gpio_wake_num		= ARRAY_SIZE(boardname_gpio_wake),
> };
>
> static struct platform_device boardname_wakeup = {
> 	.name			= "omap-wake",
> 	.id			= -1,
> 	.dev			= {
> 		.platform_data	= &boardname_wake_data,
> 	},
> };
>
> The patch adds Kconfig options "OMAP34xx wakeup source support" under
> "System type"->"TI OMAP implementations" menu.
>
> Signed-off-by: Kim Kyuwon <q1.kim@samsung.com>

Hi Kyuwon,

While I still like the concept of this driver, I'm still not quite
happy about how it is implemented for various reasons.  Most of which
have to do with the fact that this driver does many things that really
should be the job of other layers.  In particular, the list of
omap3_wake_events is a bit troubling.  It really should be handled by
the omapdev layer.  You'll see that you are duplicating the data that
is handled there.  Rather, we should just extend the omapdev data to
handle the wakeup events for that device.

Also, I still think the WKST register reading should be done in the
PRCM interrupt handler.  In your previous attempt, you were seeing a
bunch of non-device wakeup related interrupts.   Can you try again
with the attached patch (thanks to Paul Walmseley) which should help
us debug why you were seeing spurious PRCM interrupts.

Also, I know we discussed this before, but I think the GPIO wakeup
source stuff really belongs in a separate patch, and if you think it
is still useful, and cannot be done by just enabling a GPIO IRQ from
the board file, I suggest you propose a patch to the generic GPIO
layer to add this interface.

> ---
>  arch/arm/mach-omap2/Makefile           |    1 +
>  arch/arm/mach-omap2/irq.c              |   21 +-
>  arch/arm/mach-omap2/prcm-common.h      |    4 +
>  arch/arm/mach-omap2/prm-regbits-34xx.h |    5 +
>  arch/arm/mach-omap2/wake34xx.c         |  681 ++++++++++++++++++++++++++++++++
>  arch/arm/plat-omap/Kconfig             |    9 +
>  arch/arm/plat-omap/include/mach/irqs.h |    4 +
>  arch/arm/plat-omap/include/mach/wake.h |   30 ++
>  8 files changed, 752 insertions(+), 3 deletions(-)
>  create mode 100644 arch/arm/mach-omap2/wake34xx.c
>  create mode 100644 arch/arm/plat-omap/include/mach/wake.h
>
> diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
> index e693efd..4d7dbca 100644
> --- a/arch/arm/mach-omap2/Makefile
> +++ b/arch/arm/mach-omap2/Makefile
> @@ -25,6 +25,7 @@ obj-$(CONFIG_ARCH_OMAP2)		+= pm24xx.o
>  obj-$(CONFIG_ARCH_OMAP24XX)		+= sleep24xx.o
>  obj-$(CONFIG_ARCH_OMAP3)		+= pm34xx.o sleep34xx.o cpuidle34xx.o
>  obj-$(CONFIG_PM_DEBUG)			+= pm-debug.o
> +obj-$(CONFIG_OMAP_WAKE)			+= wake34xx.o
>  endif
>
>  # SmartReflex driver
> diff --git a/arch/arm/mach-omap2/irq.c b/arch/arm/mach-omap2/irq.c
> index 700fc3d..18ac725 100644
> --- a/arch/arm/mach-omap2/irq.c
> +++ b/arch/arm/mach-omap2/irq.c
> @@ -33,9 +33,6 @@
>  #define INTC_MIR_SET0		0x008c
>  #define INTC_PENDING_IRQ0	0x0098
>
> -/* Number of IRQ state bits in each MIR register */
> -#define IRQ_BITS_PER_REG	32
> -
>  /*
>   * OMAP2 has a number of different interrupt controllers, each interrupt
>   * controller is identified as its own "bank". Register definitions are
> @@ -193,6 +190,24 @@ int omap_irq_pending(void)
>  	return 0;
>  }
>
> +void omap_get_pending_irqs(u32 *pending_irqs, unsigned len)
> +{
> +	int i, j = 0;
> +
> +	for (i = 0; i < ARRAY_SIZE(irq_banks); i++) {
> +		struct omap_irq_bank *bank = irq_banks + i;
> +		int irq;
> +
> +		for (irq = 0; irq < bank->nr_irqs && j < len;
> +						irq += IRQ_BITS_PER_REG) {
> +			int offset = irq & (~(IRQ_BITS_PER_REG - 1));
> +
> +			pending_irqs[j++] = intc_bank_read_reg(bank,
> +					(INTC_PENDING_IRQ0 + offset));
> +		}
> +	}
> +}
> +
>  void __init omap_init_irq(void)
>  {
>  	unsigned long nr_of_irqs = 0;
> diff --git a/arch/arm/mach-omap2/prcm-common.h
> b/arch/arm/mach-omap2/prcm-common.h
> index cb1ae84..1f340aa 100644
> --- a/arch/arm/mach-omap2/prcm-common.h
> +++ b/arch/arm/mach-omap2/prcm-common.h
> @@ -273,6 +273,10 @@
>  #define OMAP3430_ST_D2D_SHIFT				3
>  #define OMAP3430_ST_D2D_MASK				(1 << 3)
>
> +/* PM_WKST3_CORE, CM_IDLEST3_CORE shared bits */
> +#define OMAP3430_ST_USBTLL_SHIFT			2
> +#define OMAP3430_ST_USBTLL_MASK				(1 << 2)
> +
>  /* CM_FCLKEN_WKUP, CM_ICLKEN_WKUP, PM_WKEN_WKUP shared bits */
>  #define OMAP3430_EN_GPIO1				(1 << 3)
>  #define OMAP3430_EN_GPIO1_SHIFT				3
> diff --git a/arch/arm/mach-omap2/prm-regbits-34xx.h
> b/arch/arm/mach-omap2/prm-regbits-34xx.h
> index 06fee29..f0a6395 100644
> --- a/arch/arm/mach-omap2/prm-regbits-34xx.h
> +++ b/arch/arm/mach-omap2/prm-regbits-34xx.h
> @@ -332,6 +332,8 @@
>  /* PM_IVA2GRPSEL1_CORE specific bits */
>
>  /* PM_WKST1_CORE specific bits */
> +#define OMAP3430_ST_MMC3_SHIFT				30
> +#define OMAP3430_ST_MMC3_MASK				(1 << 30)
>
>  /* PM_PWSTCTRL_CORE specific bits */
>  #define OMAP3430_MEM2ONSTATE_SHIFT			18
> @@ -432,6 +434,9 @@
>
>  /* PM_PREPWSTST_PER specific bits */
>
> +/* PM_WKST_USBHOST specific bits */
> +#define OMAP3430_ST_USBHOST				(1 << 0)
> +
>  /* RM_RSTST_EMU specific bits */

All these new bit defines should all be 3430ES2_*.

>  /* PM_PWSTST_EMU specific bits */
> diff --git a/arch/arm/mach-omap2/wake34xx.c b/arch/arm/mach-omap2/wake34xx.c
> new file mode 100644
> index 0000000..86aac4f
> --- /dev/null
> +++ b/arch/arm/mach-omap2/wake34xx.c
> @@ -0,0 +1,681 @@
> +/*
> + * wake34xx.c
> + *
> + * Copyright (c) 2009 Samsung Eletronics
> + *
> + * Author: Kim Kyuwon <q1.kim@samsung.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/kernel.h>
> +#include <linux/init.h>
> +#include <linux/platform_device.h>
> +#include <linux/interrupt.h>
> +
> +#include <mach/pm.h>
> +#include <mach/gpio.h>
> +#include <mach/wake.h>
> +
> +#include "prm-regbits-34xx.h"
> +
> +/*
> + * Sometimes, it is necessary to find out "what does wake up my board from
> + * suspend?". Notifying wake-up source feature may be used to blame
> + * unexpected wake-up events which increase power consumption. And user
> + * mode applications can act smartly according to the wake-up event from
> + * Suspend-to-RAM state to minimize power consumption. Note that this
> + * driver can't inform wake-up events from idle state. This driver uses
> + * sysfs interface to give information to user mode applications.
> + */
> +
> +#define DOMAIN_IS_WKUP		(1 << 0)
> +#define DOMAIN_IS_PER		(1 << 1)
> +#define DOMAIN_IS_CORE1		(1 << 2)
> +#define DOMAIN_IS_CORE3		(1 << 3)
> +#define DOMAIN_IS_USBHOST	(1 << 4)
> +
> +#define WAKE_STR_LEN		64
> +#define WAKE_BUF_LEN		32
> +
> +static char wakeup_gpio[WAKE_STR_LEN];
> +
> +struct pm_wakeup_status {
> +	u32	wkup;
> +	u32	per;
> +	u32	core1;
> +	u32	core3;
> +	u32	usbhost;
> +};
> +
> +struct omap_wake {
> +	u32			pending_irqs[INTCPS_NR_MIR_REGS];
> +
> +	struct pm_wakeup_status	pm_wkst;
> +};
> +
> +struct wake_event {
> +	const u32			mask;
> +	const u32			domain;
> +	const char			*name;
> +
> +	/* OMAP chip types that this wakeup status is valid on */
> +	const struct omap_chip_id	omap_chip;
> +};
> +
> +
> +/* Note: Allowed to use Only in the wakeup_source_show() function */
> +static struct omap_wake *g_wake;
> +
> +static struct wake_event omap3_wake_events[] = {
> +	{	/* WKUP */
> +		.mask = OMAP3430_ST_IO_CHAIN,
> +		.domain = DOMAIN_IS_WKUP,
> +		.name = "ST_IO_CHAIN",
> +		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +	}, {
> +		.mask = OMAP3430_ST_IO,
> +		.domain = DOMAIN_IS_WKUP,
> +		.name = "ST_IO",
> +		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +	}, {
> +		.mask = OMAP3430_ST_SR2_MASK,
> +		.domain = DOMAIN_IS_WKUP,
> +		.name = "ST_SR2",
> +		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +	}, {
> +		.mask = OMAP3430_ST_SR1_MASK,
> +		.domain = DOMAIN_IS_WKUP,
> +		.name = "ST_SR1",
> +		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +	}, {
> +		.mask = OMAP3430_ST_GPIO1_MASK,
> +		.domain = DOMAIN_IS_WKUP,
> +		.name = "ST_GPIO1",
> +		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +	}, {
> +		.mask = OMAP3430_ST_GPT12_MASK,
> +		.domain = DOMAIN_IS_WKUP,
> +		.name = "ST_GPT12",
> +		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +	}, {
> +		.mask = OMAP3430_ST_GPT1_MASK,
> +		.domain = DOMAIN_IS_WKUP,
> +		.name = "ST_GPT1",
> +		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +	}, {	/* PER */
> +		.mask = OMAP3430_ST_GPIO6_MASK,
> +		.domain = DOMAIN_IS_PER,
> +		.name = "ST_GPIO6",
> +		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +	}, {
> +		.mask = OMAP3430_ST_GPIO5_MASK,
> +		.domain = DOMAIN_IS_PER,
> +		.name = "ST_GPIO5",
> +		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +	}, {
> +		.mask = OMAP3430_ST_GPIO4_MASK,
> +		.domain = DOMAIN_IS_PER,
> +		.name = "ST_GPIO4",
> +		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +	}, {
> +		.mask = OMAP3430_ST_GPIO3_MASK,
> +		.domain = DOMAIN_IS_PER,
> +		.name = "ST_GPIO3",
> +		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +	}, {
> +		.mask = OMAP3430_ST_GPIO2_MASK,
> +		.domain = DOMAIN_IS_PER,
> +		.name = "ST_GPIO2",
> +		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +	}, {
> +		.mask = OMAP3430_ST_UART3_MASK,
> +		.domain = DOMAIN_IS_PER,
> +		.name = "ST_UART3",
> +		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +	}, {
> +		.mask = OMAP3430_ST_GPT9_MASK,
> +		.domain = DOMAIN_IS_PER,
> +		.name = "ST_GPT9",
> +		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +	}, {
> +		.mask = OMAP3430_ST_GPT8_MASK,
> +		.domain = DOMAIN_IS_PER,
> +		.name = "ST_GPT8",
> +		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +	}, {
> +		.mask = OMAP3430_ST_GPT7_MASK,
> +		.domain = DOMAIN_IS_PER,
> +		.name = "ST_GPT7",
> +		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +	}, {
> +		.mask = OMAP3430_ST_GPT6_MASK,
> +		.domain = DOMAIN_IS_PER,
> +		.name = "ST_GPT6",
> +		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +	}, {
> +		.mask = OMAP3430_ST_GPT5_MASK,
> +		.domain = DOMAIN_IS_PER,
> +		.name = "ST_GPT5",
> +		.omap_chip =  OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +	},  {
> +		.mask = OMAP3430_ST_GPT4_MASK,
> +		.domain = DOMAIN_IS_PER,
> +		.name = "ST_GPT4",
> +		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +	}, {
> +		.mask = OMAP3430_ST_GPT3_MASK,
> +		.domain = DOMAIN_IS_PER,
> +		.name = "ST_GPT3",
> +		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +	}, {
> +		.mask = OMAP3430_ST_GPT2_MASK,
> +		.domain = DOMAIN_IS_PER,
> +		.name = "ST_GPT2",
> +		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +	}, {
> +		.mask = OMAP3430_EN_MCBSP4,
> +		.domain = DOMAIN_IS_PER,
> +		.name = "EN_MCBSP4",
> +		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +	}, {
> +		.mask = OMAP3430_EN_MCBSP3,
> +		.domain = DOMAIN_IS_PER,
> +		.name = "EN_MCBSP3",
> +		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +	}, {
> +		.mask = OMAP3430_EN_MCBSP2,
> +		.domain = DOMAIN_IS_PER,
> +		.name = "EN_MCBSP2",
> +		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +	}, {	/* CORE1 */
> +		.mask = OMAP3430_ST_MMC3_MASK,
> +		.domain = DOMAIN_IS_CORE1,
> +		.name = "ST_MMC3",
> +		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +	}, {
> +		.mask = OMAP3430_ST_MMC2_MASK,
> +		.domain = DOMAIN_IS_CORE1,
> +		.name = "ST_MMC2",
> +		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +	}, {
> +		.mask = OMAP3430_ST_MMC1_MASK,
> +		.domain = DOMAIN_IS_CORE1,
> +		.name = "ST_MMC1",
> +		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +	}, {
> +		.mask = OMAP3430_ST_MCSPI4_MASK,
> +		.domain = DOMAIN_IS_CORE1,
> +		.name = "ST_MCSPI4",
> +		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +	}, {
> +		.mask = OMAP3430_ST_MCSPI3_MASK,
> +		.domain = DOMAIN_IS_CORE1,
> +		.name = "ST_MCSPI3",
> +		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +	}, {
> +		.mask = OMAP3430_ST_MCSPI2_MASK,
> +		.domain = DOMAIN_IS_CORE1,
> +		.name = "ST_MCSPI2",
> +		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +	}, {
> +		.mask = OMAP3430_ST_MCSPI1_MASK,
> +		.domain = DOMAIN_IS_CORE1,
> +		.name = "ST_MCSPI1",
> +		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +	}, {
> +		.mask = OMAP3430_ST_I2C3_MASK,
> +		.domain = DOMAIN_IS_CORE1,
> +		.name = "ST_I2C3",
> +		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +	}, {
> +		.mask = OMAP3430_ST_I2C2_MASK,
> +		.domain = DOMAIN_IS_CORE1,
> +		.name = "ST_I2C2",
> +		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +	}, {
> +		.mask = OMAP3430_ST_I2C1_MASK,
> +		.domain = DOMAIN_IS_CORE1,
> +		.name = "ST_I2C1",
> +		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +	}, {
> +		.mask = OMAP3430_ST_UART1_MASK,
> +		.domain = DOMAIN_IS_CORE1,
> +		.name = "ST_UART1",
> +		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +	}, {
> +		.mask = OMAP3430_ST_GPT11_MASK,
> +		.domain = DOMAIN_IS_CORE1,
> +		.name = "ST_GPT11",
> +		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +	}, {
> +		.mask = OMAP3430_ST_GPT10_MASK,
> +		.domain = DOMAIN_IS_CORE1,
> +		.name = "ST_GPT10",
> +		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +	}, {
> +		.mask = OMAP3430_ST_MCBSP5_MASK,
> +		.domain = DOMAIN_IS_CORE1,
> +		.name = "ST_MCBSP5",
> +		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +	}, {
> +		.mask = OMAP3430_ST_MCBSP1_MASK,
> +		.domain = DOMAIN_IS_CORE1,
> +		.name = "ST_MCBSP1",
> +		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +	}, {
> +		.mask = OMAP3430ES1_ST_FSHOSTUSB_MASK,
> +		.domain = DOMAIN_IS_CORE1,
> +		.name = "ST_FSHOSTUSB",
> +		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430ES1),
> +	}, {
> +		.mask = OMAP3430ES1_ST_HSOTGUSB_MASK,
> +		.domain = DOMAIN_IS_CORE1,
> +		.name = "ST_HSOTGUSB",
> +		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430ES1),
> +	}, {
> +		.mask = OMAP3430_ST_D2D_MASK,
> +		.domain = DOMAIN_IS_CORE1,
> +		.name = "ST_D2D",
> +		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430ES1),
> +	}, {
> +		.mask = OMAP3430ES2_ST_HSOTGUSB_STDBY_MASK,
> +		.domain = DOMAIN_IS_CORE1,
> +		.name = "ST_HSOTGUSB",
> +		.omap_chip = OMAP_CHIP_INIT(CHIP_GE_OMAP3430ES2),
> +	}, {	/* CORE3 */
> +		.mask = OMAP3430_ST_USBTLL_MASK,
> +		.domain = DOMAIN_IS_CORE3,
> +		.name = "ST_USBTLL",
> +		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430),
> +	}, {	/* USBHOST */
> +		.mask = OMAP3430_ST_USBHOST,
> +		.domain = DOMAIN_IS_USBHOST,
> +		.name = "ST_USBHOST",
> +		.omap_chip = OMAP_CHIP_INIT(CHIP_IS_OMAP3430ES1),
> +	},
> +};

This is the list that should disappear and be handled by exending
omapdev.

[...]

Kevin



[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-OMAP3-PM-improve-PRCM-interrupt-handler.patch --]
[-- Type: text/x-diff, Size: 6353 bytes --]

>From 114168fca85ba38421c84a7163ea4ae98b452577 Mon Sep 17 00:00:00 2001
From: Paul Walmsley <paul@pwsan.com>
Date: Mon, 20 Apr 2009 17:49:04 -0600
Subject: [PATCH] OMAP3 PM: improve PRCM interrupt handler

This test patch fixes a few obvious bugs in the OMAP3xxx PRCM
interrupt handler.

Clearing wakeup sources is now only done when the PRM indicates a
wakeup source interrupt.  Since we don't handle any other types of
PRCM interrupts right now, warn if we get any other type of PRCM
interrupt.  Either code needs to be added to the PRCM interrupt
handler to react to these, or these other interrupts should be masked
off at init.

Also, PM_WKST register contents should be ANDed with the contents of
the MPUGRPSEL registers.  Otherwise the MPU PRCM interrupt handler
could wind up clearing wakeup events meant for the IVA PRCM interrupt
handler.  For a production version of this patch, we should not read
MPUGRPSEL from the PRM, since those reads are very slow; rather, we
should use a cached version from struct powerdomain (not yet
implemented)

Signed-off-by: Kevin Hilman <khilman@deeprootsystems.com>
---
 arch/arm/mach-omap2/pm34xx.c |   78 +++++++++++++++++++++++++++++++++--------
 1 files changed, 62 insertions(+), 16 deletions(-)

diff --git a/arch/arm/mach-omap2/pm34xx.c b/arch/arm/mach-omap2/pm34xx.c
index 7474cfa..7e78a3b 100644
--- a/arch/arm/mach-omap2/pm34xx.c
+++ b/arch/arm/mach-omap2/pm34xx.c
@@ -194,14 +194,18 @@ static void omap3_save_secure_ram_context(u32 target_mpu_state)
 	}
 }
 
-/* PRCM Interrupt Handler for wakeups */
-static irqreturn_t prcm_interrupt_handler (int irq, void *dev_id)
+static int _prcm_int_handle_wakeup(void)
 {
-	u32 wkst, irqstatus_mpu;
-	u32 fclk, iclk;
+	u32 wkst, fclk, iclk;
+	int c = 0;
+
+        /* FIXME: MPUGRPSEL reads beow should be done from an SDRAM
+	 *        local copy in the struct powerdomain since PRM reads
+	 *        are very slow. */
 
 	/* WKUP */
 	wkst = prm_read_mod_reg(WKUP_MOD, PM_WKST);
+	wkst &= prm_read_mod_reg(WKUP_MOD, OMAP3430_PM_MPUGRPSEL);
 	if (wkst) {
 		iclk = cm_read_mod_reg(WKUP_MOD, CM_ICLKEN);
 		fclk = cm_read_mod_reg(WKUP_MOD, CM_FCLKEN);
@@ -211,10 +215,12 @@ static irqreturn_t prcm_interrupt_handler (int irq, void *dev_id)
 		while (prm_read_mod_reg(WKUP_MOD, PM_WKST));
 		cm_write_mod_reg(iclk, WKUP_MOD, CM_ICLKEN);
 		cm_write_mod_reg(fclk, WKUP_MOD, CM_FCLKEN);
+		c++;
 	}
 
 	/* CORE */
 	wkst = prm_read_mod_reg(CORE_MOD, PM_WKST1);
+	wkst &= prm_read_mod_reg(CORE_MOD, OMAP3430_PM_MPUGRPSEL1);
 	if (wkst) {
 		iclk = cm_read_mod_reg(CORE_MOD, CM_ICLKEN1);
 		fclk = cm_read_mod_reg(CORE_MOD, CM_FCLKEN1);
@@ -224,21 +230,32 @@ static irqreturn_t prcm_interrupt_handler (int irq, void *dev_id)
 		while (prm_read_mod_reg(CORE_MOD, PM_WKST1));
 		cm_write_mod_reg(iclk, CORE_MOD, CM_ICLKEN1);
 		cm_write_mod_reg(fclk, CORE_MOD, CM_FCLKEN1);
+		c++;
 	}
-	wkst = prm_read_mod_reg(CORE_MOD, OMAP3430ES2_PM_WKST3);
-	if (wkst) {
-		iclk = cm_read_mod_reg(CORE_MOD, CM_ICLKEN3);
-		fclk = cm_read_mod_reg(CORE_MOD, OMAP3430ES2_CM_FCLKEN3);
-		cm_set_mod_reg_bits(wkst, CORE_MOD, CM_ICLKEN3);
-		cm_set_mod_reg_bits(wkst, CORE_MOD, OMAP3430ES2_CM_FCLKEN3);
-		prm_write_mod_reg(wkst, CORE_MOD, OMAP3430ES2_PM_WKST3);
-		while (prm_read_mod_reg(CORE_MOD, OMAP3430ES2_PM_WKST3));
-		cm_write_mod_reg(iclk, CORE_MOD, CM_ICLKEN3);
-		cm_write_mod_reg(fclk, CORE_MOD, OMAP3430ES2_CM_FCLKEN3);
+	if (omap_rev() > OMAP3430_REV_ES1_0) {
+		wkst = prm_read_mod_reg(CORE_MOD, OMAP3430ES2_PM_WKST3);
+		wkst &= prm_read_mod_reg(CORE_MOD, OMAP3430ES2_PM_MPUGRPSEL3);
+		if (wkst) {
+			iclk = cm_read_mod_reg(CORE_MOD, CM_ICLKEN3);
+			fclk = cm_read_mod_reg(CORE_MOD,
+					       OMAP3430ES2_CM_FCLKEN3);
+			cm_set_mod_reg_bits(wkst, CORE_MOD, CM_ICLKEN3);
+			cm_set_mod_reg_bits(wkst, CORE_MOD,
+					    OMAP3430ES2_CM_FCLKEN3);
+			prm_write_mod_reg(wkst, CORE_MOD,
+					  OMAP3430ES2_PM_WKST3);
+			while (prm_read_mod_reg(CORE_MOD,
+						OMAP3430ES2_PM_WKST3));
+			cm_write_mod_reg(iclk, CORE_MOD, CM_ICLKEN3);
+			cm_write_mod_reg(fclk, CORE_MOD,
+					 OMAP3430ES2_CM_FCLKEN3);
+			c++;
+		}
 	}
 
 	/* PER */
 	wkst = prm_read_mod_reg(OMAP3430_PER_MOD, PM_WKST);
+	wkst &= prm_read_mod_reg(OMAP3430_PER_MOD, OMAP3430_PM_MPUGRPSEL);
 	if (wkst) {
 		iclk = cm_read_mod_reg(OMAP3430_PER_MOD, CM_ICLKEN);
 		fclk = cm_read_mod_reg(OMAP3430_PER_MOD, CM_FCLKEN);
@@ -248,11 +265,14 @@ static irqreturn_t prcm_interrupt_handler (int irq, void *dev_id)
 		while (prm_read_mod_reg(OMAP3430_PER_MOD, PM_WKST));
 		cm_write_mod_reg(iclk, OMAP3430_PER_MOD, CM_ICLKEN);
 		cm_write_mod_reg(fclk, OMAP3430_PER_MOD, CM_FCLKEN);
+		c++;
 	}
 
 	if (omap_rev() > OMAP3430_REV_ES1_0) {
 		/* USBHOST */
 		wkst = prm_read_mod_reg(OMAP3430ES2_USBHOST_MOD, PM_WKST);
+		wkst &= prm_read_mod_reg(OMAP3430ES2_USBHOST_MOD,
+					 OMAP3430_PM_MPUGRPSEL);
 		if (wkst) {
 			iclk = cm_read_mod_reg(OMAP3430ES2_USBHOST_MOD,
 					       CM_ICLKEN);
@@ -271,12 +291,38 @@ static irqreturn_t prcm_interrupt_handler (int irq, void *dev_id)
 			cm_write_mod_reg(fclk, OMAP3430ES2_USBHOST_MOD,
 					 CM_FCLKEN);
 		}
+		c++;
 	}
 
+	return c;
+}
+
+/* PRCM Interrupt Handler for wakeups */
+static irqreturn_t prcm_interrupt_handler (int irq, void *dev_id)
+{
+	u32 irqstatus_mpu;
+	int c;
+
 	irqstatus_mpu = prm_read_mod_reg(OCP_MOD,
 					OMAP2_PRM_IRQSTATUS_MPU_OFFSET);
+
+	if (irqstatus_mpu & OMAP3430_WKUP_ST) {
+		c = _prcm_int_handle_wakeup();
+
+		/*
+		 * Is the MPU PRCM interrupt handler racing with the
+		 * IVA2 PRCM interrupt handler ?
+		 */
+		WARN(c == 0, "prcm: WARNING: PRCM indicated MPU wakeup "
+		     "but no wakeup sources are marked\n");
+	} else {
+		/* XXX we need to expand our PRCM interrupt handler */
+		WARN(1, "prcm: WARNING: PRCM interrupt received, but no "
+		     "code to handle it (%08x)\n", irqstatus_mpu);
+	}
+
 	prm_write_mod_reg(irqstatus_mpu, OCP_MOD,
-					OMAP2_PRM_IRQSTATUS_MPU_OFFSET);
+			  OMAP2_PRM_IRQSTATUS_MPU_OFFSET);
 
 	while (prm_read_mod_reg(OCP_MOD, OMAP2_PRM_IRQSTATUS_MPU_OFFSET));
 
@@ -969,7 +1015,7 @@ int __init omap3_pm_init(void)
 	 * supervised mode for powerdomains */
 	prcm_setup_regs();
 
-	ret = request_irq(INT_34XX_PRCM_MPU_IRQ,
+	ret = request_irq(INT_34XX_PRCM_MPU_IRQ,	
 			  (irq_handler_t)prcm_interrupt_handler,
 			  IRQF_DISABLED, "prcm", NULL);
 	if (ret) {
-- 
1.6.2.2


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH] OMAP3: PM: Add the wakeup source driver, v3
  2009-04-21  0:15 ` Kevin Hilman
@ 2009-04-30  7:17   ` Kim Kyuwon
  2009-04-30 14:21     ` Kevin Hilman
  0 siblings, 1 reply; 6+ messages in thread
From: Kim Kyuwon @ 2009-04-30  7:17 UTC (permalink / raw)
  To: Kevin Hilman; +Cc: OMAP, 박경민, Paul Walmsley

Hi Kevin,
Thank you for showing steady interest in this driver.

On Tue, Apr 21, 2009 at 9:15 AM, Kevin Hilman
<khilman@deeprootsystems.com> wrote:
>
> Hi Kyuwon,
>
> While I still like the concept of this driver, I'm still not quite
> happy about how it is implemented for various reasons.  Most of which
> have to do with the fact that this driver does many things that really
> should be the job of other layers.  In particular, the list of
> omap3_wake_events is a bit troubling.  It really should be handled by
> the omapdev layer.  You'll see that you are duplicating the data that
> is handled there.  Rather, we should just extend the omapdev data to
> handle the wakeup events for that device.

If you allow me to insert a new field whose name is "mask" in the
omapdev struct, I think I can extend the omapdev to handle the wakeup
events.

> Also, I still think the WKST register reading should be done in the
> PRCM interrupt handler.  In your previous attempt, you were seeing a
> bunch of non-device wakeup related interrupts.   Can you try again
> with the attached patch (thanks to Paul Walmseley) which should help
> us debug why you were seeing spurious PRCM interrupts.

First of all, sorry for giving you the wrong information in the
previous mail. I found that we actually have configured GPTIMER12 as
the system timer.
And I tried with the attached patch, but I can't see any debug message.
However even now, PRCM interrupt handler is invoked quite often due to
the system timer.

As far as I know, after Peter's [OMAP3: PM: CPUidle: Add new
lower-latency C1 state] patch is applied, the system is in 'wfi' state
in every idle state. So whenever the system timer wake up the system
from idle, I think PRCM interrupt occurs. Do you think still the WKST
register should be read in the PRCM interrupt handler? ;)

> Also, I know we discussed this before, but I think the GPIO wakeup
> source stuff really belongs in a separate patch, and if you think it
> is still useful, and cannot be done by just enabling a GPIO IRQ from
> the board file, I suggest you propose a patch to the generic GPIO
> layer to add this interface.

OK, I will remove this GPIO wakeup feature. But I want to know more
detailed information about wakeup event . So, instead of using the
GPIO wakeup, I'm planning using WAKEUPEVENT bit in CONTROL_PADCONF_x
registers.

>> diff --git a/arch/arm/mach-omap2/prcm-common.h
>> b/arch/arm/mach-omap2/prcm-common.h
>> index cb1ae84..1f340aa 100644
>> --- a/arch/arm/mach-omap2/prcm-common.h
>> +++ b/arch/arm/mach-omap2/prcm-common.h
>> @@ -273,6 +273,10 @@
>>  #define OMAP3430_ST_D2D_SHIFT                                3
>>  #define OMAP3430_ST_D2D_MASK                         (1 << 3)
>>
>> +/* PM_WKST3_CORE, CM_IDLEST3_CORE shared bits */
>> +#define OMAP3430_ST_USBTLL_SHIFT                     2
>> +#define OMAP3430_ST_USBTLL_MASK                              (1 << 2)
>> +
>>  /* CM_FCLKEN_WKUP, CM_ICLKEN_WKUP, PM_WKEN_WKUP shared bits */
>>  #define OMAP3430_EN_GPIO1                            (1 << 3)
>>  #define OMAP3430_EN_GPIO1_SHIFT                              3
>> diff --git a/arch/arm/mach-omap2/prm-regbits-34xx.h
>> b/arch/arm/mach-omap2/prm-regbits-34xx.h
>> index 06fee29..f0a6395 100644
>> --- a/arch/arm/mach-omap2/prm-regbits-34xx.h
>> +++ b/arch/arm/mach-omap2/prm-regbits-34xx.h
>> @@ -332,6 +332,8 @@
>>  /* PM_IVA2GRPSEL1_CORE specific bits */
>>
>>  /* PM_WKST1_CORE specific bits */
>> +#define OMAP3430_ST_MMC3_SHIFT                               30
>> +#define OMAP3430_ST_MMC3_MASK                                (1 << 30)
>>
>>  /* PM_PWSTCTRL_CORE specific bits */
>>  #define OMAP3430_MEM2ONSTATE_SHIFT                   18
>> @@ -432,6 +434,9 @@
>>
>>  /* PM_PREPWSTST_PER specific bits */
>>
>> +/* PM_WKST_USBHOST specific bits */
>> +#define OMAP3430_ST_USBHOST                          (1 << 0)
>> +
>>  /* RM_RSTST_EMU specific bits */
>
> All these new bit defines should all be 3430ES2_*.
OK, I will fix it.

Thanks & Regards,
Kyuwon

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] OMAP3: PM: Add the wakeup source driver, v3
  2009-04-30  7:17   ` Kim Kyuwon
@ 2009-04-30 14:21     ` Kevin Hilman
  2009-05-05  9:08       ` Kim Kyuwon
  0 siblings, 1 reply; 6+ messages in thread
From: Kevin Hilman @ 2009-04-30 14:21 UTC (permalink / raw)
  To: Kim Kyuwon; +Cc: OMAP, 박경민, Paul Walmsley

Kyuwon,

Kim Kyuwon <chammoru@gmail.com> writes:

> Hi Kevin,
> Thank you for showing steady interest in this driver.
>
> On Tue, Apr 21, 2009 at 9:15 AM, Kevin Hilman
> <khilman@deeprootsystems.com> wrote:
>>
>> Hi Kyuwon,
>>
>> While I still like the concept of this driver, I'm still not quite
>> happy about how it is implemented for various reasons.  Most of which
>> have to do with the fact that this driver does many things that really
>> should be the job of other layers.  In particular, the list of
>> omap3_wake_events is a bit troubling.  It really should be handled by
>> the omapdev layer.  You'll see that you are duplicating the data that
>> is handled there.  Rather, we should just extend the omapdev data to
>> handle the wakeup events for that device.
>
> If you allow me to insert a new field whose name is "mask" in the
> omapdev struct, I think I can extend the omapdev to handle the wakeup
> events.

Extending omapdev would be fine.

>> Also, I still think the WKST register reading should be done in the
>> PRCM interrupt handler.  In your previous attempt, you were seeing a
>> bunch of non-device wakeup related interrupts.   Can you try again
>> with the attached patch (thanks to Paul Walmseley) which should help
>> us debug why you were seeing spurious PRCM interrupts.
>
> First of all, sorry for giving you the wrong information in the
> previous mail. I found that we actually have configured GPTIMER12 as
> the system timer.  And I tried with the attached patch, but I can't
> see any debug message.  However even now, PRCM interrupt handler is
> invoked quite often due to the system timer.

That's what I assumed was happening.

> As far as I know, after Peter's [OMAP3: PM: CPUidle: Add new
> lower-latency C1 state] patch is applied, the system is in 'wfi' state
> in every idle state. So whenever the system timer wake up the system
> from idle, I think PRCM interrupt occurs. Do you think still the WKST
> register should be read in the PRCM interrupt handler? ;)

Yes. The WKST registers are already being read in the handler so they
can be properly cleared.  All you are adding is the saving of them.

In addition, you should not enter idle between the time the system
wakes from resume and your resume handler runs so you should be able
to get the correct WKST values.

>> Also, I know we discussed this before, but I think the GPIO wakeup
>> source stuff really belongs in a separate patch, and if you think it
>> is still useful, and cannot be done by just enabling a GPIO IRQ from
>> the board file, I suggest you propose a patch to the generic GPIO
>> layer to add this interface.
>
> OK, I will remove this GPIO wakeup feature. But I want to know more
> detailed information about wakeup event . So, instead of using the
> GPIO wakeup, I'm planning using WAKEUPEVENT bit in CONTROL_PADCONF_x
> registers.

That sounds OK.  The current mux layer is lacking any knowledge of the
wake bits in the PADCONF regs, so I'd be interested in any ideas you
have of adding that support.

Kevin


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH] OMAP3: PM: Add the wakeup source driver, v3
  2009-04-30 14:21     ` Kevin Hilman
@ 2009-05-05  9:08       ` Kim Kyuwon
  0 siblings, 0 replies; 6+ messages in thread
From: Kim Kyuwon @ 2009-05-05  9:08 UTC (permalink / raw)
  To: Kevin Hilman; +Cc: OMAP, 박경민, Paul Walmsley

On Thu, Apr 30, 2009 at 11:21 PM, Kevin Hilman
<khilman@deeprootsystems.com> wrote:
>>> Also, I still think the WKST register reading should be done in the
>>> PRCM interrupt handler.  In your previous attempt, you were seeing a
>>> bunch of non-device wakeup related interrupts.   Can you try again
>>> with the attached patch (thanks to Paul Walmseley) which should help
>>> us debug why you were seeing spurious PRCM interrupts.
>>
>> First of all, sorry for giving you the wrong information in the
>> previous mail. I found that we actually have configured GPTIMER12 as
>> the system timer.  And I tried with the attached patch, but I can't
>> see any debug message.  However even now, PRCM interrupt handler is
>> invoked quite often due to the system timer.
>
> That's what I assumed was happening.
>
>> As far as I know, after Peter's [OMAP3: PM: CPUidle: Add new
>> lower-latency C1 state] patch is applied, the system is in 'wfi' state
>> in every idle state. So whenever the system timer wake up the system
>> from idle, I think PRCM interrupt occurs. Do you think still the WKST
>> register should be read in the PRCM interrupt handler? ;)
>
> Yes. The WKST registers are already being read in the handler so they
> can be properly cleared.  All you are adding is the saving of them.
>
> In addition, you should not enter idle between the time the system
> wakes from resume and your resume handler runs so you should be able
> to get the correct WKST values.

OK, Your idea is more reasonable.
I modified as you said. thanks.

>>> Also, I know we discussed this before, but I think the GPIO wakeup
>>> source stuff really belongs in a separate patch, and if you think it
>>> is still useful, and cannot be done by just enabling a GPIO IRQ from
>>> the board file, I suggest you propose a patch to the generic GPIO
>>> layer to add this interface.
>>
>> OK, I will remove this GPIO wakeup feature. But I want to know more
>> detailed information about wakeup event . So, instead of using the
>> GPIO wakeup, I'm planning using WAKEUPEVENT bit in CONTROL_PADCONF_x
>> registers.
>
> That sounds OK.  The current mux layer is lacking any knowledge of the
> wake bits in the PADCONF regs, so I'd be interested in any ideas you
> have of adding that support.

I added my ideas about CONTROL_PADCONF_x into the new version of wake
source driver.
I will send it soon.
Please review this new version again and feel free to give your opinion.

Regards,
Kyuwon (규원)
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2009-05-05  9:14 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-04-03 10:43 [PATCH] OMAP3: PM: Add the wakeup source driver, v3 Kim Kyuwon
2009-04-14  5:20 ` Kim Kyuwon
2009-04-21  0:15 ` Kevin Hilman
2009-04-30  7:17   ` Kim Kyuwon
2009-04-30 14:21     ` Kevin Hilman
2009-05-05  9:08       ` Kim Kyuwon

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox