From e198a8de14d4bb122b821432fadb28eedd4b4507 Mon Sep 17 00:00:00 2001 From: Deepak Sikri Date: Fri, 18 Nov 2011 15:20:12 +0530 Subject: GPIO/pl061: Add suspend resume capability This patch adds the suspend and resume operations in the driver. The patch ensures the data save and restore for the device registers during the suspend and resume operations respectively. Signed-off-by: Deepak Sikri Signed-off-by: Viresh Kumar Acked-by: Baruch Siach Acked-by: Rafael J. Wysocki Acked-by: Linus Walleij Signed-off-by: Grant Likely --- drivers/gpio/gpio-pl061.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) (limited to 'drivers/gpio/gpio-pl061.c') diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c index 093c90bd3c1d..42a5d5672694 100644 --- a/drivers/gpio/gpio-pl061.c +++ b/drivers/gpio/gpio-pl061.c @@ -23,6 +23,7 @@ #include #include #include +#include #define GPIODIR 0x400 #define GPIOIS 0x404 @@ -35,6 +36,17 @@ #define PL061_GPIO_NR 8 +#ifdef CONFIG_PM +struct pl061_context_save_regs { + u8 gpio_data; + u8 gpio_dir; + u8 gpio_is; + u8 gpio_ibe; + u8 gpio_iev; + u8 gpio_ie; +}; +#endif + struct pl061_gpio { /* We use a list of pl061_gpio structs for each trigger IRQ in the main * interrupts controller of the system. We need this to support systems @@ -54,6 +66,10 @@ struct pl061_gpio { void __iomem *base; unsigned irq_base; struct gpio_chip gc; + +#ifdef CONFIG_PM + struct pl061_context_save_regs csave_regs; +#endif }; static int pl061_direction_input(struct gpio_chip *gc, unsigned offset) @@ -330,6 +346,8 @@ static int pl061_probe(struct amba_device *dev, const struct amba_id *id) irq_set_chip_data(i + chip->irq_base, chip); } + amba_set_drvdata(dev, chip); + return 0; iounmap: @@ -342,6 +360,53 @@ free_mem: return ret; } +#ifdef CONFIG_PM +static int pl061_suspend(struct device *dev) +{ + struct pl061_gpio *chip = dev_get_drvdata(dev); + int offset; + + chip->csave_regs.gpio_data = 0; + chip->csave_regs.gpio_dir = readb(chip->base + GPIODIR); + chip->csave_regs.gpio_is = readb(chip->base + GPIOIS); + chip->csave_regs.gpio_ibe = readb(chip->base + GPIOIBE); + chip->csave_regs.gpio_iev = readb(chip->base + GPIOIEV); + chip->csave_regs.gpio_ie = readb(chip->base + GPIOIE); + + for (offset = 0; offset < PL061_GPIO_NR; offset++) { + if (chip->csave_regs.gpio_dir & (1 << offset)) + chip->csave_regs.gpio_data |= + pl061_get_value(&chip->gc, offset) << offset; + } + + return 0; +} + +static int pl061_resume(struct device *dev) +{ + struct pl061_gpio *chip = dev_get_drvdata(dev); + int offset; + + for (offset = 0; offset < PL061_GPIO_NR; offset++) { + if (chip->csave_regs.gpio_dir & (1 << offset)) + pl061_direction_output(&chip->gc, offset, + chip->csave_regs.gpio_data & + (1 << offset)); + else + pl061_direction_input(&chip->gc, offset); + } + + writeb(chip->csave_regs.gpio_is, chip->base + GPIOIS); + writeb(chip->csave_regs.gpio_ibe, chip->base + GPIOIBE); + writeb(chip->csave_regs.gpio_iev, chip->base + GPIOIEV); + writeb(chip->csave_regs.gpio_ie, chip->base + GPIOIE); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(pl061_dev_pm_ops, pl061_suspend, pl061_resume); +#endif + static struct amba_id pl061_ids[] = { { .id = 0x00041061, @@ -353,6 +418,9 @@ static struct amba_id pl061_ids[] = { static struct amba_driver pl061_gpio_driver = { .drv = { .name = "pl061_gpio", +#ifdef CONFIG_PM + .pm = &pl061_dev_pm_ops, +#endif }, .id_table = pl061_ids, .probe = pl061_probe, -- cgit v1.2.3 From dece904d74800d109f1bb06b55758169b1bcc140 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 9 Dec 2011 14:12:53 -0600 Subject: gpio: pl061: use chained_irq_* functions in irq handler Use chained_irq_enter/exit helper functions instead of direct pointer accesses. This is needed for generic irq chip conversion. Signed-off-by: Rob Herring Acked-by: Grant Likely Cc: Linus Walleij --- drivers/gpio/gpio-pl061.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/gpio/gpio-pl061.c') diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c index 4102f63230fd..0f718f9bbd8c 100644 --- a/drivers/gpio/gpio-pl061.c +++ b/drivers/gpio/gpio-pl061.c @@ -23,6 +23,7 @@ #include #include #include +#include #define GPIODIR 0x400 #define GPIOIS 0x404 @@ -211,8 +212,9 @@ static void pl061_irq_handler(unsigned irq, struct irq_desc *desc) struct list_head *chip_list = irq_get_handler_data(irq); struct list_head *ptr; struct pl061_gpio *chip; + struct irq_chip *irqchip = irq_desc_get_chip(desc); - desc->irq_data.chip->irq_ack(&desc->irq_data); + chained_irq_enter(irqchip, desc); list_for_each(ptr, chip_list) { unsigned long pending; int offset; @@ -227,7 +229,7 @@ static void pl061_irq_handler(unsigned irq, struct irq_desc *desc) for_each_set_bit(offset, &pending, PL061_GPIO_NR) generic_handle_irq(pl061_to_irq(&chip->gc, offset)); } - desc->irq_data.chip->irq_unmask(&desc->irq_data); + chained_irq_exit(irqchip, desc); } static int pl061_probe(struct amba_device *dev, const struct amba_id *id) -- cgit v1.2.3 From f2ab2ba09e081fbce068c0adc205ad3f25a3b626 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 9 Dec 2011 14:11:41 -0600 Subject: gpio: pl061: convert to use 0 for no irq We don't want drivers using NO_IRQ, so remove its use. For now, 0 or -1 means no irq until platforms are converted to use 0. Signed-off-by: Rob Herring Acked-by: Grant Likely Cc: Linus Walleij --- drivers/gpio/gpio-pl061.c | 8 ++++---- include/linux/amba/pl061.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/gpio/gpio-pl061.c') diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c index 0f718f9bbd8c..fe19dec4b117 100644 --- a/drivers/gpio/gpio-pl061.c +++ b/drivers/gpio/gpio-pl061.c @@ -53,7 +53,7 @@ struct pl061_gpio { spinlock_t irq_lock; /* IRQ registers */ void __iomem *base; - unsigned irq_base; + int irq_base; struct gpio_chip gc; }; @@ -119,7 +119,7 @@ static int pl061_to_irq(struct gpio_chip *gc, unsigned offset) { struct pl061_gpio *chip = container_of(gc, struct pl061_gpio, gc); - if (chip->irq_base == NO_IRQ) + if (chip->irq_base <= 0) return -EINVAL; return chip->irq_base + offset; @@ -250,7 +250,7 @@ static int pl061_probe(struct amba_device *dev, const struct amba_id *id) chip->irq_base = pdata->irq_base; } else if (dev->dev.of_node) { chip->gc.base = -1; - chip->irq_base = NO_IRQ; + chip->irq_base = 0; } else { ret = -ENODEV; goto free_mem; @@ -290,7 +290,7 @@ static int pl061_probe(struct amba_device *dev, const struct amba_id *id) * irq_chip support */ - if (chip->irq_base == NO_IRQ) + if (chip->irq_base <= 0) return 0; writeb(0, chip->base + GPIOIE); /* disable irqs */ diff --git a/include/linux/amba/pl061.h b/include/linux/amba/pl061.h index 2412af944f1f..fb83c0453489 100644 --- a/include/linux/amba/pl061.h +++ b/include/linux/amba/pl061.h @@ -7,7 +7,7 @@ struct pl061_platform_data { unsigned gpio_base; /* number of the first IRQ. - * If the IRQ functionality in not desired this must be set to NO_IRQ. + * If the IRQ functionality in not desired this must be set to 0. */ unsigned irq_base; -- cgit v1.2.3 From 3ab52475447641a6facf6ee5450bea24e477b811 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 21 Oct 2011 08:05:53 -0500 Subject: gpio: pl061: convert to use generic irq chip Convert the pl061 irq_chip code to use the generic irq chip code. This has the side effect of using 32-bit accesses rather than 8-bit accesses to interrupt registers. The h/w TRM and testing seem to indicate this is fine. Signed-off-by: Rob Herring Acked-by: Grant Likely Acked-by: Linus Walleij --- drivers/gpio/Kconfig | 1 + drivers/gpio/gpio-pl061.c | 74 +++++++++++++++++------------------------------ 2 files changed, 27 insertions(+), 48 deletions(-) (limited to 'drivers/gpio/gpio-pl061.c') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 8482a23887dc..4d433e2995f0 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -138,6 +138,7 @@ config GPIO_MXS config GPIO_PL061 bool "PrimeCell PL061 GPIO support" depends on ARM_AMBA + select GENERIC_IRQ_CHIP help Say yes here to support the PrimeCell PL061 GPIO device diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c index fe19dec4b117..96ff6b28e9ad 100644 --- a/drivers/gpio/gpio-pl061.c +++ b/drivers/gpio/gpio-pl061.c @@ -50,10 +50,10 @@ struct pl061_gpio { * the IRQ code simpler. */ spinlock_t lock; /* GPIO registers */ - spinlock_t irq_lock; /* IRQ registers */ void __iomem *base; int irq_base; + struct irq_chip_generic *irq_gc; struct gpio_chip gc; }; @@ -125,40 +125,10 @@ static int pl061_to_irq(struct gpio_chip *gc, unsigned offset) return chip->irq_base + offset; } -/* - * PL061 GPIO IRQ - */ -static void pl061_irq_disable(struct irq_data *d) -{ - struct pl061_gpio *chip = irq_data_get_irq_chip_data(d); - int offset = d->irq - chip->irq_base; - unsigned long flags; - u8 gpioie; - - spin_lock_irqsave(&chip->irq_lock, flags); - gpioie = readb(chip->base + GPIOIE); - gpioie &= ~(1 << offset); - writeb(gpioie, chip->base + GPIOIE); - spin_unlock_irqrestore(&chip->irq_lock, flags); -} - -static void pl061_irq_enable(struct irq_data *d) -{ - struct pl061_gpio *chip = irq_data_get_irq_chip_data(d); - int offset = d->irq - chip->irq_base; - unsigned long flags; - u8 gpioie; - - spin_lock_irqsave(&chip->irq_lock, flags); - gpioie = readb(chip->base + GPIOIE); - gpioie |= 1 << offset; - writeb(gpioie, chip->base + GPIOIE); - spin_unlock_irqrestore(&chip->irq_lock, flags); -} - static int pl061_irq_type(struct irq_data *d, unsigned trigger) { - struct pl061_gpio *chip = irq_data_get_irq_chip_data(d); + struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); + struct pl061_gpio *chip = gc->private; int offset = d->irq - chip->irq_base; unsigned long flags; u8 gpiois, gpioibe, gpioiev; @@ -166,7 +136,7 @@ static int pl061_irq_type(struct irq_data *d, unsigned trigger) if (offset < 0 || offset >= PL061_GPIO_NR) return -EINVAL; - spin_lock_irqsave(&chip->irq_lock, flags); + raw_spin_lock_irqsave(&gc->lock, flags); gpioiev = readb(chip->base + GPIOIEV); @@ -195,18 +165,11 @@ static int pl061_irq_type(struct irq_data *d, unsigned trigger) writeb(gpioiev, chip->base + GPIOIEV); - spin_unlock_irqrestore(&chip->irq_lock, flags); + raw_spin_unlock_irqrestore(&gc->lock, flags); return 0; } -static struct irq_chip pl061_irqchip = { - .name = "GPIO", - .irq_enable = pl061_irq_enable, - .irq_disable = pl061_irq_disable, - .irq_set_type = pl061_irq_type, -}; - static void pl061_irq_handler(unsigned irq, struct irq_desc *desc) { struct list_head *chip_list = irq_get_handler_data(irq); @@ -232,6 +195,25 @@ static void pl061_irq_handler(unsigned irq, struct irq_desc *desc) chained_irq_exit(irqchip, desc); } +static void __init pl061_init_gc(struct pl061_gpio *chip, int irq_base) +{ + struct irq_chip_type *ct; + + chip->irq_gc = irq_alloc_generic_chip("gpio-pl061", 1, irq_base, + chip->base, handle_simple_irq); + chip->irq_gc->private = chip; + + ct = chip->irq_gc->chip_types; + ct->chip.irq_mask = irq_gc_mask_clr_bit; + ct->chip.irq_unmask = irq_gc_mask_set_bit; + ct->chip.irq_set_type = pl061_irq_type; + ct->chip.irq_set_wake = irq_gc_set_wake; + ct->regs.mask = GPIOIE; + + irq_setup_generic_chip(chip->irq_gc, IRQ_MSK(PL061_GPIO_NR), + IRQ_GC_INIT_NESTED_LOCK, IRQ_NOREQUEST, 0); +} + static int pl061_probe(struct amba_device *dev, const struct amba_id *id) { struct pl061_platform_data *pdata; @@ -269,7 +251,6 @@ static int pl061_probe(struct amba_device *dev, const struct amba_id *id) } spin_lock_init(&chip->lock); - spin_lock_init(&chip->irq_lock); INIT_LIST_HEAD(&chip->list); chip->gc.direction_input = pl061_direction_input; @@ -293,6 +274,8 @@ static int pl061_probe(struct amba_device *dev, const struct amba_id *id) if (chip->irq_base <= 0) return 0; + pl061_init_gc(chip, chip->irq_base); + writeb(0, chip->base + GPIOIE); /* disable irqs */ irq = dev->irq[0]; if (irq < 0) { @@ -321,11 +304,6 @@ static int pl061_probe(struct amba_device *dev, const struct amba_id *id) else pl061_direction_input(&chip->gc, i); } - - irq_set_chip_and_handler(i + chip->irq_base, &pl061_irqchip, - handle_simple_irq); - set_irq_flags(i+chip->irq_base, IRQF_VALID); - irq_set_chip_data(i + chip->irq_base, chip); } return 0; -- cgit v1.2.3 From 2de0dbc5f6830e7659083d1929f57cb88b16a3b6 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 4 Jan 2012 10:36:07 -0600 Subject: gpio: pl061: remove combined interrupt Drivers should not have a dependency on NR_IRQS. Doing so may break with SPARSE_IRQ enabled. As there are no in kernel users of the pl061 which have multiple instances with their interrupts combined to a single parent interrupt, remove this functionality. If this capability is needed later, it could be supported more cleanly by just using a devicetree property. Signed-off-by: Rob Herring Reviewed-by: Viresh Kumar Cc: Rajeev Kumar Acked-by: Baruch Siach Cc: Linus Walleij Cc: Grant Likely --- drivers/gpio/gpio-pl061.c | 44 ++++++++------------------------------------ 1 file changed, 8 insertions(+), 36 deletions(-) (limited to 'drivers/gpio/gpio-pl061.c') diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c index 96ff6b28e9ad..23065f6b3de1 100644 --- a/drivers/gpio/gpio-pl061.c +++ b/drivers/gpio/gpio-pl061.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -37,13 +36,6 @@ #define PL061_GPIO_NR 8 struct pl061_gpio { - /* We use a list of pl061_gpio structs for each trigger IRQ in the main - * interrupts controller of the system. We need this to support systems - * in which more that one PL061s are connected to the same IRQ. The ISR - * interates through this list to find the source of the interrupt. - */ - struct list_head list; - /* Each of the two spinlocks protects a different set of hardware * regiters and data structurs. This decouples the code of the IRQ from * the GPIO code. This also makes the case of a GPIO routine call from @@ -172,26 +164,20 @@ static int pl061_irq_type(struct irq_data *d, unsigned trigger) static void pl061_irq_handler(unsigned irq, struct irq_desc *desc) { - struct list_head *chip_list = irq_get_handler_data(irq); - struct list_head *ptr; - struct pl061_gpio *chip; + unsigned long pending; + int offset; + struct pl061_gpio *chip = irq_desc_get_handler_data(desc); struct irq_chip *irqchip = irq_desc_get_chip(desc); chained_irq_enter(irqchip, desc); - list_for_each(ptr, chip_list) { - unsigned long pending; - int offset; - - chip = list_entry(ptr, struct pl061_gpio, list); - pending = readb(chip->base + GPIOMIS); - writeb(pending, chip->base + GPIOIC); - - if (pending == 0) - continue; + pending = readb(chip->base + GPIOMIS); + writeb(pending, chip->base + GPIOIC); + if (pending) { for_each_set_bit(offset, &pending, PL061_GPIO_NR) generic_handle_irq(pl061_to_irq(&chip->gc, offset)); } + chained_irq_exit(irqchip, desc); } @@ -218,9 +204,7 @@ static int pl061_probe(struct amba_device *dev, const struct amba_id *id) { struct pl061_platform_data *pdata; struct pl061_gpio *chip; - struct list_head *chip_list; int ret, irq, i; - static DECLARE_BITMAP(init_irq, NR_IRQS); chip = kzalloc(sizeof(*chip), GFP_KERNEL); if (chip == NULL) @@ -251,7 +235,6 @@ static int pl061_probe(struct amba_device *dev, const struct amba_id *id) } spin_lock_init(&chip->lock); - INIT_LIST_HEAD(&chip->list); chip->gc.direction_input = pl061_direction_input; chip->gc.direction_output = pl061_direction_output; @@ -283,18 +266,7 @@ static int pl061_probe(struct amba_device *dev, const struct amba_id *id) goto iounmap; } irq_set_chained_handler(irq, pl061_irq_handler); - if (!test_and_set_bit(irq, init_irq)) { /* list initialized? */ - chip_list = kmalloc(sizeof(*chip_list), GFP_KERNEL); - if (chip_list == NULL) { - clear_bit(irq, init_irq); - ret = -ENOMEM; - goto iounmap; - } - INIT_LIST_HEAD(chip_list); - irq_set_handler_data(irq, chip_list); - } else - chip_list = irq_get_handler_data(irq); - list_add(&chip->list, chip_list); + irq_set_handler_data(irq, chip); for (i = 0; i < PL061_GPIO_NR; i++) { if (pdata) { -- cgit v1.2.3