Message ID | 20240611143800.4115-1-asmaa@nvidia.com |
---|---|
State | New |
Headers | show |
Series | [v1,1/1] gpio: mlxbf3: Support shutdown() function | expand |
On Tue, Jun 11, 2024 at 5:38 PM Asmaa Mnebhi <asmaa@nvidia.com> wrote: > > 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. The gige driver uses polling while the GPIO10 interrupt > is still enabled from 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 GPIO > interrupt line which was not cleared. > The solution is to add a shutdown function to the gpio driver to clear and disable GPIO > all interrupts. Also clear the interrupt after disabling it in mlxbf3_gpio_irq_disable(). ... > +#define MLXBF_GPIO_CLR_ALL_INTS 0xFFFFFFFF GENMASK() ... > +static void mlxbf3_gpio_shutdown(struct platform_device *pdev) > +{ > + struct mlxbf3_gpio_context *gs = dev_get_drvdata(&pdev->dev); platform_get_drvdata() > + /* Disable and clear all interrupts */ > + writel(0, gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_EVTEN0); > + writel(MLXBF_GPIO_CLR_ALL_INTS, gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_CLRCAUSE); > +}
diff --git a/drivers/gpio/gpio-mlxbf3.c b/drivers/gpio/gpio-mlxbf3.c index d5906d419b0a..40bd93e3d695 100644 --- a/drivers/gpio/gpio-mlxbf3.c +++ b/drivers/gpio/gpio-mlxbf3.c @@ -39,6 +39,8 @@ #define MLXBF_GPIO_CAUSE_OR_EVTEN0 0x14 #define MLXBF_GPIO_CAUSE_OR_CLRCAUSE 0x18 +#define MLXBF_GPIO_CLR_ALL_INTS 0xFFFFFFFF + struct mlxbf3_gpio_context { struct gpio_chip gc; @@ -82,6 +84,8 @@ static void mlxbf3_gpio_irq_disable(struct irq_data *irqd) val = readl(gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_EVTEN0); val &= ~BIT(offset); writel(val, gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_EVTEN0); + + writel(BIT(offset), gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_CLRCAUSE); raw_spin_unlock_irqrestore(&gs->gc.bgpio_lock, flags); gpiochip_disable_irq(gc, offset); @@ -253,6 +257,15 @@ static int mlxbf3_gpio_probe(struct platform_device *pdev) return 0; } +static void mlxbf3_gpio_shutdown(struct platform_device *pdev) +{ + struct mlxbf3_gpio_context *gs = dev_get_drvdata(&pdev->dev); + + /* Disable and clear all interrupts */ + writel(0, gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_EVTEN0); + writel(MLXBF_GPIO_CLR_ALL_INTS, gs->gpio_cause_io + MLXBF_GPIO_CAUSE_OR_CLRCAUSE); +} + static const struct acpi_device_id mlxbf3_gpio_acpi_match[] = { { "MLNXBF33", 0 }, {} @@ -265,6 +278,7 @@ static struct platform_driver mlxbf3_gpio_driver = { .acpi_match_table = mlxbf3_gpio_acpi_match, }, .probe = mlxbf3_gpio_probe, + .shutdown = mlxbf3_gpio_shutdown, }; module_platform_driver(mlxbf3_gpio_driver);