diff options
author | Nancy Chen <Nancy.Chen@freescale.com> | 2009-09-17 15:39:45 -0500 |
---|---|---|
committer | Nancy Chen <Nancy.Chen@freescale.com> | 2009-09-17 22:22:51 -0500 |
commit | 3b2c44e41f3e45e7b158372dac82a785739e5dca (patch) | |
tree | 8e7a774e65bdba25c47fa2f295e330e002093086 | |
parent | 9270e1b862ba4f8cbc253c5ea5c9c954b029d578 (diff) |
ENGR00116579 Add function to set the GPIO interrupt as wakeup resource
Add function to set the GPIO interrupt as wakeup resource.
Signed-off-by: Nancy Chen <Nancy.Chen@freescale.com>
-rw-r--r-- | arch/arm/plat-mxc/gpio.c | 131 | ||||
-rw-r--r-- | arch/arm/plat-mxc/include/mach/gpio.h | 2 |
2 files changed, 132 insertions, 1 deletions
diff --git a/arch/arm/plat-mxc/gpio.c b/arch/arm/plat-mxc/gpio.c index bf8bcf476e99..4c224300759d 100644 --- a/arch/arm/plat-mxc/gpio.c +++ b/arch/arm/plat-mxc/gpio.c @@ -20,9 +20,11 @@ */ #include <linux/init.h> +#include <linux/interrupt.h> #include <linux/io.h> #include <linux/irq.h> #include <linux/gpio.h> +#include <linux/sysdev.h> #include <mach/gpio.h> #include <mach/hardware.h> #include <asm-generic/bug.h> @@ -205,11 +207,44 @@ static void mx2_gpio_irq_handler(u32 irq, struct irq_desc *desc) } #endif +/* + * Set interrupt number "irq" in the GPIO as a wake-up source. + * While system is running all registered GPIO interrupts need to have + * wake-up enabled. When system is suspended, only selected GPIO interrupts + * need to have wake-up enabled. + * @param irq interrupt source number + * @param enable enable as wake-up if equal to non-zero + * @return This function returns 0 on success. + */ +static int gpio_set_wake_irq(u32 irq, u32 enable) +{ + u32 gpio = irq_to_gpio(irq); + u32 gpio_idx = gpio & 0x1F; + struct mxc_gpio_port *port = &mxc_gpio_ports[gpio / 32]; + + if (enable) { + port->suspend_wakeup |= (1 << gpio_idx); + if (port->irq_high && (gpio_idx >= 16)) + enable_irq_wake(port->irq_high); + else + enable_irq_wake(port->irq); + } else { + port->suspend_wakeup &= ~(1 << gpio_idx); + if (port->irq_high && (gpio_idx >= 16)) + disable_irq_wake(port->irq_high); + else + disable_irq_wake(port->irq); + } + + return 0; +} + static struct irq_chip gpio_irq_chip = { .ack = gpio_ack_irq, .mask = gpio_mask_irq, .unmask = gpio_unmask_irq, .set_type = gpio_set_irq_type, + .set_wake = gpio_set_wake_irq, }; static void _set_gpio_direction(struct gpio_chip *chip, unsigned offset, @@ -260,9 +295,98 @@ static int mxc_gpio_direction_output(struct gpio_chip *chip, return 0; } +#ifdef CONFIG_PM +/*! + * This function puts the GPIO in low-power mode/state. + * All the interrupts that are enabled are first saved. + * Only those interrupts which registers as a wake source by calling + * enable_irq_wake are enabled. All other interrupts are disabled. + * + * @param dev the system device structure used to give information + * on GPIO to suspend + * @param mesg the power state the device is entering + * + * @return The function always returns 0. + */ +static int mxc_gpio_suspend(struct sys_device *dev, pm_message_t mesg) +{ + int i; + struct mxc_gpio_port *port = mxc_gpio_ports; + + for (i = 0; i < GPIO_PORT_NUM; i++) { + void __iomem *isr_reg; + void __iomem *imr_reg; + + isr_reg = port[i].base + GPIO_ISR; + imr_reg = port[i].base + GPIO_IMR; + + if (__raw_readl(isr_reg) & port[i].suspend_wakeup) + return -EPERM; + + port[i].saved_wakeup = __raw_readl(imr_reg); + __raw_writel(port[i].suspend_wakeup, imr_reg); + } + + return 0; +} + +/*! + * This function brings the GPIO back from low-power state. + * All the interrupts enabled before suspension are re-enabled from + * the saved information. + * + * @param dev the system device structure used to give information + * on GPIO to resume + * + * @return The function always returns 0. + */ +static int mxc_gpio_resume(struct sys_device *dev) +{ + int i; + struct mxc_gpio_port *port = mxc_gpio_ports; + + for (i = 0; i < GPIO_PORT_NUM; i++) { + void __iomem *isr_reg; + void __iomem *imr_reg; + + isr_reg = port[i].base + GPIO_ISR; + imr_reg = port[i].base + GPIO_IMR; + + __raw_writel(port[i].saved_wakeup, imr_reg); + } + + return 0; +} +#else +#define mxc_gpio_suspend NULL +#define mxc_gpio_resume NULL +#endif /* CONFIG_PM */ + +/*! + * This structure contains pointers to the power management callback functions. + */ +static struct sysdev_class mxc_gpio_sysclass = { + .name = "mxc_gpio", + .suspend = mxc_gpio_suspend, + .resume = mxc_gpio_resume, +}; + +/*! + * This structure represents GPIO as a system device. + * System devices follow a slightly different driver model. + * They don't need to do dynammic driver binding, can't be probed, + * and don't reside on any type of peripheral bus. + * So, it is represented and treated a little differently. + */ +static struct sys_device mxc_gpio_device = { + .id = 0, + .cls = &mxc_gpio_sysclass, +}; + int __init mxc_gpio_init(struct mxc_gpio_port *port, int cnt) { int i, j; + int ret = 0; /* save for local usage */ mxc_gpio_ports = port; @@ -307,5 +431,10 @@ int __init mxc_gpio_init(struct mxc_gpio_port *port, int cnt) set_irq_chained_handler(port[0].irq, mx2_gpio_irq_handler); set_irq_data(port[0].irq, port); #endif - return 0; + + ret = sysdev_class_register(&mxc_gpio_sysclass); + if (ret == 0) + ret = sysdev_register(&mxc_gpio_device); + + return ret; } diff --git a/arch/arm/plat-mxc/include/mach/gpio.h b/arch/arm/plat-mxc/include/mach/gpio.h index 3b00730b1964..a2d90c5177ab 100644 --- a/arch/arm/plat-mxc/include/mach/gpio.h +++ b/arch/arm/plat-mxc/include/mach/gpio.h @@ -37,6 +37,8 @@ struct mxc_gpio_port { int virtual_irq_start; struct gpio_chip chip; u32 both_edges; + u32 suspend_wakeup; + u32 saved_wakeup; }; int mxc_gpio_init(struct mxc_gpio_port*, int); |