[1/2] gpio: tegra186: Implement system suspend/resume support
diff mbox series

Message ID 20191002144502.156393-1-thierry.reding@gmail.com
State New
Headers show
Series
  • [1/2] gpio: tegra186: Implement system suspend/resume support
Related show

Commit Message

Thierry Reding Oct. 2, 2019, 2:45 p.m. UTC
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(+)

Comments

Bitan Biswas Oct. 3, 2019, 11:59 a.m. UTC | #1
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
Linus Walleij Oct. 3, 2019, 12:29 p.m. UTC | #2
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

Patch
diff mbox series

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,