summaryrefslogtreecommitdiff
path: root/drivers/gpio
diff options
context:
space:
mode:
authorShenwei Wang <shenwei.wang@nxp.com>2023-08-09 17:13:24 +0300
committerBartosz Golaszewski <bartosz.golaszewski@linaro.org>2023-08-11 17:22:46 +0300
commit5f6d1998adeb5374ef248b5ba2e2a0c30ab0f60b (patch)
tree7e3cca737b0da4ef9dc5bdfe639f0f5a8f745d16 /drivers/gpio
parentb7df0f340b64c543bb84c42698347f08b6e18fe2 (diff)
downloadlinux-5f6d1998adeb5374ef248b5ba2e2a0c30ab0f60b.tar.xz
gpio: mxc: release the parent IRQ in runtime suspend
Release the parent interrupt request during runtime suspend, allowing the parent interrupt controller to enter runtime suspend if there are no active users. This change may not have a visible impact if the parent controller is the GIC, but it can enable significant power savings for parent IRQ controllers like IRQSteer inside a subsystem on i.MX8 SoCs. Releasing the parent IRQ provides an opportunity for the subsystem to enter suspend states if there are no active users. Signed-off-by: Shenwei Wang <shenwei.wang@nxp.com> Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com> Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
Diffstat (limited to 'drivers/gpio')
-rw-r--r--drivers/gpio/gpio-mxc.c41
1 files changed, 29 insertions, 12 deletions
diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c
index 377d3ab8d626..004c6ad7ce52 100644
--- a/drivers/gpio/gpio-mxc.c
+++ b/drivers/gpio/gpio-mxc.c
@@ -62,6 +62,7 @@ struct mxc_gpio_port {
struct clk *clk;
int irq;
int irq_high;
+ void (*mx_irq_handler)(struct irq_desc *desc);
struct irq_domain *domain;
struct gpio_chip gc;
struct device *dev;
@@ -399,6 +400,24 @@ static void mxc_gpio_free(struct gpio_chip *chip, unsigned int offset)
pm_runtime_put(chip->parent);
}
+static void mxc_update_irq_chained_handler(struct mxc_gpio_port *port, bool enable)
+{
+ if (enable)
+ irq_set_chained_handler_and_data(port->irq, port->mx_irq_handler, port);
+ else
+ irq_set_chained_handler_and_data(port->irq, NULL, NULL);
+
+ /* setup handler for GPIO 16 to 31 */
+ if (port->irq_high > 0) {
+ if (enable)
+ irq_set_chained_handler_and_data(port->irq_high,
+ port->mx_irq_handler,
+ port);
+ else
+ irq_set_chained_handler_and_data(port->irq_high, NULL, NULL);
+ }
+}
+
static int mxc_gpio_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
@@ -460,18 +479,12 @@ static int mxc_gpio_probe(struct platform_device *pdev)
* the handler is needed only once, but doing it for every port
* is more robust and easier.
*/
- irq_set_chained_handler(port->irq, mx2_gpio_irq_handler);
- } else {
- /* setup one handler for each entry */
- irq_set_chained_handler_and_data(port->irq,
- mx3_gpio_irq_handler, port);
- if (port->irq_high > 0)
- /* setup handler for GPIO 16 to 31 */
- irq_set_chained_handler_and_data(port->irq_high,
- mx3_gpio_irq_handler,
- port);
- }
+ port->irq_high = -1;
+ port->mx_irq_handler = mx2_gpio_irq_handler;
+ } else
+ port->mx_irq_handler = mx3_gpio_irq_handler;
+ mxc_update_irq_chained_handler(port, true);
err = bgpio_init(&port->gc, &pdev->dev, 4,
port->base + GPIO_PSR,
port->base + GPIO_DR, NULL,
@@ -604,6 +617,7 @@ static int mxc_gpio_runtime_suspend(struct device *dev)
mxc_gpio_save_regs(port);
clk_disable_unprepare(port->clk);
+ mxc_update_irq_chained_handler(port, false);
return 0;
}
@@ -613,9 +627,12 @@ static int mxc_gpio_runtime_resume(struct device *dev)
struct mxc_gpio_port *port = dev_get_drvdata(dev);
int ret;
+ mxc_update_irq_chained_handler(port, true);
ret = clk_prepare_enable(port->clk);
- if (ret)
+ if (ret) {
+ mxc_update_irq_chained_handler(port, false);
return ret;
+ }
mxc_gpio_restore_regs(port);