--- zzzz-none-000/linux-3.10.107/drivers/gpio/gpio-mvebu.c 2017-06-27 09:49:32.000000000 +0000 +++ scorpion-7490-727/linux-3.10.107/drivers/gpio/gpio-mvebu.c 2021-02-04 17:41:59.000000000 +0000 @@ -44,6 +44,7 @@ #include #include #include +#include /* * GPIO unit register offsets. @@ -58,7 +59,7 @@ #define GPIO_LEVEL_MASK_OFF 0x001c /* The MV78200 has per-CPU registers for edge mask and level mask */ -#define GPIO_EDGE_MASK_MV78200_OFF(cpu) ((cpu) ? 0x30 : 0x18) +#define GPIO_EDGE_MASK_MV78200_OFF(cpu) ((cpu) ? 0x30 : 0x18) #define GPIO_LEVEL_MASK_MV78200_OFF(cpu) ((cpu) ? 0x34 : 0x1C) /* The Armada XP has per-CPU registers for interrupt cause, interrupt @@ -68,11 +69,11 @@ #define GPIO_EDGE_MASK_ARMADAXP_OFF(cpu) (0x10 + (cpu) * 0x4) #define GPIO_LEVEL_MASK_ARMADAXP_OFF(cpu) (0x20 + (cpu) * 0x4) -#define MVEBU_GPIO_SOC_VARIANT_ORION 0x1 -#define MVEBU_GPIO_SOC_VARIANT_MV78200 0x2 +#define MVEBU_GPIO_SOC_VARIANT_ORION 0x1 +#define MVEBU_GPIO_SOC_VARIANT_MV78200 0x2 #define MVEBU_GPIO_SOC_VARIANT_ARMADAXP 0x3 -#define MVEBU_MAX_GPIO_PER_BANK 32 +#define MVEBU_MAX_GPIO_PER_BANK 32 struct mvebu_gpio_chip { struct gpio_chip chip; @@ -81,7 +82,15 @@ void __iomem *percpu_membase; int irqbase; struct irq_domain *domain; - int soc_variant; + int soc_variant; + + /* Used to preserve GPIO registers across suspend/resume */ + u32 out_reg; + u32 io_conf_reg; + u32 blink_en_reg; + u32 in_pol_reg; + u32 edge_mask_regs[4]; + u32 level_mask_regs[4]; }; /* @@ -98,7 +107,8 @@ return mvchip->membase + GPIO_BLINK_EN_OFF; } -static inline void __iomem *mvebu_gpioreg_io_conf(struct mvebu_gpio_chip *mvchip) +static inline void __iomem * +mvebu_gpioreg_io_conf(struct mvebu_gpio_chip *mvchip) { return mvchip->membase + GPIO_IO_CONF_OFF; } @@ -108,12 +118,14 @@ return mvchip->membase + GPIO_IN_POL_OFF; } -static inline void __iomem *mvebu_gpioreg_data_in(struct mvebu_gpio_chip *mvchip) +static inline void __iomem * +mvebu_gpioreg_data_in(struct mvebu_gpio_chip *mvchip) { return mvchip->membase + GPIO_DATA_IN_OFF; } -static inline void __iomem *mvebu_gpioreg_edge_cause(struct mvebu_gpio_chip *mvchip) +static inline void __iomem * +mvebu_gpioreg_edge_cause(struct mvebu_gpio_chip *mvchip) { int cpu; @@ -123,13 +135,15 @@ return mvchip->membase + GPIO_EDGE_CAUSE_OFF; case MVEBU_GPIO_SOC_VARIANT_ARMADAXP: cpu = smp_processor_id(); - return mvchip->percpu_membase + GPIO_EDGE_CAUSE_ARMADAXP_OFF(cpu); + return mvchip->percpu_membase + + GPIO_EDGE_CAUSE_ARMADAXP_OFF(cpu); default: BUG(); } } -static inline void __iomem *mvebu_gpioreg_edge_mask(struct mvebu_gpio_chip *mvchip) +static inline void __iomem * +mvebu_gpioreg_edge_mask(struct mvebu_gpio_chip *mvchip) { int cpu; @@ -141,7 +155,8 @@ return mvchip->membase + GPIO_EDGE_MASK_MV78200_OFF(cpu); case MVEBU_GPIO_SOC_VARIANT_ARMADAXP: cpu = smp_processor_id(); - return mvchip->percpu_membase + GPIO_EDGE_MASK_ARMADAXP_OFF(cpu); + return mvchip->percpu_membase + + GPIO_EDGE_MASK_ARMADAXP_OFF(cpu); default: BUG(); } @@ -159,7 +174,8 @@ return mvchip->membase + GPIO_LEVEL_MASK_MV78200_OFF(cpu); case MVEBU_GPIO_SOC_VARIANT_ARMADAXP: cpu = smp_processor_id(); - return mvchip->percpu_membase + GPIO_LEVEL_MASK_ARMADAXP_OFF(cpu); + return mvchip->percpu_membase + + GPIO_LEVEL_MASK_ARMADAXP_OFF(cpu); default: BUG(); } @@ -169,16 +185,6 @@ * Functions implementing the gpio_chip methods */ -static int mvebu_gpio_request(struct gpio_chip *chip, unsigned pin) -{ - return pinctrl_request_gpio(chip->base + pin); -} - -static void mvebu_gpio_free(struct gpio_chip *chip, unsigned pin) -{ - pinctrl_free_gpio(chip->base + pin); -} - static void mvebu_gpio_set(struct gpio_chip *chip, unsigned pin, int value) { struct mvebu_gpio_chip *mvchip = @@ -304,11 +310,13 @@ { struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); struct mvebu_gpio_chip *mvchip = gc->private; + struct irq_chip_type *ct = irq_data_get_chip_type(d); u32 mask = 1 << (d->irq - gc->irq_base); irq_gc_lock(gc); - gc->mask_cache &= ~mask; - writel_relaxed(gc->mask_cache, mvebu_gpioreg_edge_mask(mvchip)); + ct->mask_cache_priv &= ~mask; + + writel_relaxed(ct->mask_cache_priv, mvebu_gpioreg_edge_mask(mvchip)); irq_gc_unlock(gc); } @@ -316,11 +324,13 @@ { struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); struct mvebu_gpio_chip *mvchip = gc->private; + struct irq_chip_type *ct = irq_data_get_chip_type(d); + u32 mask = 1 << (d->irq - gc->irq_base); irq_gc_lock(gc); - gc->mask_cache |= mask; - writel_relaxed(gc->mask_cache, mvebu_gpioreg_edge_mask(mvchip)); + ct->mask_cache_priv |= mask; + writel_relaxed(ct->mask_cache_priv, mvebu_gpioreg_edge_mask(mvchip)); irq_gc_unlock(gc); } @@ -328,11 +338,13 @@ { struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); struct mvebu_gpio_chip *mvchip = gc->private; + struct irq_chip_type *ct = irq_data_get_chip_type(d); + u32 mask = 1 << (d->irq - gc->irq_base); irq_gc_lock(gc); - gc->mask_cache &= ~mask; - writel_relaxed(gc->mask_cache, mvebu_gpioreg_level_mask(mvchip)); + ct->mask_cache_priv &= ~mask; + writel_relaxed(ct->mask_cache_priv, mvebu_gpioreg_level_mask(mvchip)); irq_gc_unlock(gc); } @@ -340,11 +352,13 @@ { struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); struct mvebu_gpio_chip *mvchip = gc->private; + struct irq_chip_type *ct = irq_data_get_chip_type(d); + u32 mask = 1 << (d->irq - gc->irq_base); irq_gc_lock(gc); - gc->mask_cache |= mask; - writel_relaxed(gc->mask_cache, mvebu_gpioreg_level_mask(mvchip)); + ct->mask_cache_priv |= mask; + writel_relaxed(ct->mask_cache_priv, mvebu_gpioreg_level_mask(mvchip)); irq_gc_unlock(gc); } @@ -355,22 +369,22 @@ * value of the line or the opposite value. * * Level IRQ handlers: DATA_IN is used directly as cause register. - * Interrupt are masked by LEVEL_MASK registers. + * Interrupt are masked by LEVEL_MASK registers. * Edge IRQ handlers: Change in DATA_IN are latched in EDGE_CAUSE. - * Interrupt are masked by EDGE_MASK registers. + * Interrupt are masked by EDGE_MASK registers. * Both-edge handlers: Similar to regular Edge handlers, but also swaps - * the polarity to catch the next line transaction. - * This is a race condition that might not perfectly - * work on some use cases. + * the polarity to catch the next line transaction. + * This is a race condition that might not perfectly + * work on some use cases. * * Every eight GPIO lines are grouped (OR'ed) before going up to main * cause register. * - * EDGE cause mask - * data-in /--------| |-----| |----\ - * -----| |----- ---- to main cause reg - * X \----------------| |----/ - * polarity LEVEL mask + * EDGE cause mask + * data-in /--------| |-----| |----\ + * -----| |----- ---- to main cause reg + * X \----------------| |----/ + * polarity LEVEL mask * ****************************************************************************/ @@ -385,9 +399,8 @@ pin = d->hwirq; u = readl_relaxed(mvebu_gpioreg_io_conf(mvchip)) & (1 << pin); - if (!u) { + if (!u) return -EINVAL; - } type &= IRQ_TYPE_SENSE_MASK; if (type == IRQ_TYPE_NONE) @@ -435,15 +448,18 @@ return 0; } -static void mvebu_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) +static void mvebu_gpio_irq_handler(struct irq_desc *desc) { - struct mvebu_gpio_chip *mvchip = irq_get_handler_data(irq); + struct mvebu_gpio_chip *mvchip = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); u32 cause, type; int i; if (mvchip == NULL) return; + chained_irq_enter(chip, desc); + cause = readl_relaxed(mvebu_gpioreg_data_in(mvchip)) & readl_relaxed(mvebu_gpioreg_level_mask(mvchip)); cause |= readl_relaxed(mvebu_gpioreg_edge_cause(mvchip)) & @@ -457,7 +473,7 @@ if (!(cause & (1 << i))) continue; - type = irqd_get_trigger_type(irq_get_irq_data(irq)); + type = irq_get_trigger_type(irq); if ((type & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH) { /* Swap polarity (race with GPIO line) */ u32 polarity; @@ -466,8 +482,11 @@ polarity ^= 1 << i; writel_relaxed(polarity, mvebu_gpioreg_in_pol(mvchip)); } + generic_handle_irq(irq); } + + chained_irq_exit(chip, desc); } #ifdef CONFIG_DEBUG_FS @@ -514,13 +533,13 @@ (data_in ^ in_pol) & msk ? "hi" : "lo", in_pol & msk ? "lo" : "hi"); if (!((edg_msk | lvl_msk) & msk)) { - seq_printf(s, " disabled\n"); + seq_puts(s, " disabled\n"); continue; } if (edg_msk & msk) - seq_printf(s, " edge "); + seq_puts(s, " edge "); if (lvl_msk & msk) - seq_printf(s, " level"); + seq_puts(s, " level"); seq_printf(s, " (%s)\n", cause & msk ? "pending" : "clear "); } } @@ -528,18 +547,18 @@ #define mvebu_gpio_dbg_show NULL #endif -static struct of_device_id mvebu_gpio_of_match[] = { +static const struct of_device_id mvebu_gpio_of_match[] = { { .compatible = "marvell,orion-gpio", - .data = (void *) MVEBU_GPIO_SOC_VARIANT_ORION, + .data = (void *) MVEBU_GPIO_SOC_VARIANT_ORION, }, { .compatible = "marvell,mv78200-gpio", - .data = (void *) MVEBU_GPIO_SOC_VARIANT_MV78200, + .data = (void *) MVEBU_GPIO_SOC_VARIANT_MV78200, }, { .compatible = "marvell,armadaxp-gpio", - .data = (void *) MVEBU_GPIO_SOC_VARIANT_ARMADAXP, + .data = (void *) MVEBU_GPIO_SOC_VARIANT_ARMADAXP, }, { /* sentinel */ @@ -547,6 +566,93 @@ }; MODULE_DEVICE_TABLE(of, mvebu_gpio_of_match); +static int mvebu_gpio_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct mvebu_gpio_chip *mvchip = platform_get_drvdata(pdev); + int i; + + mvchip->out_reg = readl(mvebu_gpioreg_out(mvchip)); + mvchip->io_conf_reg = readl(mvebu_gpioreg_io_conf(mvchip)); + mvchip->blink_en_reg = readl(mvebu_gpioreg_blink(mvchip)); + mvchip->in_pol_reg = readl(mvebu_gpioreg_in_pol(mvchip)); + + switch (mvchip->soc_variant) { + case MVEBU_GPIO_SOC_VARIANT_ORION: + mvchip->edge_mask_regs[0] = + readl(mvchip->membase + GPIO_EDGE_MASK_OFF); + mvchip->level_mask_regs[0] = + readl(mvchip->membase + GPIO_LEVEL_MASK_OFF); + break; + case MVEBU_GPIO_SOC_VARIANT_MV78200: + for (i = 0; i < 2; i++) { + mvchip->edge_mask_regs[i] = + readl(mvchip->membase + + GPIO_EDGE_MASK_MV78200_OFF(i)); + mvchip->level_mask_regs[i] = + readl(mvchip->membase + + GPIO_LEVEL_MASK_MV78200_OFF(i)); + } + break; + case MVEBU_GPIO_SOC_VARIANT_ARMADAXP: + for (i = 0; i < 4; i++) { + mvchip->edge_mask_regs[i] = + readl(mvchip->membase + + GPIO_EDGE_MASK_ARMADAXP_OFF(i)); + mvchip->level_mask_regs[i] = + readl(mvchip->membase + + GPIO_LEVEL_MASK_ARMADAXP_OFF(i)); + } + break; + default: + BUG(); + } + + return 0; +} + +static int mvebu_gpio_resume(struct platform_device *pdev) +{ + struct mvebu_gpio_chip *mvchip = platform_get_drvdata(pdev); + int i; + + writel(mvchip->out_reg, mvebu_gpioreg_out(mvchip)); + writel(mvchip->io_conf_reg, mvebu_gpioreg_io_conf(mvchip)); + writel(mvchip->blink_en_reg, mvebu_gpioreg_blink(mvchip)); + writel(mvchip->in_pol_reg, mvebu_gpioreg_in_pol(mvchip)); + + switch (mvchip->soc_variant) { + case MVEBU_GPIO_SOC_VARIANT_ORION: + writel(mvchip->edge_mask_regs[0], + mvchip->membase + GPIO_EDGE_MASK_OFF); + writel(mvchip->level_mask_regs[0], + mvchip->membase + GPIO_LEVEL_MASK_OFF); + break; + case MVEBU_GPIO_SOC_VARIANT_MV78200: + for (i = 0; i < 2; i++) { + writel(mvchip->edge_mask_regs[i], + mvchip->membase + GPIO_EDGE_MASK_MV78200_OFF(i)); + writel(mvchip->level_mask_regs[i], + mvchip->membase + + GPIO_LEVEL_MASK_MV78200_OFF(i)); + } + break; + case MVEBU_GPIO_SOC_VARIANT_ARMADAXP: + for (i = 0; i < 4; i++) { + writel(mvchip->edge_mask_regs[i], + mvchip->membase + + GPIO_EDGE_MASK_ARMADAXP_OFF(i)); + writel(mvchip->level_mask_regs[i], + mvchip->membase + + GPIO_LEVEL_MASK_ARMADAXP_OFF(i)); + } + break; + default: + BUG(); + } + + return 0; +} + static int mvebu_gpio_probe(struct platform_device *pdev) { struct mvebu_gpio_chip *mvchip; @@ -559,6 +665,7 @@ unsigned int ngpios; int soc_variant; int i, cpu, id; + int err; match = of_match_device(mvebu_gpio_of_match, &pdev->dev); if (match) @@ -566,17 +673,12 @@ else soc_variant = MVEBU_GPIO_SOC_VARIANT_ORION; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "Cannot get memory resource\n"); - return -ENODEV; - } - - mvchip = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_gpio_chip), GFP_KERNEL); - if (!mvchip) { - dev_err(&pdev->dev, "Cannot allocate memory\n"); + mvchip = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_gpio_chip), + GFP_KERNEL); + if (!mvchip) return -ENOMEM; - } + + platform_set_drvdata(pdev, mvchip); if (of_property_read_u32(pdev->dev.of_node, "ngpios", &ngpios)) { dev_err(&pdev->dev, "Missing ngpios OF property\n"); @@ -597,8 +699,8 @@ mvchip->soc_variant = soc_variant; mvchip->chip.label = dev_name(&pdev->dev); mvchip->chip.dev = &pdev->dev; - mvchip->chip.request = mvebu_gpio_request; - mvchip->chip.free = mvebu_gpio_free; + mvchip->chip.request = gpiochip_generic_request; + mvchip->chip.free = gpiochip_generic_free; mvchip->chip.direction_input = mvebu_gpio_direction_input; mvchip->chip.get = mvebu_gpio_get; mvchip->chip.direction_output = mvebu_gpio_direction_output; @@ -606,11 +708,12 @@ mvchip->chip.to_irq = mvebu_gpio_to_irq; mvchip->chip.base = id * MVEBU_MAX_GPIO_PER_BANK; mvchip->chip.ngpio = ngpios; - mvchip->chip.can_sleep = 0; + mvchip->chip.can_sleep = false; mvchip->chip.of_node = np; mvchip->chip.dbg_show = mvebu_gpio_dbg_show; spin_lock_init(&mvchip->lock); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); mvchip->membase = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(mvchip->membase)) return PTR_ERR(mvchip->membase); @@ -670,25 +773,27 @@ * interrupt handlers, with each handler dealing with 8 GPIO * pins. */ for (i = 0; i < 4; i++) { - int irq; - irq = platform_get_irq(pdev, i); + int irq = platform_get_irq(pdev, i); + if (irq < 0) continue; - irq_set_handler_data(irq, mvchip); - irq_set_chained_handler(irq, mvebu_gpio_irq_handler); + irq_set_chained_handler_and_data(irq, mvebu_gpio_irq_handler, + mvchip); } mvchip->irqbase = irq_alloc_descs(-1, 0, ngpios, -1); if (mvchip->irqbase < 0) { dev_err(&pdev->dev, "no irqs\n"); - return -ENOMEM; + err = mvchip->irqbase; + goto err_gpiochip_add; } gc = irq_alloc_generic_chip("mvebu_gpio_irq", 2, mvchip->irqbase, mvchip->membase, handle_level_irq); if (!gc) { dev_err(&pdev->dev, "Cannot allocate generic irq_chip\n"); - return -ENOMEM; + err = -ENOMEM; + goto err_gpiochip_add; } gc->private = mvchip; @@ -719,26 +824,30 @@ if (!mvchip->domain) { dev_err(&pdev->dev, "couldn't allocate irq domain %s (DT).\n", mvchip->chip.label); - irq_remove_generic_chip(gc, IRQ_MSK(ngpios), IRQ_NOREQUEST, - IRQ_LEVEL | IRQ_NOPROBE); - kfree(gc); - return -ENODEV; + err = -ENODEV; + goto err_generic_chip; } return 0; + +err_generic_chip: + irq_remove_generic_chip(gc, IRQ_MSK(ngpios), IRQ_NOREQUEST, + IRQ_LEVEL | IRQ_NOPROBE); + kfree(gc); + +err_gpiochip_add: + gpiochip_remove(&mvchip->chip); + + return err; } static struct platform_driver mvebu_gpio_driver = { .driver = { - .name = "mvebu-gpio", - .owner = THIS_MODULE, + .name = "mvebu-gpio", .of_match_table = mvebu_gpio_of_match, }, .probe = mvebu_gpio_probe, + .suspend = mvebu_gpio_suspend, + .resume = mvebu_gpio_resume, }; - -static int __init mvebu_gpio_init(void) -{ - return platform_driver_register(&mvebu_gpio_driver); -} -postcore_initcall(mvebu_gpio_init); +module_platform_driver(mvebu_gpio_driver);