aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDoug Anderson <dianders@chromium.org>2015-01-26 11:24:03 -0500
committerLinus Walleij <linus.walleij@linaro.org>2015-01-30 04:38:36 -0500
commit5ae0c7ad06f4386d253ff2120ea769f01292f37b (patch)
treebbd75539fef28d39c935a56d3a30478e3daa7da5
parentb6afdbe8e841e20297a38e2af0a053d8eb26c19b (diff)
pinctrl: rockchip: Only mask interrupts; never disable
The Rockchip GPIO interrupt controller totally throws away all status about an interrupt when you "disable" the interrupt. That has unfortunate consequences in the following situation: 1. An edge-triggered interrupt is enabled and should wake the system. 2. System suspend happens: interrupt is disabled and marked for wake. 3. rockchip_irq_suspend() reenables the interrupt so we can wake. 4. Interrupt happens when asleep. 5. rockchip_irq_resume() redisables the interrupt. 6. Disabling the interrupt throws away all status about it. 7. Normal system resume happens and we enable the interrupt again, since we threw away status about the interrupt we don't know it fired while suspended. Even worse: if we need both edges of the interrupt the logic to swap edges never runs. Note: even if we somehow can post the status about wakeup interrupts in rockchip_irq_resume() we would still have a window of losing any edges that came in while interrupts were disabled. If we use mask only then we don't need to worry. The GPIO Interrupt controller keeps track of pending interrupts that are enabled and just masked. There was no real strong reason to support the enable/disable functionality (other than that it seemed right), so let's go back to just supporting mask/unmask but actually map it to the real mask/unmask. This ends up with slightly different (and more correct) behavior than before (f2dd028 pinctrl: rockchip: Fix enable/disable/mask/unmask). Signed-off-by: Doug Anderson <dianders@chromium.org> Reviewed-by: Heiko Stuebner <heiko@sntech.de> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
-rw-r--r--drivers/pinctrl/pinctrl-rockchip.c48
1 files changed, 13 insertions, 35 deletions
diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c
index d144330a4865..dee7d5f06c60 100644
--- a/drivers/pinctrl/pinctrl-rockchip.c
+++ b/drivers/pinctrl/pinctrl-rockchip.c
@@ -89,7 +89,7 @@ struct rockchip_iomux {
89 * @reg_pull: optional separate register for additional pull settings 89 * @reg_pull: optional separate register for additional pull settings
90 * @clk: clock of the gpio bank 90 * @clk: clock of the gpio bank
91 * @irq: interrupt of the gpio bank 91 * @irq: interrupt of the gpio bank
92 * @saved_enables: Saved content of GPIO_INTEN at suspend time. 92 * @saved_masks: Saved content of GPIO_INTEN at suspend time.
93 * @pin_base: first pin number 93 * @pin_base: first pin number
94 * @nr_pins: number of pins in this bank 94 * @nr_pins: number of pins in this bank
95 * @name: name of the bank 95 * @name: name of the bank
@@ -108,7 +108,7 @@ struct rockchip_pin_bank {
108 struct regmap *regmap_pull; 108 struct regmap *regmap_pull;
109 struct clk *clk; 109 struct clk *clk;
110 int irq; 110 int irq;
111 u32 saved_enables; 111 u32 saved_masks;
112 u32 pin_base; 112 u32 pin_base;
113 u8 nr_pins; 113 u8 nr_pins;
114 char *name; 114 char *name;
@@ -1545,8 +1545,8 @@ static void rockchip_irq_suspend(struct irq_data *d)
1545 struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); 1545 struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
1546 struct rockchip_pin_bank *bank = gc->private; 1546 struct rockchip_pin_bank *bank = gc->private;
1547 1547
1548 bank->saved_enables = irq_reg_readl(gc, GPIO_INTEN); 1548 bank->saved_masks = irq_reg_readl(gc, GPIO_INTMASK);
1549 irq_reg_writel(gc, gc->wake_active, GPIO_INTEN); 1549 irq_reg_writel(gc, ~gc->wake_active, GPIO_INTMASK);
1550} 1550}
1551 1551
1552static void rockchip_irq_resume(struct irq_data *d) 1552static void rockchip_irq_resume(struct irq_data *d)
@@ -1554,35 +1554,7 @@ static void rockchip_irq_resume(struct irq_data *d)
1554 struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); 1554 struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
1555 struct rockchip_pin_bank *bank = gc->private; 1555 struct rockchip_pin_bank *bank = gc->private;
1556 1556
1557 irq_reg_writel(gc, bank->saved_enables, GPIO_INTEN); 1557 irq_reg_writel(gc, bank->saved_masks, GPIO_INTMASK);
1558}
1559
1560static void rockchip_irq_disable(struct irq_data *d)
1561{
1562 struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
1563 u32 val;
1564
1565 irq_gc_lock(gc);
1566
1567 val = irq_reg_readl(gc, GPIO_INTEN);
1568 val &= ~d->mask;
1569 irq_reg_writel(gc, val, GPIO_INTEN);
1570
1571 irq_gc_unlock(gc);
1572}
1573
1574static void rockchip_irq_enable(struct irq_data *d)
1575{
1576 struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
1577 u32 val;
1578
1579 irq_gc_lock(gc);
1580
1581 val = irq_reg_readl(gc, GPIO_INTEN);
1582 val |= d->mask;
1583 irq_reg_writel(gc, val, GPIO_INTEN);
1584
1585 irq_gc_unlock(gc);
1586} 1558}
1587 1559
1588static int rockchip_interrupts_register(struct platform_device *pdev, 1560static int rockchip_interrupts_register(struct platform_device *pdev,
@@ -1620,6 +1592,14 @@ static int rockchip_interrupts_register(struct platform_device *pdev,
1620 continue; 1592 continue;
1621 } 1593 }
1622 1594
1595 /*
1596 * Linux assumes that all interrupts start out disabled/masked.
1597 * Our driver only uses the concept of masked and always keeps
1598 * things enabled, so for us that's all masked and all enabled.
1599 */
1600 writel_relaxed(0xffffffff, bank->reg_base + GPIO_INTMASK);
1601 writel_relaxed(0xffffffff, bank->reg_base + GPIO_INTEN);
1602
1623 gc = irq_get_domain_generic_chip(bank->domain, 0); 1603 gc = irq_get_domain_generic_chip(bank->domain, 0);
1624 gc->reg_base = bank->reg_base; 1604 gc->reg_base = bank->reg_base;
1625 gc->private = bank; 1605 gc->private = bank;
@@ -1628,8 +1608,6 @@ static int rockchip_interrupts_register(struct platform_device *pdev,
1628 gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit; 1608 gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit;
1629 gc->chip_types[0].chip.irq_mask = irq_gc_mask_set_bit; 1609 gc->chip_types[0].chip.irq_mask = irq_gc_mask_set_bit;
1630 gc->chip_types[0].chip.irq_unmask = irq_gc_mask_clr_bit; 1610 gc->chip_types[0].chip.irq_unmask = irq_gc_mask_clr_bit;
1631 gc->chip_types[0].chip.irq_enable = rockchip_irq_enable;
1632 gc->chip_types[0].chip.irq_disable = rockchip_irq_disable;
1633 gc->chip_types[0].chip.irq_set_wake = irq_gc_set_wake; 1611 gc->chip_types[0].chip.irq_set_wake = irq_gc_set_wake;
1634 gc->chip_types[0].chip.irq_suspend = rockchip_irq_suspend; 1612 gc->chip_types[0].chip.irq_suspend = rockchip_irq_suspend;
1635 gc->chip_types[0].chip.irq_resume = rockchip_irq_resume; 1613 gc->chip_types[0].chip.irq_resume = rockchip_irq_resume;