diff mbox

[RFC,6/7] ARM: OMAP2+: PRCM: add support for registering prcm domains from DT

Message ID 1439555652-23012-7-git-send-email-t-kristo@ti.com (mailing list archive)
State New, archived
Headers show

Commit Message

Tero Kristo Aug. 14, 2015, 12:34 p.m. UTC
Power and clock domains can now be registered from DT based layout. Some
data is retained in the kernel for initialization purposes. The platforms
that require DT based domain support, shall have their DT and the
clock/powerdomainxyz_data.c files updated.

The template clock/powerdomain data should be stripped to minimal
amount and use a clkdm_setup / pwrdm_setup struct that consumes less
memory. This patch is just directly using the existing data for
the template purposes.

The domains are also now registered via the generic power domain framework,
and can be used by other generic parts of the kernel also.

Signed-off-by: Tero Kristo <t-kristo@ti.com>
---
 arch/arm/mach-omap2/Kconfig       |    1 +
 arch/arm/mach-omap2/Makefile      |    3 +-
 arch/arm/mach-omap2/clockdomain.h |   10 +-
 arch/arm/mach-omap2/pm-domains.c  |  228 +++++++++++++++++++++++++++++++++++++
 arch/arm/mach-omap2/powerdomain.h |    9 +-
 5 files changed, 243 insertions(+), 8 deletions(-)
 create mode 100644 arch/arm/mach-omap2/pm-domains.c
diff mbox

Patch

diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
index 6468f15..b4a0ce1 100644
--- a/arch/arm/mach-omap2/Kconfig
+++ b/arch/arm/mach-omap2/Kconfig
@@ -24,6 +24,7 @@  config ARCH_OMAP4
 	select ARCH_OMAP2PLUS
 	select ARCH_NEEDS_CPU_IDLE_COUPLED if SMP
 	select ARM_CPU_SUSPEND if PM
+	select PM_GENERIC_DOMAINS if PM
 	select ARM_ERRATA_720789
 	select ARM_GIC
 	select HAVE_ARM_SCU if SMP
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index d424920..1ac9fa7 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -140,7 +140,8 @@  obj-$(CONFIG_SOC_OMAP5)                += voltagedomains54xx_data.o
 obj-$(CONFIG_SOC_DRA7XX)		+= $(voltagedomain-common)
 
 # OMAP powerdomain framework
-powerdomain-common			+= powerdomain.o powerdomain-common.o
+powerdomain-common			+= powerdomain.o powerdomain-common.o \
+					   pm-domains.o
 obj-$(CONFIG_ARCH_OMAP2)		+= $(powerdomain-common)
 obj-$(CONFIG_ARCH_OMAP2)		+= powerdomains2xxx_data.o
 obj-$(CONFIG_ARCH_OMAP2)		+= powerdomains2xxx_3xxx_data.o
diff --git a/arch/arm/mach-omap2/clockdomain.h b/arch/arm/mach-omap2/clockdomain.h
index 77bab5f..d316c4e 100644
--- a/arch/arm/mach-omap2/clockdomain.h
+++ b/arch/arm/mach-omap2/clockdomain.h
@@ -123,7 +123,7 @@  struct omap_hwmod;
  *     definitions (OMAP4 only)
  */
 struct clockdomain {
-	const char *name;
+	char *name;
 	union {
 		const char *name;
 		struct powerdomain *ptr;
@@ -132,9 +132,9 @@  struct clockdomain {
 	const u8 flags;
 	u8 _flags;
 	const u8 dep_bit;
-	const u8 prcm_partition;
-	const u16 cm_inst;
-	const u16 clkdm_offs;
+	u8 prcm_partition;
+	u16 cm_inst;
+	u16 clkdm_offs;
 	struct clkdm_dep *wkdep_srcs;
 	struct clkdm_dep *sleepdep_srcs;
 	int usecount;
@@ -235,4 +235,6 @@  extern struct clkdm_dep gfx_24xx_wkdeps[];
 extern struct clkdm_dep dsp_24xx_wkdeps[];
 extern struct clockdomain wkup_common_clkdm;
 
+int of_omap_clockdomain_init(const struct of_device_id *match);
+
 #endif
diff --git a/arch/arm/mach-omap2/pm-domains.c b/arch/arm/mach-omap2/pm-domains.c
new file mode 100644
index 0000000..b373b21
--- /dev/null
+++ b/arch/arm/mach-omap2/pm-domains.c
@@ -0,0 +1,228 @@ 
+/*
+ * DT based powerdomain support for OMAP2+ SoCs
+ *
+ * Copyright (C) 2015 Texas Instruments, Inc.
+ *   Tero Kristo <t-kristo@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/bug.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/pm_domain.h>
+
+#include "powerdomain.h"
+#include "clockdomain.h"
+#include "prcm-common.h"
+
+#define OMAP_PD_TYPE_PWRDM	0x1
+#define OMAP_PD_TYPE_CLKDM	0x2
+
+struct omap_pm_domain {
+	void *domain;
+	int type;
+	struct generic_pm_domain pd;
+};
+
+#define to_omap_pm_domain(genpd) container_of(genpd, struct omap_pm_domain, pd)
+
+static int _get_offset(struct device_node *node, u16 *val)
+{
+	u32 tmp;
+
+	if (of_property_read_u32(node, "reg", &tmp)) {
+		pr_err("%s: %s does not provide reg addr!\n", __func__,
+		       node->name);
+		return -EINVAL;
+	}
+
+	*val = (u16)tmp;
+
+	return 0;
+}
+
+static int omap_genpd_power_off(struct generic_pm_domain *gen_pd)
+{
+	return 0;
+}
+
+static int omap_genpd_power_on(struct generic_pm_domain *gen_pd)
+{
+	return 0;
+}
+
+int __init of_omap_powerdomain_init(const struct of_device_id *match_table)
+{
+	struct device_node *np;
+	const struct of_device_id *match;
+	struct powerdomain *pd_tmpl, *pd;
+	struct omap_pm_domain *domain;
+	void *arr[2];
+	bool enabled;
+
+	arr[1] = NULL;
+
+	for_each_matching_node_and_match(np, match_table, &match) {
+		pd_tmpl = (struct powerdomain *)match->data;
+
+		pd = kmemdup(pd_tmpl, sizeof(*pd), GFP_KERNEL);
+		if (!pd)
+			return -ENOMEM;
+
+		domain = kzalloc(sizeof(*domain), GFP_KERNEL);
+		if (!domain)
+			return -ENOMEM;
+
+		if (_get_offset(np, &pd->prcm_offs))
+			return -EINVAL;
+
+		pd->name = kstrdup(np->name, GFP_KERNEL);
+
+		domain->domain = pd;
+		domain->type = OMAP_PD_TYPE_PWRDM;
+		domain->pd.name = pd->name;
+		domain->pd.power_off = omap_genpd_power_off;
+		domain->pd.power_on = omap_genpd_power_on;
+
+		pd->prcm_partition =
+			omap_prcm_get_partition(np->parent->parent);
+
+		arr[0] = pd;
+
+		pwrdm_register_pwrdms((struct powerdomain **)arr);
+
+		enabled = pwrdm_read_pwrst(pd) == PWRDM_POWER_ON ? true : false;
+
+		pm_genpd_init(&domain->pd, NULL, !enabled);
+		of_genpd_add_provider_simple(np, &domain->pd);
+		pr_info("%s: registered %s, %08x, part=%d\n", __func__,
+			np->name, (u32)&domain->pd, pd->prcm_partition);
+	}
+
+	return 0;
+}
+
+int __init of_omap_clockdomain_init(const struct of_device_id *match_table)
+{
+	struct device_node *np;
+	const struct of_device_id *match;
+	struct clockdomain *cd_tmpl, *cd;
+	struct of_phandle_args args;
+	void *arr[2];
+	struct omap_pm_domain *domain, *parent_domain;
+	struct generic_pm_domain *gen_pd;
+	struct powerdomain *pd;
+	int num, i;
+	struct clkdm_dep *dep_arr;
+
+	arr[1] = NULL;
+
+	for_each_matching_node_and_match(np, match_table, &match) {
+		cd_tmpl = (struct clockdomain *)match->data;
+
+		cd = kmemdup(cd_tmpl, sizeof(*cd), GFP_KERNEL);
+		if (!cd)
+			return -ENOMEM;
+
+		domain = kzalloc(sizeof(*domain), GFP_KERNEL);
+		if (!domain)
+			return -ENOMEM;
+
+		if (_get_offset(np, &cd->clkdm_offs))
+			return -EINVAL;
+
+		cd->name = kstrdup(np->name, GFP_KERNEL);
+
+		domain->pd.name = cd->name;
+		domain->pd.power_off = omap_genpd_power_off;
+		domain->pd.power_on = omap_genpd_power_on;
+
+		num = of_count_phandle_with_args(np, "power-domains",
+						 "#power-domain-cells");
+
+		pr_info("%s: %s has %d pds defined.\n", __func__, np->name,
+			num);
+
+		if (of_parse_phandle_with_args(np, "power-domains",
+					       "#power-domain-cells", 0,
+					       &args)) {
+			pr_err("%s: %s: missing parent pwrdm.\n", __func__,
+			       np->name);
+			return -EINVAL;
+		}
+
+		gen_pd = of_genpd_get_from_provider(&args);
+		pr_info("%s: %s->%s: parent_gen_pd=%08x\n", __func__, np->name,
+			np->parent->name, (u32)gen_pd);
+		if (!gen_pd)
+			return -EINVAL;
+
+		parent_domain = to_omap_pm_domain(gen_pd);
+
+		pd = parent_domain->domain;
+
+		cd->pwrdm.name = pd->name;
+
+		cd->prcm_partition =
+			omap_prcm_get_partition(np->parent->parent);
+
+		if (cd->prcm_partition != pd->prcm_partition) {
+			/* Different partitions, same inst offset */
+			cd->cm_inst = pd->prcm_offs;
+		} else if (pd->prcm_offs + 0x100 <= cd->clkdm_offs) {
+			/* Same partition, clkdm inst offset + 0x100 */
+			cd->cm_inst = pd->prcm_offs + 0x100;
+		} else {
+			/*
+			 * Same partition, but offset so close we must
+			 * use same instance.
+			 */
+			cd->cm_inst = pd->prcm_offs;
+		}
+
+		/* fix clkdm offset based on instance offset */
+		cd->clkdm_offs -= cd->cm_inst;
+
+		domain->domain = cd;
+		domain->type = OMAP_PD_TYPE_CLKDM;
+
+		if (num > 1) {
+			dep_arr = kcalloc(num, sizeof(*dep_arr), GFP_KERNEL);
+			cd->sleepdep_srcs = dep_arr;
+			cd->wkdep_srcs = dep_arr;
+
+			for (i = 1; i < num; i++) {
+				if (of_parse_phandle_with_args(np, "power-domains",
+							       "#power-domain-cells",
+							       i, &args)) {
+					pr_err("parsing of clkdm dep %d failed for %s\n", i, np->name);
+					return -EINVAL;
+				}
+
+				dep_arr[i - 1].clkdm_name = args.np->name;
+
+				pr_info("%s: %s: dep[%d] = %s\n", __func__,
+					np->name, i - 1, args.np->name);
+			}
+		}
+
+		arr[0] = cd;
+
+		clkdm_register_clkdms((struct clockdomain **)arr);
+
+		/* During boot, all clockdomains are basically idle */
+		pm_genpd_init(&domain->pd, NULL, true);
+		of_genpd_add_provider_simple(np, &domain->pd);
+		pm_genpd_add_subdomain(gen_pd, &domain->pd);
+		pr_info("%s: registered %s, %08x, part=%d, inst=%04x, offs=%04x\n", __func__,
+			np->name, (u32)&domain->pd, cd->prcm_partition,
+			cd->cm_inst, cd->clkdm_offs);
+	}
+
+	return 0;
+}
diff --git a/arch/arm/mach-omap2/powerdomain.h b/arch/arm/mach-omap2/powerdomain.h
index 28a796c..d5ee948 100644
--- a/arch/arm/mach-omap2/powerdomain.h
+++ b/arch/arm/mach-omap2/powerdomain.h
@@ -75,6 +75,7 @@ 
 struct clockdomain;
 struct powerdomain;
 struct voltagedomain;
+struct of_device_id;
 
 /**
  * struct powerdomain - OMAP powerdomain
@@ -110,19 +111,19 @@  struct voltagedomain;
  * @prcm_partition possible values are defined in mach-omap2/prcm44xx.h.
  */
 struct powerdomain {
-	const char *name;
+	char *name;
 	union {
 		const char *name;
 		struct voltagedomain *ptr;
 	} voltdm;
-	const s16 prcm_offs;
+	s16 prcm_offs;
 	const u8 pwrsts;
 	const u8 pwrsts_logic_ret;
 	const u8 flags;
 	const u8 banks;
 	const u8 pwrsts_mem_ret[PWRDM_MAX_MEM_BANKS];
 	const u8 pwrsts_mem_on[PWRDM_MAX_MEM_BANKS];
-	const u8 prcm_partition;
+	u8 prcm_partition;
 	struct clockdomain *pwrdm_clkdms[PWRDM_MAX_CLKDMS];
 	struct list_head node;
 	struct list_head voltdm_node;
@@ -273,4 +274,6 @@  extern struct powerdomain gfx_omap2_pwrdm;
 extern void pwrdm_lock(struct powerdomain *pwrdm);
 extern void pwrdm_unlock(struct powerdomain *pwrdm);
 
+int of_omap_powerdomain_init(const struct of_device_id *match);
+
 #endif