Message ID | 20191002144502.156393-1-thierry.reding@gmail.com |
---|---|
State | Rejected |
Headers | show |
Series | [1/2] gpio: tegra186: Implement system suspend/resume support | expand |
On 10/2/19 7:45 AM, Thierry Reding wrote: > From: Thierry Reding <treding@nvidia.com> > > Backup GPIO control registers on suspend and restore them on resume to > ensure that the GPIOs' configuration remains the same across suspend and > resume. > > Signed-off-by: Thierry Reding <treding@nvidia.com> > --- > drivers/gpio/gpio-tegra186.c | 51 ++++++++++++++++++++++++++++++++++++ > 1 file changed, 51 insertions(+) > > diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c > index a9058fda187e..3ded6ba2f997 100644 > --- a/drivers/gpio/gpio-tegra186.c > +++ b/drivers/gpio/gpio-tegra186.c > @@ -64,6 +64,12 @@ struct tegra_gpio { > const struct tegra_gpio_soc *soc; > > void __iomem *base; > + > + struct tegra_gpio_context { > + u32 value; > + u32 control; > + u32 config; > + } *context; > }; > > static const struct tegra_gpio_port * > @@ -455,6 +461,11 @@ static int tegra186_gpio_probe(struct platform_device *pdev) > for (i = 0; i < gpio->soc->num_ports; i++) > gpio->gpio.ngpio += gpio->soc->ports[i].pins; > > + gpio->context = devm_kmalloc_array(gpio->gpio.parent, gpio->gpio.ngpio, > + sizeof(*gpio->context), GFP_KERNEL); > + if (!gpio->context) > + return -ENOMEM; > + > names = devm_kcalloc(gpio->gpio.parent, gpio->gpio.ngpio, > sizeof(*names), GFP_KERNEL); > if (!names) > @@ -526,6 +537,45 @@ static int tegra186_gpio_remove(struct platform_device *pdev) > return 0; > } > > +static int tegra186_gpio_suspend(struct device *dev) > +{ > + struct tegra_gpio *gpio = dev_get_drvdata(dev); > + unsigned int i; > + > + for (i = 0; i < gpio->gpio.ngpio; i++) { > + struct tegra_gpio_context *context = &gpio->context[i]; > + void __iomem *base = tegra186_gpio_get_base(gpio, i); > + > + context->config = readl(base + TEGRA186_GPIO_ENABLE_CONFIG); > + context->control = readl(base + TEGRA186_GPIO_OUTPUT_CONTROL); > + context->value = readl(base + TEGRA186_GPIO_OUTPUT_VALUE); > + } > + > + return 0; > +} > + > +static int tegra186_gpio_resume(struct device *dev) > +{ > + struct tegra_gpio *gpio = dev_get_drvdata(dev); > + unsigned int i; > + > + for (i = 0; i < gpio->gpio.ngpio; i++) { > + struct tegra_gpio_context *context = &gpio->context[i]; > + void __iomem *base = tegra186_gpio_get_base(gpio, i); > + > + writel(context->value, base + TEGRA186_GPIO_OUTPUT_VALUE); > + writel(context->control, base + TEGRA186_GPIO_OUTPUT_CONTROL); > + writel(context->config, base + TEGRA186_GPIO_ENABLE_CONFIG); > + } > + > + return 0; > +} > + > +static const struct dev_pm_ops tegra186_gpio_pm = { > + .suspend_late = tegra186_gpio_suspend, > + .resume_early = tegra186_gpio_resume, > +}; > + > #define TEGRA186_MAIN_GPIO_PORT(port, base, count, controller) \ > [TEGRA186_MAIN_GPIO_PORT_##port] = { \ > .name = #port, \ > @@ -680,6 +730,7 @@ static struct platform_driver tegra186_gpio_driver = { > .driver = { > .name = "tegra186-gpio", > .of_match_table = tegra186_gpio_of_match, > + .pm = &tegra186_gpio_pm, > }, > .probe = tegra186_gpio_probe, > .remove = tegra186_gpio_remove, > I see jetson Xavier RTC wakeup test fail with this patch. It no longer reaches the UART shell after suspend exit. Jetson-TX2 works fine with this patch. There seems to be some hang on Xavier. -regards, Bitan
On Thu, Oct 3, 2019 at 1:59 PM Bitan Biswas <bbiswas@nvidia.com> wrote: > I see jetson Xavier RTC wakeup test fail with this patch. It no longer > reaches the UART shell after suspend exit. Jetson-TX2 works fine with > this patch. There seems to be some hang on Xavier. That sounds like some very typical situation where you need proper wakeup handling, so that the chip does not suspend if any irq lines are flagged as wakeup-capable and in use. Yours, Linus Walleij
diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c index a9058fda187e..3ded6ba2f997 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -64,6 +64,12 @@ struct tegra_gpio { const struct tegra_gpio_soc *soc; void __iomem *base; + + struct tegra_gpio_context { + u32 value; + u32 control; + u32 config; + } *context; }; static const struct tegra_gpio_port * @@ -455,6 +461,11 @@ static int tegra186_gpio_probe(struct platform_device *pdev) for (i = 0; i < gpio->soc->num_ports; i++) gpio->gpio.ngpio += gpio->soc->ports[i].pins; + gpio->context = devm_kmalloc_array(gpio->gpio.parent, gpio->gpio.ngpio, + sizeof(*gpio->context), GFP_KERNEL); + if (!gpio->context) + return -ENOMEM; + names = devm_kcalloc(gpio->gpio.parent, gpio->gpio.ngpio, sizeof(*names), GFP_KERNEL); if (!names) @@ -526,6 +537,45 @@ static int tegra186_gpio_remove(struct platform_device *pdev) return 0; } +static int tegra186_gpio_suspend(struct device *dev) +{ + struct tegra_gpio *gpio = dev_get_drvdata(dev); + unsigned int i; + + for (i = 0; i < gpio->gpio.ngpio; i++) { + struct tegra_gpio_context *context = &gpio->context[i]; + void __iomem *base = tegra186_gpio_get_base(gpio, i); + + context->config = readl(base + TEGRA186_GPIO_ENABLE_CONFIG); + context->control = readl(base + TEGRA186_GPIO_OUTPUT_CONTROL); + context->value = readl(base + TEGRA186_GPIO_OUTPUT_VALUE); + } + + return 0; +} + +static int tegra186_gpio_resume(struct device *dev) +{ + struct tegra_gpio *gpio = dev_get_drvdata(dev); + unsigned int i; + + for (i = 0; i < gpio->gpio.ngpio; i++) { + struct tegra_gpio_context *context = &gpio->context[i]; + void __iomem *base = tegra186_gpio_get_base(gpio, i); + + writel(context->value, base + TEGRA186_GPIO_OUTPUT_VALUE); + writel(context->control, base + TEGRA186_GPIO_OUTPUT_CONTROL); + writel(context->config, base + TEGRA186_GPIO_ENABLE_CONFIG); + } + + return 0; +} + +static const struct dev_pm_ops tegra186_gpio_pm = { + .suspend_late = tegra186_gpio_suspend, + .resume_early = tegra186_gpio_resume, +}; + #define TEGRA186_MAIN_GPIO_PORT(port, base, count, controller) \ [TEGRA186_MAIN_GPIO_PORT_##port] = { \ .name = #port, \ @@ -680,6 +730,7 @@ static struct platform_driver tegra186_gpio_driver = { .driver = { .name = "tegra186-gpio", .of_match_table = tegra186_gpio_of_match, + .pm = &tegra186_gpio_pm, }, .probe = tegra186_gpio_probe, .remove = tegra186_gpio_remove,