[PATCH RFC v2 04/16] WIP: ARM: PM domains for CPUs/clusters
Lina Iyer
lina.iyer at linaro.org
Fri Jun 26 20:02:24 PDT 2015
From: Kevin Hilman <khilman at linaro.org>
diff --git a/arch/arm/boot/dts/exynos5420.dtsi b/arch/arm/boot/dts/exynos5420.dtsi
index 534f27c..fc59876 100644
--- a/arch/arm/boot/dts/exynos5420.dtsi
+++ b/arch/arm/boot/dts/exynos5420.dtsi
@@ -61,6 +61,7 @@
reg = <0x0>;
clock-frequency = <1800000000>;
cci-control-port = <&cci_control1>;
+ power-domains = <&big_cluster_pd>;
};
cpu1: cpu at 1 {
@@ -69,6 +70,7 @@
reg = <0x1>;
clock-frequency = <1800000000>;
cci-control-port = <&cci_control1>;
+ power-domains = <&big_cluster_pd>;
};
cpu2: cpu at 2 {
@@ -77,6 +79,7 @@
reg = <0x2>;
clock-frequency = <1800000000>;
cci-control-port = <&cci_control1>;
+ power-domains = <&big_cluster_pd>;
};
cpu3: cpu at 3 {
@@ -85,6 +88,7 @@
reg = <0x3>;
clock-frequency = <1800000000>;
cci-control-port = <&cci_control1>;
+ power-domains = <&big_cluster_pd>;
};
cpu4: cpu at 100 {
@@ -93,6 +97,7 @@
reg = <0x100>;
clock-frequency = <1000000000>;
cci-control-port = <&cci_control0>;
+ power-domains = <&little_cluster_pd>;
};
cpu5: cpu at 101 {
@@ -101,6 +106,7 @@
reg = <0x101>;
clock-frequency = <1000000000>;
cci-control-port = <&cci_control0>;
+ power-domains = <&little_cluster_pd>;
};
cpu6: cpu at 102 {
@@ -109,6 +115,7 @@
reg = <0x102>;
clock-frequency = <1000000000>;
cci-control-port = <&cci_control0>;
+ power-domains = <&little_cluster_pd>;
};
cpu7: cpu at 103 {
@@ -117,6 +124,7 @@
reg = <0x103>;
clock-frequency = <1000000000>;
cci-control-port = <&cci_control0>;
+ power-domains = <&little_cluster_pd>;
};
};
@@ -249,6 +257,16 @@
};
};
+ big_cluster_pd: big_cluster {
+ compatible = "arm,pd";
+ #power-domain-cells = <0>;
+ };
+
+ little_cluster_pd: little_cluster {
+ compatible = "arm,pd";
+ #power-domain-cells = <0>;
+ };
+
gsc_pd: power-domain at 10044000 {
compatible = "samsung,exynos4210-pd";
reg = <0x10044000 0x20>;
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index e69f7a1..98ce19c 100644
--- a/arch/arm/kernel/Makefile
+++ b/arch/arm/kernel/Makefile
@@ -78,6 +78,7 @@ CFLAGS_pj4-cp0.o := -marm
AFLAGS_iwmmxt.o := -Wa,-mcpu=iwmmxt
obj-$(CONFIG_ARM_CPU_TOPOLOGY) += topology.o
obj-$(CONFIG_VDSO) += vdso.o
+obj-$(CONFIG_PM_GENERIC_DOMAINS) += domains.o
ifneq ($(CONFIG_ARCH_EBSA110),y)
obj-y += io.o
diff --git a/arch/arm/kernel/domains.c b/arch/arm/kernel/domains.c
new file mode 100644
index 0000000..8388f54
--- /dev/null
+++ b/arch/arm/kernel/domains.c
@@ -0,0 +1,122 @@
+#define DEBUG
+
+#include <linux/kernel.h>
+#include <linux/of_platform.h>
+#include <linux/device.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#define NAME_MAX 16
+
+struct arm_pm_domain {
+ struct generic_pm_domain genpd;
+};
+
+static inline
+struct arm_pm_domain *to_arm_pd(struct generic_pm_domain *d)
+{
+ return container_of(d, struct arm_pm_domain, genpd);
+}
+
+static int arm_pd_power_down(struct generic_pm_domain *genpd)
+{
+ /* pr_info("KJH: %s: %s\n", __func__, genpd->name); */
+ return 0;
+}
+
+static int arm_pd_power_up(struct generic_pm_domain *genpd)
+{
+ /* pr_info("KJH: %s: %s\n", __func__, genpd->name); */
+ return 0;
+}
+
+static int arm_domain_cpu_init(void)
+{
+ int cpuid, ret = 0;
+
+ /* Find any CPU nodes with a phandle to this power domain */
+ for_each_possible_cpu(cpuid) {
+ struct device *cpu_dev;
+
+ /* FIXME: this is open-coding of_cpu_device_node_get(), but I want handle to cpu_dev */
+ cpu_dev = get_cpu_device(cpuid);
+ if (!cpu_dev) {
+ pr_warn("%s: Unable to get device for CPU%d\n", __func__, cpuid);
+ return -ENODEV;
+ }
+
+ /*
+ * HACK: genpd checks if devices are runtime_suspended
+ * before doing a poweroff of the domain. However, that check
+ * assumes that that device has a driver. Since CPU devices don't
+ * have a driver, genpd assumes that the device is runtime_suspended
+ * and will power off the domain as soon as the any device
+ * in the domain does a runtime_suspend.
+ *
+ * c.f. the following code in pm_genpd_poweroff():
+ *
+ * if (pdd->dev->driver && (!pm_runtime_suspended(pdd->dev)
+ * || pdd->dev->power.irq_safe))
+ * not_suspended++;
+ *
+ * Just removing the pdd->dev->driver check would also work,
+ * but not sure if that's right in the general case.
+ */
+ cpu_dev->driver = kzalloc(sizeof(struct device_driver), GFP_KERNEL);
+ WARN_ON(!cpu_dev->driver);
+
+ if (cpu_online(cpuid)) {
+ pm_runtime_set_active(cpu_dev);
+ pm_runtime_get_noresume(cpu_dev);
+ } else {
+ pm_runtime_set_suspended(cpu_dev);
+ }
+ pm_runtime_irq_safe(cpu_dev);
+ pm_runtime_enable(cpu_dev);
+
+ ret = genpd_dev_pm_attach(cpu_dev);
+ if (ret) {
+ dev_warn(cpu_dev, "%s: Unable to attach to power-domain: %d\n", __func__, ret);
+ pm_runtime_disable(cpu_dev);
+ }
+ }
+
+ return 0;
+}
+device_initcall(arm_domain_cpu_init);
+
+static int arm_domain_init(void)
+{
+ struct platform_device *pdev;
+ struct device_node *np;
+
+ for_each_compatible_node(np, NULL, "arm,pd") {
+ struct arm_pm_domain *pd;
+ struct device *dev;
+
+ pdev = of_find_device_by_node(np);
+ dev = &pdev->dev;
+
+ pd = kzalloc(sizeof(*pd), GFP_KERNEL);
+ if (!pd) {
+ pr_err("%s: failed to allocate memory for domain\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ pd->genpd.name = kstrdup(np->name, GFP_KERNEL);
+ pd->genpd.power_off = arm_pd_power_down;
+ pd->genpd.power_on = arm_pd_power_up;
+ platform_set_drvdata(pdev, pd);
+
+ dev_dbg(dev, "adding as generic power domain.\n");
+ pm_genpd_init(&pd->genpd, &simple_qos_governor, false);
+ of_genpd_add_provider_simple(np, &pd->genpd);
+ }
+
+ return 0;
+}
+arch_initcall(arm_domain_init);
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index 42ffb8b..02140e6 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -5,6 +5,7 @@
*
* This file is released under the GPLv2.
*/
+#define DEBUG
#include <linux/kernel.h>
#include <linux/io.h>
--
2.1.4
More information about the linux-arm-kernel
mailing list