[PATCH 1/2] gpio: tegra186: Implement system suspend/resume support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Thierry Reding <treding@xxxxxxxxxx>

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@xxxxxxxxxx>
---
 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,
-- 
2.23.0




[Index of Archives]     [ARM Kernel]     [Linux ARM]     [Linux ARM MSM]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux