diff options
-rw-r--r-- | drivers/gpio/gpio-mxc.c | 73 |
1 files changed, 73 insertions, 0 deletions
diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index 2f2829966d4c..995cf0b9e0b1 100644 --- a/drivers/gpio/gpio-mxc.c +++ b/drivers/gpio/gpio-mxc.c @@ -45,6 +45,15 @@ struct mxc_gpio_hwdata { unsigned fall_edge; }; +struct mxc_gpio_reg_saved { + u32 icr1; + u32 icr2; + u32 imr; + u32 gdir; + u32 edge_sel; + u32 dr; +}; + struct mxc_gpio_port { struct list_head node; void __iomem *base; @@ -55,6 +64,8 @@ struct mxc_gpio_port { struct gpio_chip gc; struct device *dev; u32 both_edges; + struct mxc_gpio_reg_saved gpio_saved_reg; + bool power_off; }; static struct mxc_gpio_hwdata imx1_imx21_gpio_hwdata = { @@ -143,6 +154,7 @@ static const struct of_device_id mxc_gpio_dt_ids[] = { { .compatible = "fsl,imx21-gpio", .data = &mxc_gpio_devtype[IMX21_GPIO], }, { .compatible = "fsl,imx31-gpio", .data = &mxc_gpio_devtype[IMX31_GPIO], }, { .compatible = "fsl,imx35-gpio", .data = &mxc_gpio_devtype[IMX35_GPIO], }, + { .compatible = "fsl,imx7d-gpio", .data = &mxc_gpio_devtype[IMX35_GPIO], }, { /* sentinel */ } }; @@ -434,6 +446,9 @@ static int mxc_gpio_probe(struct platform_device *pdev) return err; } + if (of_device_is_compatible(np, "fsl,imx7d-gpio")) + port->power_off = true; + /* disable the interrupt and clear the status */ writel(0, port->base + GPIO_IMR); writel(~0, port->base + GPIO_ISR); @@ -497,6 +512,8 @@ static int mxc_gpio_probe(struct platform_device *pdev) list_add_tail(&port->node, &mxc_gpio_ports); + platform_set_drvdata(pdev, port); + return 0; out_irqdomain_remove: @@ -507,11 +524,67 @@ out_bgio: return err; } +static void mxc_gpio_save_regs(struct mxc_gpio_port *port) +{ + if (!port->power_off) + return; + + port->gpio_saved_reg.icr1 = readl(port->base + GPIO_ICR1); + port->gpio_saved_reg.icr2 = readl(port->base + GPIO_ICR2); + port->gpio_saved_reg.imr = readl(port->base + GPIO_IMR); + port->gpio_saved_reg.gdir = readl(port->base + GPIO_GDIR); + port->gpio_saved_reg.edge_sel = readl(port->base + GPIO_EDGE_SEL); + port->gpio_saved_reg.dr = readl(port->base + GPIO_DR); +} + +static void mxc_gpio_restore_regs(struct mxc_gpio_port *port) +{ + if (!port->power_off) + return; + + writel(port->gpio_saved_reg.icr1, port->base + GPIO_ICR1); + writel(port->gpio_saved_reg.icr2, port->base + GPIO_ICR2); + writel(port->gpio_saved_reg.imr, port->base + GPIO_IMR); + writel(port->gpio_saved_reg.gdir, port->base + GPIO_GDIR); + writel(port->gpio_saved_reg.edge_sel, port->base + GPIO_EDGE_SEL); + writel(port->gpio_saved_reg.dr, port->base + GPIO_DR); +} + +static int __maybe_unused mxc_gpio_noirq_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mxc_gpio_port *port = platform_get_drvdata(pdev); + + mxc_gpio_save_regs(port); + clk_disable_unprepare(port->clk); + + return 0; +} + +static int __maybe_unused mxc_gpio_noirq_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mxc_gpio_port *port = platform_get_drvdata(pdev); + int ret; + + ret = clk_prepare_enable(port->clk); + if (ret) + return ret; + mxc_gpio_restore_regs(port); + + return 0; +} + +static const struct dev_pm_ops mxc_gpio_dev_pm_ops = { + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(mxc_gpio_noirq_suspend, mxc_gpio_noirq_resume) +}; + static struct platform_driver mxc_gpio_driver = { .driver = { .name = "gpio-mxc", .of_match_table = mxc_gpio_dt_ids, .suppress_bind_attrs = true, + .pm = &mxc_gpio_dev_pm_ops, }, .probe = mxc_gpio_probe, .id_table = mxc_gpio_devtype, |