[RFC PATCH 4/9] soc: samsung: Add generic power-management driver for Exynos

Chanwoo Choi cw00.choi at samsung.com
Mon Jan 8 23:59:01 PST 2018


To enter suspend, Exynos SoC requires the some machine dependent procedures.
This patch introduces the generic power-management driver to support
those requirements and generic interface for power state management.

Signed-off-by: Jonghwa Lee <jonghwa3.lee at samsung.com>
Signed-off-by: Chanwoo Choi <cw00.choi at samsung.com>
---
 arch/arm/mach-exynos/common.h         |   1 -
 arch/arm/mach-exynos/exynos.c         |  23 +----
 drivers/soc/samsung/Makefile          |   2 +-
 drivers/soc/samsung/exynos-pm.c       | 176 ++++++++++++++++++++++++++++++++++
 include/linux/soc/samsung/exynos-pm.h |  21 ++++
 5 files changed, 199 insertions(+), 24 deletions(-)
 create mode 100644 drivers/soc/samsung/exynos-pm.c
 create mode 100644 include/linux/soc/samsung/exynos-pm.h

diff --git a/arch/arm/mach-exynos/common.h b/arch/arm/mach-exynos/common.h
index afbc143a3d5d..ad482c0fc131 100644
--- a/arch/arm/mach-exynos/common.h
+++ b/arch/arm/mach-exynos/common.h
@@ -119,7 +119,6 @@ enum {
  * Magic values for bootloader indicating chosen low power mode.
  * See also Documentation/arm/Samsung/Bootloader-interface.txt
  */
-#define EXYNOS_SLEEP_MAGIC	0x00000bad
 #define EXYNOS_AFTR_MAGIC	0xfcba0d10

 void exynos_set_boot_flag(unsigned int cpu, unsigned int mode);
diff --git a/arch/arm/mach-exynos/exynos.c b/arch/arm/mach-exynos/exynos.c
index fbd108ce8745..0d5265d175c4 100644
--- a/arch/arm/mach-exynos/exynos.c
+++ b/arch/arm/mach-exynos/exynos.c
@@ -12,6 +12,7 @@
 #include <linux/of_fdt.h>
 #include <linux/platform_device.h>
 #include <linux/irqchip.h>
+#include <linux/soc/samsung/exynos-pm.h>
 #include <linux/soc/samsung/exynos-regs-pmu.h>

 #include <asm/cacheflush.h>
@@ -41,28 +42,6 @@
 	.id                = -1,
 };

-void __iomem *sysram_base_addr __ro_after_init;
-void __iomem *sysram_ns_base_addr __ro_after_init;
-
-void __init exynos_sysram_init(void)
-{
-	struct device_node *node;
-
-	for_each_compatible_node(node, NULL, "samsung,exynos4210-sysram") {
-		if (!of_device_is_available(node))
-			continue;
-		sysram_base_addr = of_iomap(node, 0);
-		break;
-	}
-
-	for_each_compatible_node(node, NULL, "samsung,exynos4210-sysram-ns") {
-		if (!of_device_is_available(node))
-			continue;
-		sysram_ns_base_addr = of_iomap(node, 0);
-		break;
-	}
-}
-
 static void __init exynos_init_late(void)
 {
 	if (of_machine_is_compatible("samsung,exynos5440"))
diff --git a/drivers/soc/samsung/Makefile b/drivers/soc/samsung/Makefile
index d2e637339a45..58ca5bdabf1f 100644
--- a/drivers/soc/samsung/Makefile
+++ b/drivers/soc/samsung/Makefile
@@ -1,5 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0
-obj-$(CONFIG_EXYNOS_PMU)	+= exynos-pmu.o
+obj-$(CONFIG_EXYNOS_PMU)	+= exynos-pmu.o exynos-pm.o

 obj-$(CONFIG_EXYNOS_PMU_ARM_DRIVERS)	+= exynos3250-pmu.o exynos4-pmu.o \
 					exynos5250-pmu.o exynos5420-pmu.o \
diff --git a/drivers/soc/samsung/exynos-pm.c b/drivers/soc/samsung/exynos-pm.c
new file mode 100644
index 000000000000..45d84bbe5e61
--- /dev/null
+++ b/drivers/soc/samsung/exynos-pm.c
@@ -0,0 +1,176 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// based on arch/arm/mach-exynos/suspend.c
+// Copyright (c) 2018 Samsung Electronics Co., Ltd.
+//
+// Exynos Power Management support driver
+
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_fdt.h>
+#include <linux/kernel.h>
+#include <linux/regulator/machine.h>
+#include <linux/syscore_ops.h>
+#include <linux/suspend.h>
+
+#include <asm/cpuidle.h>
+#include <asm/io.h>
+#include <asm/suspend.h>
+
+#include <linux/soc/samsung/exynos-pm.h>
+#include <linux/soc/samsung/exynos-pmu.h>
+
+/*
+ * The struct exynos_pm_data contains the callbacks of
+ * both struct platform_suspend_ops and syscore_ops.
+ * This structure is listed according to the call order,
+ * because the callback call order for the two structures is mixed.
+ */
+struct exynos_pm_data {
+	int (*prepare)(void);			/* for platform_suspend_ops */
+	int (*suspend)(void);			/* for syscore_ops */
+	int (*enter)(suspend_state_t state);	/* for platform_suspend_ops */
+	void (*resume)(void);			/* for syscore_ops */
+	void (*finish)(void);			/* for platform_suspend_ops */
+};
+
+static struct platform_suspend_ops exynos_pm_suspend_ops;
+static struct syscore_ops exynos_pm_syscore_ops;
+static const struct exynos_pm_data *pm_data  __ro_after_init;
+
+void __iomem *sysram_base_addr __ro_after_init;
+void __iomem *sysram_ns_base_addr __ro_after_init;
+
+static int exynos_pm_prepare(void)
+{
+	int ret;
+
+	/*
+	 * REVISIT: It would be better if struct platform_suspend_ops
+	 * .prepare handler get the suspend_state_t as a parameter to
+	 * avoid hard-coding the suspend to mem state. It's safe to do
+	 * it now only because the suspend_valid_only_mem function is
+	 * used as the .valid callback used to check if a given state
+	 * is supported by the platform anyways.
+	 */
+	ret = regulator_suspend_prepare(PM_SUSPEND_MEM);
+	if (ret) {
+		pr_err("Failed to prepare regulators for suspend (%d)\n", ret);
+		return ret;
+	}
+
+	if (pm_data->prepare) {
+		ret = pm_data->prepare();
+		if (ret) {
+			pr_err("Failed to prepare for suspend (%d)\n", ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int exynos_pm_suspend(void)
+{
+	if (pm_data->suspend)
+		return pm_data->suspend();
+
+	return 0;
+}
+
+static int exynos_pm_enter(suspend_state_t state)
+{
+	int ret;
+
+	exynos_sys_powerdown_conf(SYS_SLEEP);
+
+	ret = pm_data->enter(state);
+	if (ret) {
+		pr_err("Failed to enter sleep\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static void exynos_pm_resume(void)
+{
+	exynos_sys_powerup_conf(SYS_SLEEP);
+
+	if (pm_data->resume)
+		pm_data->resume();
+}
+
+static void exynos_pm_finish(void)
+{
+	int ret;
+
+	ret = regulator_suspend_finish();
+	if (ret)
+		pr_warn("Failed to resume regulators from suspend (%d)\n", ret);
+
+	if (pm_data->finish)
+		pm_data->finish();
+}
+
+/*
+ * Split the data between ARM architectures because it is relatively big
+ * and useless on other arch.
+ */
+#ifdef CONFIG_EXYNOS_PMU_ARM_DRIVERS
+#define exynos_pm_data_arm_ptr(data)	(&data)
+#else
+#define exynos_pm_data_arm_ptr(data)	NULL
+#endif
+
+static const struct of_device_id exynos_pm_of_device_ids[] = {
+	{ /*sentinel*/ },
+};
+
+void __init exynos_sysram_init(void)
+{
+	struct device_node *np;
+
+	for_each_compatible_node(np, NULL, "samsung,exynos4210-sysram") {
+		if (!of_device_is_available(np))
+			continue;
+		sysram_base_addr = of_iomap(np, 0);
+		break;
+	}
+
+	for_each_compatible_node(np, NULL, "samsung,exynos4210-sysram-ns") {
+		if (!of_device_is_available(np))
+			continue;
+		sysram_ns_base_addr = of_iomap(np, 0);
+		break;
+	}
+}
+
+static int __init exynos_pm_init(void)
+{
+	const struct of_device_id *match;
+	struct device_node *np;
+
+	np = of_find_matching_node_and_match(NULL,
+					exynos_pm_of_device_ids, &match);
+	if (!np) {
+		pr_err("Failed to find PMU node for Exynos Power-Management\n");
+		return -ENODEV;
+	}
+	pm_data = (const struct exynos_pm_data *) match->data;
+
+	exynos_sysram_init();
+
+	exynos_pm_suspend_ops.valid	= suspend_valid_only_mem;
+	exynos_pm_suspend_ops.prepare	= exynos_pm_prepare;
+	exynos_pm_syscore_ops.suspend	= exynos_pm_suspend;
+	exynos_pm_suspend_ops.enter	= exynos_pm_enter;
+	exynos_pm_syscore_ops.resume	= exynos_pm_resume;
+	exynos_pm_suspend_ops.finish	= exynos_pm_finish;
+
+	register_syscore_ops(&exynos_pm_syscore_ops);
+	suspend_set_ops(&exynos_pm_suspend_ops);
+
+	return 0;
+}
+postcore_initcall(exynos_pm_init);
diff --git a/include/linux/soc/samsung/exynos-pm.h b/include/linux/soc/samsung/exynos-pm.h
new file mode 100644
index 000000000000..b1afe95ed10c
--- /dev/null
+++ b/include/linux/soc/samsung/exynos-pm.h
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (c) 2018 Samsung Electronics Co., Ltd.
+//
+// Header for Exynos Power-Management support driver
+
+#ifndef __LINUX_SOC_EXYNOS_PM_H
+#define __LINUX_SOC_EXYNOS_PM_H
+
+/*
+ * Magic values for bootloader indicating chosen low power mode.
+ * See also Documentation/arm/Samsung/Bootloader-interface.txt
+ */
+#define EXYNOS_SLEEP_MAGIC	0x00000bad
+
+extern void __iomem *sysram_base_addr;
+extern void __iomem *sysram_ns_base_addr;
+
+extern void exynos_sysram_init(void);
+
+#endif /* __LINUX_SOC_EXYNOS_PMU_H */
--
1.9.1




More information about the linux-arm-kernel mailing list