diff options
author | Fugang Duan <fugang.duan@nxp.com> | 2017-12-25 17:44:28 +0800 |
---|---|---|
committer | Fugang Duan <fugang.duan@nxp.com> | 2018-01-08 14:12:02 +0800 |
commit | 21a46bbbefffd7a8e2ad2c500e84eb057e2a2cd8 (patch) | |
tree | c256f3c5147f62ae2f048b48e64f665a7b0dd389 | |
parent | 6927b042dfc9ab99d5bbd4374aa2ac299f9f1a14 (diff) |
MLK-17290-05 gpio: mxc: save and restore gpio controller registers when power off
Save gpio controller registers before power off, and then restore these
registers after power on. There have two cases need to save/restore regs:
a. If sub_irqs/sub_gpios are not free/released, device suspend() force
runtime suspend and power domain off in suspended stage, it needs to
keep the previous registers value after device resume back.
b. If some sub_irqs set irq type just one time, then irqchip should restore
the registers for correct irq type.
Signed-off-by: Fugang Duan <fugang.duan@nxp.com>
Tested-by: Guoniu.Zhou <guoniu.zhou@nxp.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
(cherry picked from commit: 764ebc90db18fe6f98bd77921050529ca6dd183e)
-rw-r--r-- | drivers/gpio/gpio-mxc.c | 45 |
1 files changed, 44 insertions, 1 deletions
diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index 8c7f2ad897d6..e81bb530cb17 100644 --- a/drivers/gpio/gpio-mxc.c +++ b/drivers/gpio/gpio-mxc.c @@ -70,6 +70,7 @@ struct mxc_gpio_port { struct irq_domain *domain; struct gpio_chip gc; u32 both_edges; + int saved_reg[6]; bool gpio_ranges; }; @@ -584,11 +585,46 @@ out_bgio: return err; } +static void mxc_gpio_save_regs(struct mxc_gpio_port *port) +{ + unsigned long flags; + + if (mxc_gpio_hwtype == IMX21_GPIO) + return; + + spin_lock_irqsave(&port->gc.bgpio_lock, flags); + port->saved_reg[0] = readl(port->base + GPIO_ICR1); + port->saved_reg[1] = readl(port->base + GPIO_ICR2); + port->saved_reg[2] = readl(port->base + GPIO_IMR); + port->saved_reg[3] = readl(port->base + GPIO_GDIR); + port->saved_reg[4] = readl(port->base + GPIO_EDGE_SEL); + port->saved_reg[5] = readl(port->base + GPIO_DR); + spin_unlock_irqrestore(&port->gc.bgpio_lock, flags); +} + +static void mxc_gpio_restore_regs(struct mxc_gpio_port *port) +{ + unsigned long flags; + + if (mxc_gpio_hwtype == IMX21_GPIO) + return; + + spin_lock_irqsave(&port->gc.bgpio_lock, flags); + writel(port->saved_reg[0], port->base + GPIO_ICR1); + writel(port->saved_reg[1], port->base + GPIO_ICR2); + writel(port->saved_reg[2], port->base + GPIO_IMR); + writel(port->saved_reg[3], port->base + GPIO_GDIR); + writel(port->saved_reg[4], port->base + GPIO_EDGE_SEL); + writel(port->saved_reg[5], port->base + GPIO_DR); + spin_unlock_irqrestore(&port->gc.bgpio_lock, flags); +} + static int __maybe_unused mxc_gpio_runtime_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; @@ -598,8 +634,15 @@ static int __maybe_unused mxc_gpio_runtime_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; - return clk_prepare_enable(port->clk); + mxc_gpio_restore_regs(port); + + return 0; } static int __maybe_unused mxc_gpio_suspend(struct device *dev) |