Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

gpio: mlxbf3: Support shutdown() function

During Linux graceful reboot, the GPIO interrupts are not disabled.
Since the drivers are not removed during graceful reboot,
the logic to call mlxbf3_gpio_irq_disable() is not triggered.
Interrupts that remain enabled can cause issues on subsequent boots.

For example, the mlxbf-gige driver contains PHY logic to bring up the link.
If the gpio-mlxbf3 driver loads first, the mlxbf-gige driver
will use a GPIO interrupt to bring up the link.
Otherwise, it will use polling.
The next time Linux boots and loads the drivers in this order, we encounter the issue:
- mlxbf-gige loads first and uses polling while the GPIO10
interrupt is still enabled from the previous boot. So if
the interrupt triggers, there is nothing to clear it.
- gpio-mlxbf3 loads.
- i2c-mlxbf loads. The interrupt doesn't trigger for I2C
because it is shared with the GPIO interrupt line which
was not cleared.

The solution is to add a shutdown function to the GPIO driver to clear and disable
all interrupts. Also clear the interrupt after disabling it in mlxbf3_gpio_irq_disable().

Fixes: 38a700efc510 ("gpio: mlxbf3: Add gpio driver support")
Signed-off-by: Asmaa Mnebhi <asmaa@nvidia.com>
Reviewed-by: David Thompson <davthompson@nvidia.com>
Reviewed-by: Andy Shevchenko <andy@kernel.org>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Link: https://lore.kernel.org/r/20240611171509.22151-1-asmaa@nvidia.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>

authored by

Asmaa Mnebhi and committed by
Bartosz Golaszewski
aad41832 8400291e

+14
+14
drivers/gpio/gpio-mlxbf3.c
··· 39 39 #define MLXBF_GPIO_CAUSE_OR_EVTEN0 0x14 40 40 #define MLXBF_GPIO_CAUSE_OR_CLRCAUSE 0x18 41 41 42 + #define MLXBF_GPIO_CLR_ALL_INTS GENMASK(31, 0) 43 + 42 44 struct mlxbf3_gpio_context { 43 45 struct gpio_chip gc; 44 46 ··· 84 82 val = readl(gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_EVTEN0); 85 83 val &= ~BIT(offset); 86 84 writel(val, gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_EVTEN0); 85 + 86 + writel(BIT(offset), gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_CLRCAUSE); 87 87 raw_spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags); 88 88 89 89 gpiochip_disable_irq(gc, offset); ··· 257 253 return 0; 258 254 } 259 255 256 + static void mlxbf3_gpio_shutdown(struct platform_device *pdev) 257 + { 258 + struct mlxbf3_gpio_context *gs = platform_get_drvdata(pdev); 259 + 260 + /* Disable and clear all interrupts */ 261 + writel(0, gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_EVTEN0); 262 + writel(MLXBF_GPIO_CLR_ALL_INTS, gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_CLRCAUSE); 263 + } 264 + 260 265 static const struct acpi_device_id mlxbf3_gpio_acpi_match[] = { 261 266 { "MLNXBF33", 0 }, 262 267 {} ··· 278 265 .acpi_match_table = mlxbf3_gpio_acpi_match, 279 266 }, 280 267 .probe = mlxbf3_gpio_probe, 268 + .shutdown = mlxbf3_gpio_shutdown, 281 269 }; 282 270 module_platform_driver(mlxbf3_gpio_driver); 283 271