| From e986f0e602f19ecb7880b04dd1db415ed9bca3f6 Mon Sep 17 00:00:00 2001 |
| From: =?UTF-8?q?=C5=81ukasz=20Bartosik?= <lb@semihalf.com> |
| Date: Mon, 24 Jan 2022 13:55:29 +0100 |
| Subject: pinctrl: intel: fix unexpected interrupt |
| MIME-Version: 1.0 |
| Content-Type: text/plain; charset=UTF-8 |
| Content-Transfer-Encoding: 8bit |
| |
| From: Łukasz Bartosik <lb@semihalf.com> |
| |
| commit e986f0e602f19ecb7880b04dd1db415ed9bca3f6 upstream. |
| |
| ASUS Chromebook C223 with Celeron N3350 crashes sometimes during |
| cold booot. Inspection of the kernel log showed that it gets into |
| an inifite loop logging the following message: |
| |
| ->handle_irq(): 000000009cdb51e8, handle_bad_irq+0x0/0x251 |
| ->irq_data.chip(): 000000005ec212a7, 0xffffa043009d8e7 |
| ->action(): 00000 |
| IRQ_NOPROBE set |
| unexpected IRQ trap at vector 7c |
| |
| The issue happens during cold boot but only if cold boot happens |
| at most several dozen seconds after Chromebook is powered off. For |
| longer intervals between power off and power on (cold boot) the issue |
| does not reproduce. The unexpected interrupt is sourced from INT3452 |
| GPIO pin which is used for SD card detect. Investigation relevealed |
| that when the interval between power off and power on (cold boot) |
| is less than several dozen seconds then values of INT3452 GPIO interrupt |
| enable and interrupt pending registers survive power off and power |
| on sequence and interrupt for SD card detect pin is enabled and pending |
| during probe of SD controller which causes the unexpected IRQ message. |
| "Intel Pentium and Celeron Processor N- and J- Series" volume 3 doc |
| mentions that GPIO interrupt enable and status registers default |
| value is 0x0. |
| The fix clears INT3452 GPIO interrupt enabled and interrupt pending |
| registers in its probe function. |
| |
| Fixes: 7981c0015af2 ("pinctrl: intel: Add Intel Sunrisepoint pin controller and GPIO support") |
| Signed-off-by: Łukasz Bartosik <lb@semihalf.com> |
| Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| drivers/pinctrl/intel/pinctrl-intel.c | 54 +++++++++++++++++++++------------- |
| 1 file changed, 34 insertions(+), 20 deletions(-) |
| |
| --- a/drivers/pinctrl/intel/pinctrl-intel.c |
| +++ b/drivers/pinctrl/intel/pinctrl-intel.c |
| @@ -1201,6 +1201,39 @@ static irqreturn_t intel_gpio_irq(int ir |
| return IRQ_RETVAL(ret); |
| } |
| |
| +static void intel_gpio_irq_init(struct intel_pinctrl *pctrl) |
| +{ |
| + int i; |
| + |
| + for (i = 0; i < pctrl->ncommunities; i++) { |
| + const struct intel_community *community; |
| + void __iomem *base; |
| + unsigned int gpp; |
| + |
| + community = &pctrl->communities[i]; |
| + base = community->regs; |
| + |
| + for (gpp = 0; gpp < community->ngpps; gpp++) { |
| + /* Mask and clear all interrupts */ |
| + writel(0, base + community->ie_offset + gpp * 4); |
| + writel(0xffff, base + community->is_offset + gpp * 4); |
| + } |
| + } |
| +} |
| + |
| +static int intel_gpio_irq_init_hw(struct gpio_chip *gc) |
| +{ |
| + struct intel_pinctrl *pctrl = gpiochip_get_data(gc); |
| + |
| + /* |
| + * Make sure the interrupt lines are in a proper state before |
| + * further configuration. |
| + */ |
| + intel_gpio_irq_init(pctrl); |
| + |
| + return 0; |
| +} |
| + |
| static int intel_gpio_add_community_ranges(struct intel_pinctrl *pctrl, |
| const struct intel_community *community) |
| { |
| @@ -1305,6 +1338,7 @@ static int intel_gpio_probe(struct intel |
| girq->num_parents = 0; |
| girq->default_type = IRQ_TYPE_NONE; |
| girq->handler = handle_bad_irq; |
| + girq->init_hw = intel_gpio_irq_init_hw; |
| |
| ret = devm_gpiochip_add_data(pctrl->dev, &pctrl->chip, pctrl); |
| if (ret) { |
| @@ -1634,26 +1668,6 @@ int intel_pinctrl_suspend_noirq(struct d |
| } |
| EXPORT_SYMBOL_GPL(intel_pinctrl_suspend_noirq); |
| |
| -static void intel_gpio_irq_init(struct intel_pinctrl *pctrl) |
| -{ |
| - size_t i; |
| - |
| - for (i = 0; i < pctrl->ncommunities; i++) { |
| - const struct intel_community *community; |
| - void __iomem *base; |
| - unsigned int gpp; |
| - |
| - community = &pctrl->communities[i]; |
| - base = community->regs; |
| - |
| - for (gpp = 0; gpp < community->ngpps; gpp++) { |
| - /* Mask and clear all interrupts */ |
| - writel(0, base + community->ie_offset + gpp * 4); |
| - writel(0xffff, base + community->is_offset + gpp * 4); |
| - } |
| - } |
| -} |
| - |
| static bool intel_gpio_update_reg(void __iomem *reg, u32 mask, u32 value) |
| { |
| u32 curr, updated; |