linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v11 0/3] ARM: rk3288: Add PM Domain support
@ 2014-11-12 22:01 Caesar Wang
  2014-11-12 22:01 ` [PATCH v11 1/3] dt-bindings: add document of Rockchip power domain Caesar Wang
                   ` (2 more replies)
  0 siblings, 3 replies; 10+ messages in thread
From: Caesar Wang @ 2014-11-12 22:01 UTC (permalink / raw)
  To: linus.walleij, linux-arm-kernel, Heiko Stuebner, Russell King
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Grant Likely, linux-kernel, devicetree, Randy Dunlap, linux-doc,
	dianders, linux-rockchip, Ulf Hansson, Dmitry Torokhov, fzf, cf,
	caesar.wang, jinkun.hong, Jack Dai

    Add power domain drivers based on generic power domain for
    Rockchip platform, and support RK3288.

    https://chromium-review.googlesource.com/#/c/220253/9
    This is the GPU driver, add the following information in DT,
    and it can support the PMDOMAIN

gpu: gpu@ffa30000 {
    compatible = "arm,malit764",
                 "arm,malit76x",
                 "arm,malit7xx",
                 "arm,mali-midgard";
    reg = <0xffa30000 0x10000>;
    interrupts = <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>,
                 <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>,
                 <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>;
    interrupt-names = "JOB", "MMU", "GPU";
    clocks = <&cru ACLK_GPU>;
    clock-names = "aclk_gpu";
    operating-points = <
                /* KHz uV */
                100000 800000
                200000 850000
                300000 950000
                400000 1000000
                600000 1150000
    >;
    power-domains = <&power RK3288_PD_GPU>;
    status = "disabled";
};

Based on:
    - [PATCH v1 1/4] PM / clock_ops: Add pm_clk_add_clk()
    http://www.mail-archive.com/linux-kernel@vger.kernel.org/msg735599.html

Changes in v11:
    - fix pm_genpd_init(&pd->genpd, NULL, false);

Changes in v10:
    - this switches over domain infos to use masks instead of recomputing
      them each time and also gets rid of custom domain translator and
      uses standard onecell on.
    - fix missing the #include <dt-bindings/power-domain/rk3288.h>
    - remove the notes

Changes in v9:
    - add document decription
    - fix v8 changes as follows:
    - This reconciles the v2 and v7 code so that we power domain have
      lists of clocks they toggle on and off during power transitions and
      independently from power domains clocks we attach clocks to devices
      comprising power domain and prepare them so they are turn on and off
      by runtime PM.
    - add rockchip_pm_add_one_domain() to control domains.
    - add pd_start/pd_stop interface to control clocks
    - add decription for power-doamin node

Changes in v8:
    - document go back to v2
    - This reconciles the v2 and v7 code so that we power domain have
      lists of clocks they toggle on and off during power transitions and
      independently from power domains clocks we attach clocks to devices
      comprising power domain and prepare them so they are turn on and off
      by runtime PM.
    - DTS go back to v2

Changes in v7:
    - Delete unused variables

Changes in v6:
    - delete pmu_lock
    - modify dev_lock using mutex
    - pm_clk_resume(pd->dev) change to pm_clk_resume(ed->dev)
    - pm_clk_suspend(pd->dev) change to pm_clk_suspend(ed->dev)
    - add devm_kfree(pd->dev, de) in rockchip_pm_domain_detach_dev

Changes in v5:
    - delete idle_lock
    - add timeout in rockchip_pmu_set_idle_request()

Changes in v4:
    - use list storage dev

Changes in v3:
    - DT structure has changed
    - change use pm_clk_resume() and pm_clk_suspend()
    - Decomposition power-controller, changed to multiple controller
    (gpu-power-controller, hevc-power-controller)

Changes in v2:
    - move clocks to "optional"
    - remove the "pd->pd.of_node = np"
    - make pd_vio clocks all one entry per line and alphabetize.
    - power: power-controller move back to pinctrl: pinctrl.

Caesar Wang (3):
  dt-bindings: add document of Rockchip power domain
  power-domain: rockchip: add power doamin driver
  ARM: dts: add rk3288 power-domain node

 .../bindings/arm/rockchip/power_domain.txt         |  48 +++
 arch/arm/boot/dts/rk3288.dtsi                      |  66 +++
 arch/arm/mach-rockchip/Kconfig                     |   1 +
 arch/arm/mach-rockchip/Makefile                    |   1 +
 arch/arm/mach-rockchip/pm_domains.c                | 471 +++++++++++++++++++++
 include/dt-bindings/power-domain/rk3288.h          |  11 +
 6 files changed, 598 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/arm/rockchip/power_domain.txt
 create mode 100644 arch/arm/mach-rockchip/pm_domains.c
 create mode 100644 include/dt-bindings/power-domain/rk3288.h

-- 
1.9.1



^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH v11 1/3] dt-bindings: add document of Rockchip power domain
  2014-11-12 22:01 [PATCH v11 0/3] ARM: rk3288: Add PM Domain support Caesar Wang
@ 2014-11-12 22:01 ` Caesar Wang
  2014-11-12 22:01 ` [PATCH v11 2/3] power-domain: rockchip: add power doamin driver Caesar Wang
  2014-11-12 22:01 ` [PATCH v11 3/3] ARM: dts: add rk3288 power-domain node Caesar Wang
  2 siblings, 0 replies; 10+ messages in thread
From: Caesar Wang @ 2014-11-12 22:01 UTC (permalink / raw)
  To: linus.walleij, linux-arm-kernel, Heiko Stuebner, Russell King
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Grant Likely, linux-kernel, devicetree, Randy Dunlap, linux-doc,
	dianders, linux-rockchip, Ulf Hansson, Dmitry Torokhov, fzf, cf,
	caesar.wang, Jack Dai, jinkun.hong

This add the necessary binding documentation for the power domain
found on Rockchip Socs.

Signed-off-by: Jack Dai <jack.dai@rock-chips.com>
Signed-off-by: jinkun.hong <jinkun.hong@rock-chips.com>
Signed-off-by: Caesar Wang <caesar.wang@rock-chips.com>

---

Changes in v11: None
Changes in v10: None
Changes in v9:
    - add document decription

Changes in v8:
    - document go back to v2

Changes in v7: None
Changes in v6: None
Changes in v5: None
Changes in v4: None
Changes in v3:
    - DT structure has changed

Changes in v2:
    - move clocks to "optional"

 .../bindings/arm/rockchip/power_domain.txt         | 48 ++++++++++++++++++++++
 1 file changed, 48 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/arm/rockchip/power_domain.txt

diff --git a/Documentation/devicetree/bindings/arm/rockchip/power_domain.txt b/Documentation/devicetree/bindings/arm/rockchip/power_domain.txt
new file mode 100644
index 0000000..3e74e6d
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/rockchip/power_domain.txt
@@ -0,0 +1,48 @@
+* Rockchip Power Domains
+
+Rockchip processors include support for multiple power domains which can be
+powered up/down by software based on different application scenes to save power.
+
+Required properties for power domain controller:
+- compatible: should be one of the following.
+    * rockchip,rk3288-power-controller - for rk3288 type power domain.
+- #power-domain-cells: Number of cells in a power-domain specifier.
+		       should be 1.
+- rockchip,pmu: phandle referencing a syscon providing the pmu registers
+- #address-cells: should be 1.
+- #size-cells: should be 0.
+
+Required properties for power domain sub nodes:
+- reg: index of the power domain, should use macros in:
+    *  include/dt-bindings/power-domain/rk3288.h - for rk3288 type power domain.
+- clocks (optional): phandles to clocks which need to be enabled while power domain
+          switches state.
+
+Example:
+
+	power: power-controller {
+	       compatible = "rockchip,rk3288-power-controller";
+	       #power-domain-cells = <1>;
+	       rockchip,pmu = <&pmu>;
+               #address-cells = <1>;
+               #size-cells = <0>;
+
+	       pd_gpu {
+	               reg = <RK3288_PD_GPU>;
+	               clocks = <&cru ACLK_GPU>;
+	       };
+	};
+
+Node of a device using power domains must have a power-domains property,
+containing a phandle to the power device node and an index specifying which
+power domain to use.
+The index should use macros in:
+   * include/dt-bindings/power-domain/rk3288.h - for rk3288 type power domain.
+
+Example of the node using power domain:
+
+	node {
+		/* ... */
+		power-domains = <&power RK3288_PD_GPU>;
+		/* ... */
+	};
-- 
1.9.1



^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v11 2/3] power-domain: rockchip: add power doamin driver
  2014-11-12 22:01 [PATCH v11 0/3] ARM: rk3288: Add PM Domain support Caesar Wang
  2014-11-12 22:01 ` [PATCH v11 1/3] dt-bindings: add document of Rockchip power domain Caesar Wang
@ 2014-11-12 22:01 ` Caesar Wang
  2014-11-13 17:29   ` Doug Anderson
                     ` (2 more replies)
  2014-11-12 22:01 ` [PATCH v11 3/3] ARM: dts: add rk3288 power-domain node Caesar Wang
  2 siblings, 3 replies; 10+ messages in thread
From: Caesar Wang @ 2014-11-12 22:01 UTC (permalink / raw)
  To: linus.walleij, linux-arm-kernel, Heiko Stuebner, Russell King
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Grant Likely, linux-kernel, devicetree, Randy Dunlap, linux-doc,
	dianders, linux-rockchip, Ulf Hansson, Dmitry Torokhov, fzf, cf,
	caesar.wang, Jack Dai, jinkun.hong

In order to meet high performance and low power requirements, a power
management unit is designed or saving power when RK3288 in low power mode.
The RK3288 PMU is dedicated for managing the power ot the whole chip.

Signed-off-by: Jack Dai <jack.dai@rock-chips.com>
Signed-off-by: jinkun.hong <jinkun.hong@rock-chips.com>
Signed-off-by: Caesar Wang <caesar.wang@rock-chips.com>

---

Changes in v11:
    - fix pm_genpd_init(&pd->genpd, NULL, false);

Changes in v10:
    - this switches over domain infos to use masks instead of recomputing
      them each time and also gets rid of custom domain translator and
      uses standard onecell on.

Changes in v9:
    - fix v8 changes as follows:
    - This reconciles the v2 and v7 code so that we power domain have
      lists of clocks they toggle on and off during power transitions and
      independently from power domains clocks we attach clocks to devices
      comprising power domain and prepare them so they are turn on and off
      by runtime PM.
    - add rockchip_pm_add_one_domain() to control domains.
    - add pd_start/pd_stop interface to control clocks

Changes in v8:
    - This reconciles the v2 and v7 code so that we power domain have
      lists of clocks they toggle on and off during power transitions and
      independently from power domains clocks we attach clocks to devices
      comprising power domain and prepare them so they are turn on and off
      by runtime PM.

Changes in v7:
    - Delete unused variables

Changes in v6:
    - delete pmu_lock
    - modify dev_lock using mutex
    - pm_clk_resume(pd->dev) change to pm_clk_resume(ed->dev)
    - pm_clk_suspend(pd->dev) change to pm_clk_suspend(ed->dev)
    - add devm_kfree(pd->dev, de) in rockchip_pm_domain_detach_dev

Changes in v5:
    - delete idle_lock
    - add timeout in rockchip_pmu_set_idle_request()

Changes in v4:
    - use list storage dev

Changes in v3:
    - change use pm_clk_resume() and pm_clk_suspend()

Changes in v2:
    - remove the "pd->pd.of_node = np"

 arch/arm/mach-rockchip/Kconfig            |   1 +
 arch/arm/mach-rockchip/Makefile           |   1 +
 arch/arm/mach-rockchip/pm_domains.c       | 471 ++++++++++++++++++++++++++++++
 include/dt-bindings/power-domain/rk3288.h |  11 +
 4 files changed, 484 insertions(+)
 create mode 100644 arch/arm/mach-rockchip/pm_domains.c
 create mode 100644 include/dt-bindings/power-domain/rk3288.h

diff --git a/arch/arm/mach-rockchip/Kconfig b/arch/arm/mach-rockchip/Kconfig
index ac5803c..d033993 100644
--- a/arch/arm/mach-rockchip/Kconfig
+++ b/arch/arm/mach-rockchip/Kconfig
@@ -13,6 +13,7 @@ config ARCH_ROCKCHIP
 	select DW_APB_TIMER_OF
 	select ARM_GLOBAL_TIMER
 	select CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK
+	select PM_GENERIC_DOMAINS if PM
 	help
 	  Support for Rockchip's Cortex-A9 Single-to-Quad-Core-SoCs
 	  containing the RK2928, RK30xx and RK31xx series.
diff --git a/arch/arm/mach-rockchip/Makefile b/arch/arm/mach-rockchip/Makefile
index b29d8ea..17ea082 100644
--- a/arch/arm/mach-rockchip/Makefile
+++ b/arch/arm/mach-rockchip/Makefile
@@ -1,4 +1,5 @@
 CFLAGS_platsmp.o := -march=armv7-a
 
 obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip.o
+obj-$(CONFIG_PM_GENERIC_DOMAINS) += pm_domains.o
 obj-$(CONFIG_SMP) += headsmp.o platsmp.o
diff --git a/arch/arm/mach-rockchip/pm_domains.c b/arch/arm/mach-rockchip/pm_domains.c
new file mode 100644
index 0000000..00730eb
--- /dev/null
+++ b/arch/arm/mach-rockchip/pm_domains.c
@@ -0,0 +1,471 @@
+/*
+ * Rockchip Generic power domain support.
+ *
+ * Copyright (c) 2014 ROCKCHIP, Co. Ltd.
+ *
+ * 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/module.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/pm_clock.h>
+#include <linux/pm_domain.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <dt-bindings/power-domain/rk3288.h>
+
+struct rockchip_domain_info {
+	int pwr_mask;
+	int status_mask;
+	int req_mask;
+	int idle_mask;
+	int ack_mask;
+};
+
+struct rockchip_pmu_info {
+	u32 pwr_offset;
+	u32 status_offset;
+	u32 req_offset;
+	u32 idle_offset;
+	u32 ack_offset;
+
+	int num_domains;
+	const struct rockchip_domain_info *domain_info;
+};
+
+struct rockchip_pm_domain {
+	struct generic_pm_domain genpd;
+	const struct rockchip_domain_info *info;
+	struct rockchip_pmu *pmu;
+	int num_clks;
+	struct clk *clks[];
+};
+
+struct rockchip_pmu {
+	struct device *dev;
+	struct regmap *regmap;
+	const struct rockchip_pmu_info *info;
+	struct mutex mutex; /* mutex lock for pmu */
+	struct genpd_onecell_data genpd_data;
+	struct generic_pm_domain *domains[];
+};
+
+#define to_rockchip_pd(gpd) container_of(gpd, struct rockchip_pm_domain, genpd)
+
+#define DOMAIN(pwr, status, req, idle, ack)	\
+{						\
+	.pwr_mask = BIT(pwr),			\
+	.status_mask = BIT(status),		\
+	.req_mask = BIT(req),			\
+	.idle_mask = BIT(idle),			\
+	.ack_mask = BIT(ack),			\
+}
+
+#define DOMAIN_RK3288(pwr, status, req)		\
+	DOMAIN(pwr, status, req, req, (req) + 16)
+
+static bool rockchip_pmu_domain_is_idle(struct rockchip_pm_domain *pd)
+{
+	struct rockchip_pmu *pmu = pd->pmu;
+	const struct rockchip_domain_info *pd_info = pd->info;
+	unsigned int val;
+
+	regmap_read(pmu->regmap, pmu->info->idle_offset, &val);
+	return (val & pd_info->idle_mask) == pd_info->idle_mask;
+}
+
+static int rockchip_pmu_set_idle_request(struct rockchip_pm_domain *pd,
+					 bool idle)
+{
+	const struct rockchip_domain_info *pd_info = pd->info;
+	struct rockchip_pmu *pmu = pd->pmu;
+	unsigned int val;
+
+	regmap_update_bits(pmu->regmap, pmu->info->req_offset,
+			   pd_info->req_mask, idle ? -1U : 0);
+
+	dsb();
+
+	do {
+		regmap_read(pmu->regmap, pmu->info->ack_offset, &val);
+	} while ((val & pd_info->ack_mask) != (idle ? pd_info->ack_mask : 0));
+
+	while (rockchip_pmu_domain_is_idle(pd) != idle)
+		cpu_relax();
+
+	return 0;
+}
+
+static bool rockchip_pmu_domain_is_on(struct rockchip_pm_domain *pd)
+{
+	struct rockchip_pmu *pmu = pd->pmu;
+	unsigned int val;
+
+	regmap_read(pmu->regmap, pmu->info->status_offset, &val);
+
+	/* 1'b0: power on, 1'b1: power off */
+	return !(val & pd->info->status_mask);
+}
+
+static void rockchip_do_pmu_set_power_domain(struct rockchip_pm_domain *pd,
+					     bool on)
+{
+	struct rockchip_pmu *pmu = pd->pmu;
+
+	regmap_update_bits(pmu->regmap, pmu->info->pwr_offset,
+			   pd->info->pwr_mask, on ? 0 : -1U);
+
+	dsb();
+
+	while (rockchip_pmu_domain_is_on(pd) != on)
+		cpu_relax();
+}
+
+static int rockchip_pd_power(struct rockchip_pm_domain *pd, bool power_on)
+{
+	int i;
+
+	mutex_lock(&pd->pmu->mutex);
+
+	if (rockchip_pmu_domain_is_on(pd) != power_on) {
+		for (i = 0; i < pd->num_clks; i++)
+			clk_enable(pd->clks[i]);
+
+		if (!power_on) {
+			/* FIXME: add code to save AXI_QOS */
+
+			/* if powering down, idle request to NIU first */
+			rockchip_pmu_set_idle_request(pd, true);
+		}
+
+		rockchip_do_pmu_set_power_domain(pd, power_on);
+
+		if (power_on) {
+			/* if powering up, leave idle mode */
+			rockchip_pmu_set_idle_request(pd, false);
+
+			/* FIXME: add code to restore AXI_QOS */
+		}
+
+		for (i = pd->num_clks - 1; i >= 0; i--)
+			clk_disable(pd->clks[i]);
+	}
+
+	mutex_unlock(&pd->pmu->mutex);
+	return 0;
+}
+
+static int rockchip_pd_power_on(struct generic_pm_domain *domain)
+{
+	struct rockchip_pm_domain *pd = to_rockchip_pd(domain);
+
+	return rockchip_pd_power(pd, true);
+}
+
+static int rockchip_pd_power_off(struct generic_pm_domain *domain)
+{
+	struct rockchip_pm_domain *pd = to_rockchip_pd(domain);
+
+	return rockchip_pd_power(pd, false);
+}
+
+static void rockchip_pd_attach_dev(struct device *dev)
+{
+	struct generic_pm_domain *genpd = pd_to_genpd(dev->pm_domain);
+	struct clk *clk;
+	int i;
+	int error;
+
+	dev_dbg(dev, "attaching to power domain '%s'\n", genpd->name);
+
+	error = pm_clk_create(dev);
+	if (error) {
+		dev_err(dev, "pm_clk_create failed %d\n", error);
+		return;
+	}
+
+	while ((clk = of_clk_get(dev->of_node, i++)) && !IS_ERR(clk)) {
+		error = pm_clk_add_clk(dev, clk);
+		clk_put(clk);
+		if (error) {
+			dev_err(dev, "pm_clk_add_clk failed %d\n", error);
+			pm_clk_destroy(dev);
+			return;
+		}
+		dev_dbg(dev, "added clock '%s' to list of PM clocks\n",
+			__clk_get_name(clk));
+	}
+}
+
+static void rockchip_pd_detach_dev(struct device *dev)
+{
+	struct generic_pm_domain *genpd = pd_to_genpd(dev->pm_domain);
+
+	dev_dbg(dev, "detaching from power domain %s\n", genpd->name);
+
+	pm_clk_destroy(dev);
+}
+
+static int rockchip_pd_start_dev(struct device *dev)
+{
+	struct generic_pm_domain *genpd = pd_to_genpd(dev->pm_domain);
+
+	dev_dbg(dev, "starting device in power domain '%s'\n", genpd->name);
+
+	return pm_clk_resume(dev);
+}
+
+static int rockchip_pd_stop_dev(struct device *dev)
+{
+	struct generic_pm_domain *genpd = pd_to_genpd(dev->pm_domain);
+
+	dev_dbg(dev, "stopping device in power domain '%s'\n", genpd->name);
+
+	return pm_clk_suspend(dev);
+}
+
+static int rockchip_pm_add_one_domain(struct rockchip_pmu *pmu,
+				      struct device_node *node)
+{
+	const struct rockchip_domain_info *pd_info;
+	struct rockchip_pm_domain *pd;
+	struct clk *clk;
+	int clk_cnt;
+	int i;
+	u32 id;
+	bool is_on;
+	int error;
+
+	error = of_property_read_u32(node, "reg", &id);
+	if (error) {
+		dev_err(pmu->dev,
+			"%s: failed to retrieve domain id (reg): %d\n",
+			node->name, error);
+		return -EINVAL;
+	}
+
+	if (id >= pmu->info->num_domains) {
+		dev_err(pmu->dev, "%s: invalid domain id %d\n",
+			node->name, id);
+		return -EINVAL;
+	}
+
+	pd_info = &pmu->info->domain_info[id];
+	if (!pd_info) {
+		dev_err(pmu->dev, "%s: undefined domain id %d\n",
+			node->name, id);
+		return -EINVAL;
+	}
+
+	clk_cnt = of_count_phandle_with_args(node, "clocks", "#clock-cells");
+	pd = devm_kzalloc(pmu->dev,
+			  sizeof(*pd) + clk_cnt * sizeof(pd->clks[0]),
+			  GFP_KERNEL);
+	if (!pd)
+		return -ENOMEM;
+
+	pd->genpd.name = node->name;
+	pd->genpd.power_off = rockchip_pd_power_off;
+	pd->genpd.power_on = rockchip_pd_power_on;
+	pd->genpd.attach_dev = rockchip_pd_attach_dev;
+	pd->genpd.detach_dev = rockchip_pd_detach_dev;
+	pd->genpd.dev_ops.start = rockchip_pd_start_dev;
+	pd->genpd.dev_ops.stop = rockchip_pd_stop_dev;
+
+	pd->info = pd_info;
+	pd->pmu = pmu;
+	pd->num_clks = clk_cnt;
+
+	is_on = rockchip_pmu_domain_is_on(pd);
+	pm_genpd_init(&pd->genpd, NULL, !is_on);
+
+	for (i = 0; i < clk_cnt; i++) {
+		clk = of_clk_get(node, i);
+		if (IS_ERR(clk)) {
+			error = PTR_ERR(clk);
+			dev_err(pmu->dev,
+				"%s: failed to get clk %s (index %d): %d\n",
+				node->name, __clk_get_name(clk), i, error);
+			goto err_out;
+		}
+
+		error = clk_prepare(clk);
+		if (error) {
+			dev_err(pmu->dev,
+				"%s: failed to prepare clk %s (index %d): %d\n",
+				node->name, __clk_get_name(clk), i, error);
+			clk_put(clk);
+			goto err_out;
+		}
+
+		pd->clks[i] = clk;
+
+		dev_dbg(pmu->dev, "added clock '%s' to domain '%s'\n",
+			__clk_get_name(clk), node->name);
+	}
+
+	pmu->genpd_data.domains[id] = &pd->genpd;
+	return 0;
+
+err_out:
+	while (--i >= 0) {
+		clk_unprepare(pd->clks[i]);
+		clk_put(pd->clks[i]);
+	}
+	return error;
+}
+
+static void rockchip_pm_remove_one_domain(struct rockchip_pm_domain *pd)
+{
+	int i;
+
+	for (i = 0; i < pd->num_clks; i++) {
+		clk_unprepare(pd->clks[i]);
+		clk_put(pd->clks[i]);
+	}
+
+	/* devm will free our memory */
+}
+
+static void rockchip_pm_domain_cleanup(struct rockchip_pmu *pmu)
+{
+	struct generic_pm_domain *genpd;
+	struct rockchip_pm_domain *pd;
+	int i;
+
+	for (i = 0; i < pmu->genpd_data.num_domains; i++) {
+		genpd = pmu->genpd_data.domains[i];
+		if (genpd) {
+			pd = to_rockchip_pd(genpd);
+			rockchip_pm_remove_one_domain(pd);
+		}
+	}
+
+	/* devm will free our memory */
+}
+
+static int rockchip_pm_domain_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	struct device_node *node;
+	struct rockchip_pmu *pmu;
+	const struct of_device_id *match;
+	const struct rockchip_pmu_info *pmu_info;
+	int error;
+
+	if (!np) {
+		dev_err(dev, "device tree node not found\n");
+		return -ENXIO;
+	}
+
+	match = of_match_device(dev->driver->of_match_table, dev);
+	if (!match || !match->data) {
+		dev_err(dev, "missing pmu data\n");
+		return -EINVAL;
+	}
+
+	pmu_info = match->data;
+
+	pmu = devm_kzalloc(dev,
+			   sizeof(*pmu) +
+				pmu_info->num_domains * sizeof(pmu->domains[0]),
+			   GFP_KERNEL);
+	if (!pmu)
+		return -ENOMEM;
+
+	pmu->dev = &pdev->dev;
+	mutex_init(&pmu->mutex);
+
+	pmu->info = pmu_info;
+
+	pmu->genpd_data.domains = pmu->domains;
+	pmu->genpd_data.num_domains = pmu_info->num_domains;
+
+	node = of_parse_phandle(np, "rockchip,pmu", 0);
+	pmu->regmap = syscon_node_to_regmap(node);
+	of_node_put(node);
+	if (IS_ERR(pmu->regmap)) {
+		error = PTR_ERR(pmu->regmap);
+		dev_err(dev, "failed to get PMU regmap: %d\n", error);
+		return error;
+	}
+
+	error = -ENXIO;
+
+	for_each_available_child_of_node(np, node) {
+		error = rockchip_pm_add_one_domain(pmu, node);
+		if (error) {
+			dev_err(dev, "failed to handle node %s: %d\n",
+				node->name, error);
+			goto err_out;
+		}
+	}
+
+	if (error) {
+		dev_dbg(dev, "no power domains defined\n");
+		goto err_out;
+	}
+
+	of_genpd_add_provider_onecell(np, &pmu->genpd_data);
+
+	return 0;
+
+err_out:
+	rockchip_pm_domain_cleanup(pmu);
+	return error;
+}
+
+static const struct rockchip_domain_info rk3288_pm_domains[] = {
+	[RK3288_PD_GPU]		= DOMAIN_RK3288(9, 9, 2),
+	[RK3288_PD_VIO]		= DOMAIN_RK3288(7, 7, 4),
+	[RK3288_PD_VIDEO]	= DOMAIN_RK3288(8, 8, 3),
+	[RK3288_PD_HEVC]	= DOMAIN_RK3288(14, 10, 9),
+};
+
+static const struct rockchip_pmu_info rk3288_pmu = {
+	.pwr_offset = 0x08,
+	.status_offset = 0x0c,
+	.req_offset = 0x10,
+	.idle_offset = 0x14,
+	.ack_offset = 0x14,
+	.num_domains = ARRAY_SIZE(rk3288_pm_domains),
+	.domain_info = rk3288_pm_domains,
+};
+
+static const struct of_device_id rockchip_pm_domain_dt_match[] = {
+	{
+		.compatible = "rockchip,rk3288-power-controller",
+		.data = (void *)&rk3288_pmu,
+	},
+	{ /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, rockchip_pm_domain_dt_match);
+
+static struct platform_driver rockchip_pm_domain_driver = {
+	.probe = rockchip_pm_domain_probe,
+	.driver = {
+		.name   = "rockchip-pm-domain",
+		.owner  = THIS_MODULE,
+		.of_match_table = rockchip_pm_domain_dt_match,
+		/*
+		 * We can't forcibly eject devices form power domain,
+		 * so we can't really remove power domains once they
+		 * were added.
+		 */
+		.suppress_bind_attrs = true,
+	},
+};
+
+static int __init rockchip_pm_domain_drv_register(void)
+{
+	return platform_driver_register(&rockchip_pm_domain_driver);
+}
+postcore_initcall(rockchip_pm_domain_drv_register);
diff --git a/include/dt-bindings/power-domain/rk3288.h b/include/dt-bindings/power-domain/rk3288.h
new file mode 100644
index 0000000..ca68c11
--- /dev/null
+++ b/include/dt-bindings/power-domain/rk3288.h
@@ -0,0 +1,11 @@
+#ifndef __DT_BINDINGS_POWER_DOMAIN_RK3288_H__
+#define __DT_BINDINGS_POWER_DOMAIN_RK3288_H__
+
+/* RK3288 power domain index */
+#define RK3288_PD_GPU          0
+#define RK3288_PD_VIO          1
+#define RK3288_PD_VIDEO        2
+#define RK3288_PD_HEVC         3
+#define RK3288_PD_PERI         4
+
+#endif
-- 
1.9.1



^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v11 3/3] ARM: dts: add rk3288 power-domain node
  2014-11-12 22:01 [PATCH v11 0/3] ARM: rk3288: Add PM Domain support Caesar Wang
  2014-11-12 22:01 ` [PATCH v11 1/3] dt-bindings: add document of Rockchip power domain Caesar Wang
  2014-11-12 22:01 ` [PATCH v11 2/3] power-domain: rockchip: add power doamin driver Caesar Wang
@ 2014-11-12 22:01 ` Caesar Wang
  2014-11-13 17:33   ` Doug Anderson
  2 siblings, 1 reply; 10+ messages in thread
From: Caesar Wang @ 2014-11-12 22:01 UTC (permalink / raw)
  To: linus.walleij, linux-arm-kernel, Heiko Stuebner, Russell King
  Cc: Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Grant Likely, linux-kernel, devicetree, Randy Dunlap, linux-doc,
	dianders, linux-rockchip, Ulf Hansson, Dmitry Torokhov, fzf, cf,
	caesar.wang, Jack Dai, jinkun.hong

This patch add the needed clocks into power-controller.

why need we do so that?

Firstly, we always be needed turn off clocks to save power when
the system enter suspend.So we need to enumerate the clocks are needed
to switch power doamin no and off.

Secondly, Rk3288 reset circuit should be syncchronous reset and
then sync revoked.so we need to enable clocks of all devices.

Signed-off-by: Jack Dai <jack.dai@rock-chips.com>
Signed-off-by: jinkun.hong <jinkun.hong@rock-chips.com>
Signed-off-by: Caesar Wang <caesar.wang@rock-chips.com>

---

Changes in v11: None
Changes in v10:
    - fix missing the #include <dt-bindings/power-domain/rk3288.h>
    - remove the notes

Changes in v9:
    - add decription for power-doamin node

Changes in v8:
    - DTS go back to v2

Changes in v7: None
Changes in v6: None
Changes in v5: None
Changes in v4: None
Changes in v3:
    - Decomposition power-controller, changed to multiple controller
    (gpu-power-controller, hevc-power-controller)

Changes in v2:
    - make pd_vio clocks all one entry per line and alphabetize.
    - power: power-controller move back to pinctrl: pinctrl.

 arch/arm/boot/dts/rk3288.dtsi | 66 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 66 insertions(+)

diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi
index cb18bb4..4e64fb2 100644
--- a/arch/arm/boot/dts/rk3288.dtsi
+++ b/arch/arm/boot/dts/rk3288.dtsi
@@ -15,6 +15,7 @@
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 #include <dt-bindings/pinctrl/rockchip.h>
 #include <dt-bindings/clock/rk3288-cru.h>
+#include <dt-bindings/power-domain/rk3288.h>
 #include "skeleton.dtsi"
 
 / {
@@ -989,4 +990,69 @@
 			};
 		};
 	};
+
+	power: power-controller {
+		compatible = "rockchip,rk3288-power-controller";
+		#power-domain-cells = <1>;
+		rockchip,pmu = <&pmu>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		pd_gpu {
+			reg = <RK3288_PD_GPU>;
+			clocks = <&cru ACLK_GPU>;
+		};
+
+		pd_hevc {
+			reg = <RK3288_PD_HEVC>;
+			clocks = <&cru ACLK_HEVC>,
+				 <&cru SCLK_HEVC_CABAC>,
+				 <&cru SCLK_HEVC_CORE>,
+				 <&cru HCLK_HEVC>;
+		};
+
+		pd_vio {
+			reg = <RK3288_PD_VIO>;
+			clocks = <&cru ACLK_IEP>,
+				 <&cru ACLK_ISP>,
+				 <&cru ACLK_RGA_NIU>,
+				 <&cru ACLK_RGA>,
+				 <&cru ACLK_VIO0_NIU>,
+				 <&cru ACLK_VIO1_NIU>,
+				 <&cru ACLK_VIP>,
+				 <&cru ACLK_VOP0>,
+				 <&cru ACLK_VOP1>,
+				 <&cru DCLK_VOP0>,
+				 <&cru DCLK_VOP1>,
+				 <&cru HCLK_IEP>,
+				 <&cru HCLK_ISP>,
+				 <&cru HCLK_RGA>,
+				 <&cru HCLK_VIO_AHB_ARBI>,
+				 <&cru HCLK_VIO_NIU>,
+				 <&cru HCLK_VIO2_H2P>,
+				 <&cru HCLK_VIP>,
+				 <&cru HCLK_VOP0>,
+				 <&cru HCLK_VOP1>,
+				 <&cru PCLK_EDP_CTRL>,
+				 <&cru PCLK_HDMI_CTRL>,
+				 <&cru PCLK_LVDS_PHY>,
+				 <&cru PCLK_MIPI_CSI>,
+				 <&cru PCLK_MIPI_DSI0>,
+				 <&cru PCLK_MIPI_DSI1>,
+				 <&cru PCLK_VIO2_H2P>,
+				 <&cru SCLK_EDP_24M>,
+				 <&cru SCLK_EDP>,
+				 <&cru SCLK_HDMI_CEC>,
+				 <&cru SCLK_HDMI_HDCP>,
+				 <&cru SCLK_ISP_JPE>,
+				 <&cru SCLK_ISP>,
+				 <&cru SCLK_RGA>;
+		};
+
+		pd_video {
+			reg = <RK3288_PD_VIDEO>;
+			clocks = <&cru ACLK_VCODEC>,
+				 <&cru HCLK_VCODEC>;
+		};
+	};
 };
-- 
1.9.1



^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH v11 2/3] power-domain: rockchip: add power doamin driver
  2014-11-12 22:01 ` [PATCH v11 2/3] power-domain: rockchip: add power doamin driver Caesar Wang
@ 2014-11-13 17:29   ` Doug Anderson
  2014-11-13 19:35   ` Daniel Kurtz
  2014-11-14  2:57   ` Daniel Kurtz
  2 siblings, 0 replies; 10+ messages in thread
From: Doug Anderson @ 2014-11-13 17:29 UTC (permalink / raw)
  To: Caesar Wang
  Cc: Linus Walleij, linux-arm-kernel, Heiko Stuebner, Russell King,
	Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Grant Likely, linux-kernel, devicetree, Randy Dunlap, linux-doc,
	open list:ARM/Rockchip SoC...,
	Ulf Hansson, Dmitry Torokhov, ZhenFu Fang, Eddie Cai, Jack Dai,
	jinkun.hong

Hi,

On Wed, Nov 12, 2014 at 2:01 PM, Caesar Wang <caesar.wang@rock-chips.com> wrote:
> In order to meet high performance and low power requirements, a power
> management unit is designed or saving power when RK3288 in low power mode.
> The RK3288 PMU is dedicated for managing the power ot the whole chip.
>
> Signed-off-by: Jack Dai <jack.dai@rock-chips.com>
> Signed-off-by: jinkun.hong <jinkun.hong@rock-chips.com>
> Signed-off-by: Caesar Wang <caesar.wang@rock-chips.com>
>
> ---
>
> Changes in v11:
>     - fix pm_genpd_init(&pd->genpd, NULL, false);
>
> Changes in v10:
>     - this switches over domain infos to use masks instead of recomputing
>       them each time and also gets rid of custom domain translator and
>       uses standard onecell on.
>
> Changes in v9:
>     - fix v8 changes as follows:
>     - This reconciles the v2 and v7 code so that we power domain have
>       lists of clocks they toggle on and off during power transitions and
>       independently from power domains clocks we attach clocks to devices
>       comprising power domain and prepare them so they are turn on and off
>       by runtime PM.
>     - add rockchip_pm_add_one_domain() to control domains.
>     - add pd_start/pd_stop interface to control clocks
>
> Changes in v8:
>     - This reconciles the v2 and v7 code so that we power domain have
>       lists of clocks they toggle on and off during power transitions and
>       independently from power domains clocks we attach clocks to devices
>       comprising power domain and prepare them so they are turn on and off
>       by runtime PM.
>
> Changes in v7:
>     - Delete unused variables
>
> Changes in v6:
>     - delete pmu_lock
>     - modify dev_lock using mutex
>     - pm_clk_resume(pd->dev) change to pm_clk_resume(ed->dev)
>     - pm_clk_suspend(pd->dev) change to pm_clk_suspend(ed->dev)
>     - add devm_kfree(pd->dev, de) in rockchip_pm_domain_detach_dev
>
> Changes in v5:
>     - delete idle_lock
>     - add timeout in rockchip_pmu_set_idle_request()
>
> Changes in v4:
>     - use list storage dev
>
> Changes in v3:
>     - change use pm_clk_resume() and pm_clk_suspend()
>
> Changes in v2:
>     - remove the "pd->pd.of_node = np"
>
>  arch/arm/mach-rockchip/Kconfig            |   1 +
>  arch/arm/mach-rockchip/Makefile           |   1 +
>  arch/arm/mach-rockchip/pm_domains.c       | 471 ++++++++++++++++++++++++++++++
>  include/dt-bindings/power-domain/rk3288.h |  11 +
>  4 files changed, 484 insertions(+)

This was discussed offlist, but just to make sure it's also included here:

This patch will need to be spun to deal with the fact that (c16561e PM
/ Domains: Change prototype for the attach and detach callbacks) has
landed.

-Doug

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v11 3/3] ARM: dts: add rk3288 power-domain node
  2014-11-12 22:01 ` [PATCH v11 3/3] ARM: dts: add rk3288 power-domain node Caesar Wang
@ 2014-11-13 17:33   ` Doug Anderson
  0 siblings, 0 replies; 10+ messages in thread
From: Doug Anderson @ 2014-11-13 17:33 UTC (permalink / raw)
  To: Caesar Wang
  Cc: Linus Walleij, linux-arm-kernel, Heiko Stuebner, Russell King,
	Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Grant Likely, linux-kernel, devicetree, Randy Dunlap, linux-doc,
	open list:ARM/Rockchip SoC...,
	Ulf Hansson, Dmitry Torokhov, ZhenFu Fang, Eddie Cai, Jack Dai,
	jinkun.hong

Hi,

On Wed, Nov 12, 2014 at 2:01 PM, Caesar Wang <caesar.wang@rock-chips.com> wrote:
> +               pd_vio {
> +                       reg = <RK3288_PD_VIO>;
> +                       clocks = <&cru ACLK_IEP>,
> +                                <&cru ACLK_ISP>,
> +                                <&cru ACLK_RGA_NIU>,
> +                                <&cru ACLK_RGA>,
> +                                <&cru ACLK_VIO0_NIU>,
> +                                <&cru ACLK_VIO1_NIU>,
> +                                <&cru ACLK_VIP>,
> +                                <&cru ACLK_VOP0>,
> +                                <&cru ACLK_VOP1>,
> +                                <&cru DCLK_VOP0>,
> +                                <&cru DCLK_VOP1>,
> +                                <&cru HCLK_IEP>,
> +                                <&cru HCLK_ISP>,
> +                                <&cru HCLK_RGA>,
> +                                <&cru HCLK_VIO_AHB_ARBI>,
> +                                <&cru HCLK_VIO_NIU>,
> +                                <&cru HCLK_VIO2_H2P>,
> +                                <&cru HCLK_VIP>,
> +                                <&cru HCLK_VOP0>,
> +                                <&cru HCLK_VOP1>,
> +                                <&cru PCLK_EDP_CTRL>,
> +                                <&cru PCLK_HDMI_CTRL>,
> +                                <&cru PCLK_LVDS_PHY>,
> +                                <&cru PCLK_MIPI_CSI>,
> +                                <&cru PCLK_MIPI_DSI0>,
> +                                <&cru PCLK_MIPI_DSI1>,
> +                                <&cru PCLK_VIO2_H2P>,
> +                                <&cru SCLK_EDP_24M>,
> +                                <&cru SCLK_EDP>,
> +                                <&cru SCLK_HDMI_CEC>,
> +                                <&cru SCLK_HDMI_HDCP>,
> +                                <&cru SCLK_ISP_JPE>,
> +                                <&cru SCLK_ISP>,
> +                                <&cru SCLK_RGA>;

This was discussed offlist, but just to make sure it's summarized here:

Some of the above clocks are "IGNORE_UNUSED" (AKA left on) in Linux
right now because nobody is managing them right now (due to the fact
that they are "interface unit clocks", etc).  The fact that these are
now being enabled and disabled as part of power domain transitions
will break this.

My vote would be to leave such clocks out of the list for now until we
figure out how they should be dealt with.


-Doug

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v11 2/3] power-domain: rockchip: add power doamin driver
  2014-11-12 22:01 ` [PATCH v11 2/3] power-domain: rockchip: add power doamin driver Caesar Wang
  2014-11-13 17:29   ` Doug Anderson
@ 2014-11-13 19:35   ` Daniel Kurtz
  2014-11-13 20:20     ` Dmitry Torokhov
  2014-11-17  3:53     ` Caesar Wang
  2014-11-14  2:57   ` Daniel Kurtz
  2 siblings, 2 replies; 10+ messages in thread
From: Daniel Kurtz @ 2014-11-13 19:35 UTC (permalink / raw)
  To: Caesar Wang
  Cc: linus.walleij, linux-arm-kernel, Heiko Stuebner, Russell King,
	Mark Rutland, open list:OPEN FIRMWARE AND...,
	Ulf Hansson, Dmitry Torokhov, Pawel Moll, Ian Campbell,
	jinkun.hong, Randy Dunlap, linux-doc, linux-kernel,
	Douglas Anderson, open list:ARM/Rockchip SoC...,
	Rob Herring, ZhenFu Fang, Kumar Gala, Grant Likely, Eddie Cai,
	Jack Dai

On Thu, Nov 13, 2014 at 6:01 AM, Caesar Wang <caesar.wang@rock-chips.com> wrote:
> In order to meet high performance and low power requirements, a power
> management unit is designed or saving power when RK3288 in low power mode.
> The RK3288 PMU is dedicated for managing the power ot the whole chip.
>
> Signed-off-by: Jack Dai <jack.dai@rock-chips.com>
> Signed-off-by: jinkun.hong <jinkun.hong@rock-chips.com>
> Signed-off-by: Caesar Wang <caesar.wang@rock-chips.com>
>
> ---
>
> Changes in v11:
>     - fix pm_genpd_init(&pd->genpd, NULL, false);
>
> Changes in v10:
>     - this switches over domain infos to use masks instead of recomputing
>       them each time and also gets rid of custom domain translator and
>       uses standard onecell on.
>
> Changes in v9:
>     - fix v8 changes as follows:
>     - This reconciles the v2 and v7 code so that we power domain have
>       lists of clocks they toggle on and off during power transitions and
>       independently from power domains clocks we attach clocks to devices
>       comprising power domain and prepare them so they are turn on and off
>       by runtime PM.
>     - add rockchip_pm_add_one_domain() to control domains.
>     - add pd_start/pd_stop interface to control clocks
>
> Changes in v8:
>     - This reconciles the v2 and v7 code so that we power domain have
>       lists of clocks they toggle on and off during power transitions and
>       independently from power domains clocks we attach clocks to devices
>       comprising power domain and prepare them so they are turn on and off
>       by runtime PM.
>
> Changes in v7:
>     - Delete unused variables
>
> Changes in v6:
>     - delete pmu_lock
>     - modify dev_lock using mutex
>     - pm_clk_resume(pd->dev) change to pm_clk_resume(ed->dev)
>     - pm_clk_suspend(pd->dev) change to pm_clk_suspend(ed->dev)
>     - add devm_kfree(pd->dev, de) in rockchip_pm_domain_detach_dev
>
> Changes in v5:
>     - delete idle_lock
>     - add timeout in rockchip_pmu_set_idle_request()
>
> Changes in v4:
>     - use list storage dev
>
> Changes in v3:
>     - change use pm_clk_resume() and pm_clk_suspend()
>
> Changes in v2:
>     - remove the "pd->pd.of_node = np"
>
>  arch/arm/mach-rockchip/Kconfig            |   1 +
>  arch/arm/mach-rockchip/Makefile           |   1 +
>  arch/arm/mach-rockchip/pm_domains.c       | 471 ++++++++++++++++++++++++++++++
>  include/dt-bindings/power-domain/rk3288.h |  11 +
>  4 files changed, 484 insertions(+)
>  create mode 100644 arch/arm/mach-rockchip/pm_domains.c
>  create mode 100644 include/dt-bindings/power-domain/rk3288.h
>
> diff --git a/arch/arm/mach-rockchip/Kconfig b/arch/arm/mach-rockchip/Kconfig
> index ac5803c..d033993 100644
> --- a/arch/arm/mach-rockchip/Kconfig
> +++ b/arch/arm/mach-rockchip/Kconfig
> @@ -13,6 +13,7 @@ config ARCH_ROCKCHIP
>         select DW_APB_TIMER_OF
>         select ARM_GLOBAL_TIMER
>         select CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK
> +       select PM_GENERIC_DOMAINS if PM
>         help
>           Support for Rockchip's Cortex-A9 Single-to-Quad-Core-SoCs
>           containing the RK2928, RK30xx and RK31xx series.
> diff --git a/arch/arm/mach-rockchip/Makefile b/arch/arm/mach-rockchip/Makefile
> index b29d8ea..17ea082 100644
> --- a/arch/arm/mach-rockchip/Makefile
> +++ b/arch/arm/mach-rockchip/Makefile
> @@ -1,4 +1,5 @@
>  CFLAGS_platsmp.o := -march=armv7-a
>
>  obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip.o
> +obj-$(CONFIG_PM_GENERIC_DOMAINS) += pm_domains.o
>  obj-$(CONFIG_SMP) += headsmp.o platsmp.o
> diff --git a/arch/arm/mach-rockchip/pm_domains.c b/arch/arm/mach-rockchip/pm_domains.c
> new file mode 100644
> index 0000000..00730eb
> --- /dev/null
> +++ b/arch/arm/mach-rockchip/pm_domains.c
> @@ -0,0 +1,471 @@
> +/*
> + * Rockchip Generic power domain support.
> + *
> + * Copyright (c) 2014 ROCKCHIP, Co. Ltd.
> + *
> + * 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/module.h>
> +#include <linux/io.h>
> +#include <linux/err.h>
> +#include <linux/pm_clock.h>
> +#include <linux/pm_domain.h>
> +#include <linux/of_address.h>
> +#include <linux/of_platform.h>
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/regmap.h>
> +#include <linux/mfd/syscon.h>
> +#include <dt-bindings/power-domain/rk3288.h>
> +
> +struct rockchip_domain_info {
> +       int pwr_mask;
> +       int status_mask;
> +       int req_mask;
> +       int idle_mask;
> +       int ack_mask;
> +};
> +
> +struct rockchip_pmu_info {
> +       u32 pwr_offset;
> +       u32 status_offset;
> +       u32 req_offset;
> +       u32 idle_offset;
> +       u32 ack_offset;
> +
> +       int num_domains;
> +       const struct rockchip_domain_info *domain_info;
> +};
> +
> +struct rockchip_pm_domain {
> +       struct generic_pm_domain genpd;
> +       const struct rockchip_domain_info *info;
> +       struct rockchip_pmu *pmu;
> +       int num_clks;
> +       struct clk *clks[];
> +};
> +
> +struct rockchip_pmu {
> +       struct device *dev;
> +       struct regmap *regmap;
> +       const struct rockchip_pmu_info *info;
> +       struct mutex mutex; /* mutex lock for pmu */
> +       struct genpd_onecell_data genpd_data;
> +       struct generic_pm_domain *domains[];
> +};
> +
> +#define to_rockchip_pd(gpd) container_of(gpd, struct rockchip_pm_domain, genpd)
> +
> +#define DOMAIN(pwr, status, req, idle, ack)    \
> +{                                              \
> +       .pwr_mask = BIT(pwr),                   \
> +       .status_mask = BIT(status),             \
> +       .req_mask = BIT(req),                   \
> +       .idle_mask = BIT(idle),                 \
> +       .ack_mask = BIT(ack),                   \
> +}
> +
> +#define DOMAIN_RK3288(pwr, status, req)                \
> +       DOMAIN(pwr, status, req, req, (req) + 16)
> +
> +static bool rockchip_pmu_domain_is_idle(struct rockchip_pm_domain *pd)
> +{
> +       struct rockchip_pmu *pmu = pd->pmu;
> +       const struct rockchip_domain_info *pd_info = pd->info;
> +       unsigned int val;
> +
> +       regmap_read(pmu->regmap, pmu->info->idle_offset, &val);
> +       return (val & pd_info->idle_mask) == pd_info->idle_mask;
> +}
> +
> +static int rockchip_pmu_set_idle_request(struct rockchip_pm_domain *pd,
> +                                        bool idle)
> +{
> +       const struct rockchip_domain_info *pd_info = pd->info;
> +       struct rockchip_pmu *pmu = pd->pmu;
> +       unsigned int val;
> +
> +       regmap_update_bits(pmu->regmap, pmu->info->req_offset,
> +                          pd_info->req_mask, idle ? -1U : 0);
> +
> +       dsb();
> +
> +       do {
> +               regmap_read(pmu->regmap, pmu->info->ack_offset, &val);
> +       } while ((val & pd_info->ack_mask) != (idle ? pd_info->ack_mask : 0));

Hi Caesar,


just a nit:
it would be slightly cleaner if you created a function for this like
you did for rockchip_pmu_domain_is_idle():

static bool rockchip_pmu_domain_is_ack(struct rockchip_pm_domain *pd)
{
       struct rockchip_pmu *pmu = pd->pmu;
       const struct rockchip_domain_info *pd_info = pd->info;
       unsigned int val;

       regmap_read(pmu->regmap, pmu->info->ack_offset, &val);
       return (val & pd_info->ack_mask) == pd_info->ack_mask;
}

...

  while (rockchip_pmu_domain_is_ack(pd) != idle)
    cpu_relax();

> +
> +       while (rockchip_pmu_domain_is_idle(pd) != idle)
> +               cpu_relax();
> +
> +       return 0;
> +}
> +
> +static bool rockchip_pmu_domain_is_on(struct rockchip_pm_domain *pd)
> +{
> +       struct rockchip_pmu *pmu = pd->pmu;
> +       unsigned int val;
> +
> +       regmap_read(pmu->regmap, pmu->info->status_offset, &val);
> +
> +       /* 1'b0: power on, 1'b1: power off */
> +       return !(val & pd->info->status_mask);
> +}
> +
> +static void rockchip_do_pmu_set_power_domain(struct rockchip_pm_domain *pd,
> +                                            bool on)
> +{
> +       struct rockchip_pmu *pmu = pd->pmu;
> +
> +       regmap_update_bits(pmu->regmap, pmu->info->pwr_offset,
> +                          pd->info->pwr_mask, on ? 0 : -1U);
> +
> +       dsb();
> +
> +       while (rockchip_pmu_domain_is_on(pd) != on)
> +               cpu_relax();
> +}
> +
> +static int rockchip_pd_power(struct rockchip_pm_domain *pd, bool power_on)
> +{
> +       int i;
> +
> +       mutex_lock(&pd->pmu->mutex);
> +
> +       if (rockchip_pmu_domain_is_on(pd) != power_on) {
> +               for (i = 0; i < pd->num_clks; i++)
> +                       clk_enable(pd->clks[i]);
> +
> +               if (!power_on) {
> +                       /* FIXME: add code to save AXI_QOS */
> +
> +                       /* if powering down, idle request to NIU first */
> +                       rockchip_pmu_set_idle_request(pd, true);
> +               }
> +
> +               rockchip_do_pmu_set_power_domain(pd, power_on);
> +
> +               if (power_on) {
> +                       /* if powering up, leave idle mode */
> +                       rockchip_pmu_set_idle_request(pd, false);
> +
> +                       /* FIXME: add code to restore AXI_QOS */
> +               }
> +
> +               for (i = pd->num_clks - 1; i >= 0; i--)
> +                       clk_disable(pd->clks[i]);
> +       }
> +
> +       mutex_unlock(&pd->pmu->mutex);
> +       return 0;
> +}
> +
> +static int rockchip_pd_power_on(struct generic_pm_domain *domain)
> +{
> +       struct rockchip_pm_domain *pd = to_rockchip_pd(domain);
> +
> +       return rockchip_pd_power(pd, true);
> +}
> +
> +static int rockchip_pd_power_off(struct generic_pm_domain *domain)
> +{
> +       struct rockchip_pm_domain *pd = to_rockchip_pd(domain);
> +
> +       return rockchip_pd_power(pd, false);
> +}
> +
> +static void rockchip_pd_attach_dev(struct device *dev)
> +{
> +       struct generic_pm_domain *genpd = pd_to_genpd(dev->pm_domain);
> +       struct clk *clk;
> +       int i;
> +       int error;
> +
> +       dev_dbg(dev, "attaching to power domain '%s'\n", genpd->name);
> +
> +       error = pm_clk_create(dev);
> +       if (error) {
> +               dev_err(dev, "pm_clk_create failed %d\n", error);
> +               return;
> +       }
> +
> +       while ((clk = of_clk_get(dev->of_node, i++)) && !IS_ERR(clk)) {
> +               error = pm_clk_add_clk(dev, clk);
> +               clk_put(clk);
> +               if (error) {
> +                       dev_err(dev, "pm_clk_add_clk failed %d\n", error);
> +                       pm_clk_destroy(dev);
> +                       return;
> +               }
> +               dev_dbg(dev, "added clock '%s' to list of PM clocks\n",
> +                       __clk_get_name(clk));
> +       }
> +}
> +
> +static void rockchip_pd_detach_dev(struct device *dev)
> +{
> +       struct generic_pm_domain *genpd = pd_to_genpd(dev->pm_domain);
> +
> +       dev_dbg(dev, "detaching from power domain %s\n", genpd->name);
> +
> +       pm_clk_destroy(dev);
> +}
> +
> +static int rockchip_pd_start_dev(struct device *dev)
> +{
> +       struct generic_pm_domain *genpd = pd_to_genpd(dev->pm_domain);
> +
> +       dev_dbg(dev, "starting device in power domain '%s'\n", genpd->name);
> +
> +       return pm_clk_resume(dev);
> +}
> +
> +static int rockchip_pd_stop_dev(struct device *dev)
> +{
> +       struct generic_pm_domain *genpd = pd_to_genpd(dev->pm_domain);
> +
> +       dev_dbg(dev, "stopping device in power domain '%s'\n", genpd->name);
> +
> +       return pm_clk_suspend(dev);
> +}
> +
> +static int rockchip_pm_add_one_domain(struct rockchip_pmu *pmu,
> +                                     struct device_node *node)
> +{
> +       const struct rockchip_domain_info *pd_info;
> +       struct rockchip_pm_domain *pd;
> +       struct clk *clk;
> +       int clk_cnt;
> +       int i;
> +       u32 id;
> +       bool is_on;
> +       int error;
> +
> +       error = of_property_read_u32(node, "reg", &id);
> +       if (error) {
> +               dev_err(pmu->dev,
> +                       "%s: failed to retrieve domain id (reg): %d\n",
> +                       node->name, error);
> +               return -EINVAL;
> +       }
> +
> +       if (id >= pmu->info->num_domains) {
> +               dev_err(pmu->dev, "%s: invalid domain id %d\n",
> +                       node->name, id);
> +               return -EINVAL;
> +       }
> +
> +       pd_info = &pmu->info->domain_info[id];
> +       if (!pd_info) {
> +               dev_err(pmu->dev, "%s: undefined domain id %d\n",
> +                       node->name, id);
> +               return -EINVAL;
> +       }
> +
> +       clk_cnt = of_count_phandle_with_args(node, "clocks", "#clock-cells");
> +       pd = devm_kzalloc(pmu->dev,
> +                         sizeof(*pd) + clk_cnt * sizeof(pd->clks[0]),
> +                         GFP_KERNEL);
> +       if (!pd)
> +               return -ENOMEM;
> +
> +       pd->genpd.name = node->name;
> +       pd->genpd.power_off = rockchip_pd_power_off;
> +       pd->genpd.power_on = rockchip_pd_power_on;
> +       pd->genpd.attach_dev = rockchip_pd_attach_dev;
> +       pd->genpd.detach_dev = rockchip_pd_detach_dev;
> +       pd->genpd.dev_ops.start = rockchip_pd_start_dev;
> +       pd->genpd.dev_ops.stop = rockchip_pd_stop_dev;
> +
> +       pd->info = pd_info;
> +       pd->pmu = pmu;
> +       pd->num_clks = clk_cnt;
> +
> +       is_on = rockchip_pmu_domain_is_on(pd);
> +       pm_genpd_init(&pd->genpd, NULL, !is_on);
> +
> +       for (i = 0; i < clk_cnt; i++) {
> +               clk = of_clk_get(node, i);
> +               if (IS_ERR(clk)) {
> +                       error = PTR_ERR(clk);
> +                       dev_err(pmu->dev,
> +                               "%s: failed to get clk %s (index %d): %d\n",
> +                               node->name, __clk_get_name(clk), i, error);
> +                       goto err_out;
> +               }
> +
> +               error = clk_prepare(clk);
> +               if (error) {
> +                       dev_err(pmu->dev,
> +                               "%s: failed to prepare clk %s (index %d): %d\n",
> +                               node->name, __clk_get_name(clk), i, error);
> +                       clk_put(clk);
> +                       goto err_out;
> +               }
> +
> +               pd->clks[i] = clk;
> +
> +               dev_dbg(pmu->dev, "added clock '%s' to domain '%s'\n",
> +                       __clk_get_name(clk), node->name);
> +       }
> +
> +       pmu->genpd_data.domains[id] = &pd->genpd;
> +       return 0;
> +
> +err_out:
> +       while (--i >= 0) {
> +               clk_unprepare(pd->clks[i]);
> +               clk_put(pd->clks[i]);
> +       }
> +       return error;
> +}
> +
> +static void rockchip_pm_remove_one_domain(struct rockchip_pm_domain *pd)
> +{
> +       int i;
> +
> +       for (i = 0; i < pd->num_clks; i++) {
> +               clk_unprepare(pd->clks[i]);
> +               clk_put(pd->clks[i]);
> +       }
> +
> +       /* devm will free our memory */
> +}
> +
> +static void rockchip_pm_domain_cleanup(struct rockchip_pmu *pmu)
> +{
> +       struct generic_pm_domain *genpd;
> +       struct rockchip_pm_domain *pd;
> +       int i;
> +
> +       for (i = 0; i < pmu->genpd_data.num_domains; i++) {
> +               genpd = pmu->genpd_data.domains[i];
> +               if (genpd) {
> +                       pd = to_rockchip_pd(genpd);
> +                       rockchip_pm_remove_one_domain(pd);
> +               }
> +       }
> +
> +       /* devm will free our memory */
> +}
> +
> +static int rockchip_pm_domain_probe(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct device_node *np = dev->of_node;
> +       struct device_node *node;
> +       struct rockchip_pmu *pmu;
> +       const struct of_device_id *match;
> +       const struct rockchip_pmu_info *pmu_info;
> +       int error;
> +
> +       if (!np) {
> +               dev_err(dev, "device tree node not found\n");
> +               return -ENXIO;
> +       }
> +
> +       match = of_match_device(dev->driver->of_match_table, dev);
> +       if (!match || !match->data) {
> +               dev_err(dev, "missing pmu data\n");
> +               return -EINVAL;
> +       }
> +
> +       pmu_info = match->data;
> +
> +       pmu = devm_kzalloc(dev,
> +                          sizeof(*pmu) +
> +                               pmu_info->num_domains * sizeof(pmu->domains[0]),
> +                          GFP_KERNEL);
> +       if (!pmu)
> +               return -ENOMEM;
> +
> +       pmu->dev = &pdev->dev;
> +       mutex_init(&pmu->mutex);
> +
> +       pmu->info = pmu_info;
> +
> +       pmu->genpd_data.domains = pmu->domains;
> +       pmu->genpd_data.num_domains = pmu_info->num_domains;
> +
> +       node = of_parse_phandle(np, "rockchip,pmu", 0);
> +       pmu->regmap = syscon_node_to_regmap(node);
> +       of_node_put(node);
> +       if (IS_ERR(pmu->regmap)) {
> +               error = PTR_ERR(pmu->regmap);
> +               dev_err(dev, "failed to get PMU regmap: %d\n", error);
> +               return error;
> +       }
> +
> +       error = -ENXIO;
> +
> +       for_each_available_child_of_node(np, node) {
> +               error = rockchip_pm_add_one_domain(pmu, node);
> +               if (error) {
> +                       dev_err(dev, "failed to handle node %s: %d\n",
> +                               node->name, error);
> +                       goto err_out;
> +               }
> +       }
> +
> +       if (error) {
> +               dev_dbg(dev, "no power domains defined\n");

Perhaps dev_err() or dev_warn(), to ensure this message is seen.
But, not have defined power domains doesn't seem like a fatal error.
Perhaps just return 0?

-Daniel

> +               goto err_out;
> +       }
> +
> +       of_genpd_add_provider_onecell(np, &pmu->genpd_data);
> +
> +       return 0;
> +
> +err_out:
> +       rockchip_pm_domain_cleanup(pmu);
> +       return error;
> +}
> +
> +static const struct rockchip_domain_info rk3288_pm_domains[] = {
> +       [RK3288_PD_GPU]         = DOMAIN_RK3288(9, 9, 2),
> +       [RK3288_PD_VIO]         = DOMAIN_RK3288(7, 7, 4),
> +       [RK3288_PD_VIDEO]       = DOMAIN_RK3288(8, 8, 3),
> +       [RK3288_PD_HEVC]        = DOMAIN_RK3288(14, 10, 9),
> +};
> +
> +static const struct rockchip_pmu_info rk3288_pmu = {
> +       .pwr_offset = 0x08,
> +       .status_offset = 0x0c,
> +       .req_offset = 0x10,
> +       .idle_offset = 0x14,
> +       .ack_offset = 0x14,
> +       .num_domains = ARRAY_SIZE(rk3288_pm_domains),
> +       .domain_info = rk3288_pm_domains,
> +};
> +
> +static const struct of_device_id rockchip_pm_domain_dt_match[] = {
> +       {
> +               .compatible = "rockchip,rk3288-power-controller",
> +               .data = (void *)&rk3288_pmu,
> +       },
> +       { /* sentinel */ },
> +};
> +MODULE_DEVICE_TABLE(of, rockchip_pm_domain_dt_match);
> +
> +static struct platform_driver rockchip_pm_domain_driver = {
> +       .probe = rockchip_pm_domain_probe,
> +       .driver = {
> +               .name   = "rockchip-pm-domain",
> +               .owner  = THIS_MODULE,
> +               .of_match_table = rockchip_pm_domain_dt_match,
> +               /*
> +                * We can't forcibly eject devices form power domain,
> +                * so we can't really remove power domains once they
> +                * were added.
> +                */
> +               .suppress_bind_attrs = true,
> +       },
> +};
> +
> +static int __init rockchip_pm_domain_drv_register(void)
> +{
> +       return platform_driver_register(&rockchip_pm_domain_driver);
> +}
> +postcore_initcall(rockchip_pm_domain_drv_register);
> diff --git a/include/dt-bindings/power-domain/rk3288.h b/include/dt-bindings/power-domain/rk3288.h
> new file mode 100644
> index 0000000..ca68c11
> --- /dev/null
> +++ b/include/dt-bindings/power-domain/rk3288.h
> @@ -0,0 +1,11 @@
> +#ifndef __DT_BINDINGS_POWER_DOMAIN_RK3288_H__
> +#define __DT_BINDINGS_POWER_DOMAIN_RK3288_H__
> +
> +/* RK3288 power domain index */
> +#define RK3288_PD_GPU          0
> +#define RK3288_PD_VIO          1
> +#define RK3288_PD_VIDEO        2
> +#define RK3288_PD_HEVC         3
> +#define RK3288_PD_PERI         4
> +
> +#endif
> --
> 1.9.1
>
>
>
> _______________________________________________
> Linux-rockchip mailing list
> Linux-rockchip@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rockchip

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v11 2/3] power-domain: rockchip: add power doamin driver
  2014-11-13 19:35   ` Daniel Kurtz
@ 2014-11-13 20:20     ` Dmitry Torokhov
  2014-11-17  3:53     ` Caesar Wang
  1 sibling, 0 replies; 10+ messages in thread
From: Dmitry Torokhov @ 2014-11-13 20:20 UTC (permalink / raw)
  To: Daniel Kurtz
  Cc: Caesar Wang, linus.walleij, linux-arm-kernel, Heiko Stuebner,
	Russell King, Mark Rutland, open list:OPEN FIRMWARE AND...,
	Ulf Hansson, Pawel Moll, Ian Campbell, jinkun.hong, Randy Dunlap,
	linux-doc, linux-kernel, Douglas Anderson,
	open list:ARM/Rockchip SoC...,
	Rob Herring, ZhenFu Fang, Kumar Gala, Grant Likely, Eddie Cai,
	Jack Dai

On Friday, November 14, 2014 03:35:44 AM Daniel Kurtz wrote:
> On Thu, Nov 13, 2014 at 6:01 AM, Caesar Wang <caesar.wang@rock-chips.com> wrote:
> > +
> > +       error = -ENXIO;
> > +
> > +       for_each_available_child_of_node(np, node) {
> > +               error = rockchip_pm_add_one_domain(pmu, node);
> > +               if (error) {
> > +                       dev_err(dev, "failed to handle node %s: %d\n",
> > +                               node->name, error);
> > +                       goto err_out;
> > +               }
> > +       }
> > +
> > +       if (error) {
> > +               dev_dbg(dev, "no power domains defined\n");
> 
> Perhaps dev_err() or dev_warn(), to ensure this message is seen.
> But, not have defined power domains doesn't seem like a fatal error.
> Perhaps just return 0?

As far as the driver concerned it is a fatal error - the driver can't do
anything when no domain is defined.

Thanks.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v11 2/3] power-domain: rockchip: add power doamin driver
  2014-11-12 22:01 ` [PATCH v11 2/3] power-domain: rockchip: add power doamin driver Caesar Wang
  2014-11-13 17:29   ` Doug Anderson
  2014-11-13 19:35   ` Daniel Kurtz
@ 2014-11-14  2:57   ` Daniel Kurtz
  2 siblings, 0 replies; 10+ messages in thread
From: Daniel Kurtz @ 2014-11-14  2:57 UTC (permalink / raw)
  To: Caesar Wang
  Cc: Linus Walleij, linux-arm-kernel, Heiko Stuebner, Russell King,
	Mark Rutland, open list:OPEN FIRMWARE AND...,
	Ulf Hansson, Dmitry Torokhov, Pawel Moll, Ian Campbell,
	jinkun.hong, Randy Dunlap, linux-doc, linux-kernel,
	Douglas Anderson, open list:ARM/Rockchip SoC...,
	Rob Herring, ZhenFu Fang, Kumar Gala, Grant Likely, Eddie Cai,
	Jack Dai

Caesar,

One more little nit:

The title of this patch has a typo:
  doamin -> domain

Thanks,
-Dan

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v11 2/3] power-domain: rockchip: add power doamin driver
  2014-11-13 19:35   ` Daniel Kurtz
  2014-11-13 20:20     ` Dmitry Torokhov
@ 2014-11-17  3:53     ` Caesar Wang
  1 sibling, 0 replies; 10+ messages in thread
From: Caesar Wang @ 2014-11-17  3:53 UTC (permalink / raw)
  To: Daniel Kurtz
  Cc: linus.walleij, linux-arm-kernel, Heiko Stuebner, Russell King,
	Mark Rutland, open list:OPEN FIRMWARE AND...,
	Ulf Hansson, Dmitry Torokhov, Pawel Moll, Ian Campbell,
	jinkun.hong, Randy Dunlap, linux-doc, linux-kernel,
	Douglas Anderson, open list:ARM/Rockchip SoC...,
	Rob Herring, ZhenFu Fang, Kumar Gala, Grant Likely, Eddie Cai,
	Jack Dai


在 2014年11月14日 03:35, Daniel Kurtz 写道:
> On Thu, Nov 13, 2014 at 6:01 AM, Caesar Wang <caesar.wang@rock-chips.com> wrote:
>> In order to meet high performance and low power requirements, a power
>> management unit is designed or saving power when RK3288 in low power mode.
>> The RK3288 PMU is dedicated for managing the power ot the whole chip.
>>
>> Signed-off-by: Jack Dai <jack.dai@rock-chips.com>
>> Signed-off-by: jinkun.hong <jinkun.hong@rock-chips.com>
>> Signed-off-by: Caesar Wang <caesar.wang@rock-chips.com>
>>
>> ---
>>
>> Changes in v11:
>>      - fix pm_genpd_init(&pd->genpd, NULL, false);
>>
>> Changes in v10:
>>      - this switches over domain infos to use masks instead of recomputing
>>        them each time and also gets rid of custom domain translator and
>>        uses standard onecell on.
>>
>> Changes in v9:
>>      - fix v8 changes as follows:
>>      - This reconciles the v2 and v7 code so that we power domain have
>>        lists of clocks they toggle on and off during power transitions and
>>        independently from power domains clocks we attach clocks to devices
>>        comprising power domain and prepare them so they are turn on and off
>>        by runtime PM.
>>      - add rockchip_pm_add_one_domain() to control domains.
>>      - add pd_start/pd_stop interface to control clocks
>>
>> Changes in v8:
>>      - This reconciles the v2 and v7 code so that we power domain have
>>        lists of clocks they toggle on and off during power transitions and
>>        independently from power domains clocks we attach clocks to devices
>>        comprising power domain and prepare them so they are turn on and off
>>        by runtime PM.
>>
>> Changes in v7:
>>      - Delete unused variables
>>
>> Changes in v6:
>>      - delete pmu_lock
>>      - modify dev_lock using mutex
>>      - pm_clk_resume(pd->dev) change to pm_clk_resume(ed->dev)
>>      - pm_clk_suspend(pd->dev) change to pm_clk_suspend(ed->dev)
>>      - add devm_kfree(pd->dev, de) in rockchip_pm_domain_detach_dev
>>
>> Changes in v5:
>>      - delete idle_lock
>>      - add timeout in rockchip_pmu_set_idle_request()
>>
>> Changes in v4:
>>      - use list storage dev
>>
>> Changes in v3:
>>      - change use pm_clk_resume() and pm_clk_suspend()
>>
>> Changes in v2:
>>      - remove the "pd->pd.of_node = np"
>>
>>   arch/arm/mach-rockchip/Kconfig            |   1 +
>>   arch/arm/mach-rockchip/Makefile           |   1 +
>>   arch/arm/mach-rockchip/pm_domains.c       | 471 ++++++++++++++++++++++++++++++
>>   include/dt-bindings/power-domain/rk3288.h |  11 +
>>   4 files changed, 484 insertions(+)
>>   create mode 100644 arch/arm/mach-rockchip/pm_domains.c
>>   create mode 100644 include/dt-bindings/power-domain/rk3288.h
>>
>> diff --git a/arch/arm/mach-rockchip/Kconfig b/arch/arm/mach-rockchip/Kconfig
>> index ac5803c..d033993 100644
>> --- a/arch/arm/mach-rockchip/Kconfig
>> +++ b/arch/arm/mach-rockchip/Kconfig
>> @@ -13,6 +13,7 @@ config ARCH_ROCKCHIP
>>          select DW_APB_TIMER_OF
>>          select ARM_GLOBAL_TIMER
>>          select CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK
>> +       select PM_GENERIC_DOMAINS if PM
>>          help
>>            Support for Rockchip's Cortex-A9 Single-to-Quad-Core-SoCs
>>            containing the RK2928, RK30xx and RK31xx series.
>> diff --git a/arch/arm/mach-rockchip/Makefile b/arch/arm/mach-rockchip/Makefile
>> index b29d8ea..17ea082 100644
>> --- a/arch/arm/mach-rockchip/Makefile
>> +++ b/arch/arm/mach-rockchip/Makefile
>> @@ -1,4 +1,5 @@
>>   CFLAGS_platsmp.o := -march=armv7-a
>>
>>   obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip.o
>> +obj-$(CONFIG_PM_GENERIC_DOMAINS) += pm_domains.o
>>   obj-$(CONFIG_SMP) += headsmp.o platsmp.o
>> diff --git a/arch/arm/mach-rockchip/pm_domains.c b/arch/arm/mach-rockchip/pm_domains.c
>> new file mode 100644
>> index 0000000..00730eb
>> --- /dev/null
>> +++ b/arch/arm/mach-rockchip/pm_domains.c
>> @@ -0,0 +1,471 @@
>> +/*
>> + * Rockchip Generic power domain support.
>> + *
>> + * Copyright (c) 2014 ROCKCHIP, Co. Ltd.
>> + *
>> + * 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/module.h>
>> +#include <linux/io.h>
>> +#include <linux/err.h>
>> +#include <linux/pm_clock.h>
>> +#include <linux/pm_domain.h>
>> +#include <linux/of_address.h>
>> +#include <linux/of_platform.h>
>> +#include <linux/clk.h>
>> +#include <linux/clk-provider.h>
>> +#include <linux/regmap.h>
>> +#include <linux/mfd/syscon.h>
>> +#include <dt-bindings/power-domain/rk3288.h>
>> +
>> +struct rockchip_domain_info {
>> +       int pwr_mask;
>> +       int status_mask;
>> +       int req_mask;
>> +       int idle_mask;
>> +       int ack_mask;
>> +};
>> +
>> +struct rockchip_pmu_info {
>> +       u32 pwr_offset;
>> +       u32 status_offset;
>> +       u32 req_offset;
>> +       u32 idle_offset;
>> +       u32 ack_offset;
>> +
>> +       int num_domains;
>> +       const struct rockchip_domain_info *domain_info;
>> +};
>> +
>> +struct rockchip_pm_domain {
>> +       struct generic_pm_domain genpd;
>> +       const struct rockchip_domain_info *info;
>> +       struct rockchip_pmu *pmu;
>> +       int num_clks;
>> +       struct clk *clks[];
>> +};
>> +
>> +struct rockchip_pmu {
>> +       struct device *dev;
>> +       struct regmap *regmap;
>> +       const struct rockchip_pmu_info *info;
>> +       struct mutex mutex; /* mutex lock for pmu */
>> +       struct genpd_onecell_data genpd_data;
>> +       struct generic_pm_domain *domains[];
>> +};
>> +
>> +#define to_rockchip_pd(gpd) container_of(gpd, struct rockchip_pm_domain, genpd)
>> +
>> +#define DOMAIN(pwr, status, req, idle, ack)    \
>> +{                                              \
>> +       .pwr_mask = BIT(pwr),                   \
>> +       .status_mask = BIT(status),             \
>> +       .req_mask = BIT(req),                   \
>> +       .idle_mask = BIT(idle),                 \
>> +       .ack_mask = BIT(ack),                   \
>> +}
>> +
>> +#define DOMAIN_RK3288(pwr, status, req)                \
>> +       DOMAIN(pwr, status, req, req, (req) + 16)
>> +
>> +static bool rockchip_pmu_domain_is_idle(struct rockchip_pm_domain *pd)
>> +{
>> +       struct rockchip_pmu *pmu = pd->pmu;
>> +       const struct rockchip_domain_info *pd_info = pd->info;
>> +       unsigned int val;
>> +
>> +       regmap_read(pmu->regmap, pmu->info->idle_offset, &val);
>> +       return (val & pd_info->idle_mask) == pd_info->idle_mask;
>> +}
>> +
>> +static int rockchip_pmu_set_idle_request(struct rockchip_pm_domain *pd,
>> +                                        bool idle)
>> +{
>> +       const struct rockchip_domain_info *pd_info = pd->info;
>> +       struct rockchip_pmu *pmu = pd->pmu;
>> +       unsigned int val;
>> +
>> +       regmap_update_bits(pmu->regmap, pmu->info->req_offset,
>> +                          pd_info->req_mask, idle ? -1U : 0);
>> +
>> +       dsb();
>> +
>> +       do {
>> +               regmap_read(pmu->regmap, pmu->info->ack_offset, &val);
>> +       } while ((val & pd_info->ack_mask) != (idle ? pd_info->ack_mask : 0));
> Hi Caesar,
>
>
> just a nit:
> it would be slightly cleaner if you created a function for this like
> you did for rockchip_pmu_domain_is_idle():
>
> static bool rockchip_pmu_domain_is_ack(struct rockchip_pm_domain *pd)
> {
>         struct rockchip_pmu *pmu = pd->pmu;
>         const struct rockchip_domain_info *pd_info = pd->info;
>         unsigned int val;
>
>         regmap_read(pmu->regmap, pmu->info->ack_offset, &val);
>         return (val & pd_info->ack_mask) == pd_info->ack_mask;
> }
>
> ...
>
>    while (rockchip_pmu_domain_is_ack(pd) != idle)
>      cpu_relax();

Fixed.


>> +
>> +       while (rockchip_pmu_domain_is_idle(pd) != idle)
>> +               cpu_relax();
>> +
>> +       return 0;
>> +}
>> +
>> +static bool rockchip_pmu_domain_is_on(struct rockchip_pm_domain *pd)
>> +{
>> +       struct rockchip_pmu *pmu = pd->pmu;
>> +       unsigned int val;
>> +
>> +       regmap_read(pmu->regmap, pmu->info->status_offset, &val);
>> +
>> +       /* 1'b0: power on, 1'b1: power off */
>> +       return !(val & pd->info->status_mask);
>> +}
>> +
>> +static void rockchip_do_pmu_set_power_domain(struct rockchip_pm_domain *pd,
>> +                                            bool on)
>> +{
>> +       struct rockchip_pmu *pmu = pd->pmu;
>> +
>> +       regmap_update_bits(pmu->regmap, pmu->info->pwr_offset,
>> +                          pd->info->pwr_mask, on ? 0 : -1U);
>> +
>> +       dsb();
>> +
>> +       while (rockchip_pmu_domain_is_on(pd) != on)
>> +               cpu_relax();
>> +}
>> +
>> +static int rockchip_pd_power(struct rockchip_pm_domain *pd, bool power_on)
>> +{
>> +       int i;
>> +
>> +       mutex_lock(&pd->pmu->mutex);
>> +
>> +       if (rockchip_pmu_domain_is_on(pd) != power_on) {
>> +               for (i = 0; i < pd->num_clks; i++)
>> +                       clk_enable(pd->clks[i]);
>> +
>> +               if (!power_on) {
>> +                       /* FIXME: add code to save AXI_QOS */
>> +
>> +                       /* if powering down, idle request to NIU first */
>> +                       rockchip_pmu_set_idle_request(pd, true);
>> +               }
>> +
>> +               rockchip_do_pmu_set_power_domain(pd, power_on);
>> +
>> +               if (power_on) {
>> +                       /* if powering up, leave idle mode */
>> +                       rockchip_pmu_set_idle_request(pd, false);
>> +
>> +                       /* FIXME: add code to restore AXI_QOS */
>> +               }
>> +
>> +               for (i = pd->num_clks - 1; i >= 0; i--)
>> +                       clk_disable(pd->clks[i]);
>> +       }
>> +
>> +       mutex_unlock(&pd->pmu->mutex);
>> +       return 0;
>> +}
>> +
>> +static int rockchip_pd_power_on(struct generic_pm_domain *domain)
>> +{
>> +       struct rockchip_pm_domain *pd = to_rockchip_pd(domain);
>> +
>> +       return rockchip_pd_power(pd, true);
>> +}
>> +
>> +static int rockchip_pd_power_off(struct generic_pm_domain *domain)
>> +{
>> +       struct rockchip_pm_domain *pd = to_rockchip_pd(domain);
>> +
>> +       return rockchip_pd_power(pd, false);
>> +}
>> +
>> +static void rockchip_pd_attach_dev(struct device *dev)
>> +{
>> +       struct generic_pm_domain *genpd = pd_to_genpd(dev->pm_domain);
>> +       struct clk *clk;
>> +       int i;
>> +       int error;
>> +
>> +       dev_dbg(dev, "attaching to power domain '%s'\n", genpd->name);
>> +
>> +       error = pm_clk_create(dev);
>> +       if (error) {
>> +               dev_err(dev, "pm_clk_create failed %d\n", error);
>> +               return;
>> +       }
>> +
>> +       while ((clk = of_clk_get(dev->of_node, i++)) && !IS_ERR(clk)) {
>> +               error = pm_clk_add_clk(dev, clk);
>> +               clk_put(clk);
>> +               if (error) {
>> +                       dev_err(dev, "pm_clk_add_clk failed %d\n", error);
>> +                       pm_clk_destroy(dev);
>> +                       return;
>> +               }
>> +               dev_dbg(dev, "added clock '%s' to list of PM clocks\n",
>> +                       __clk_get_name(clk));
>> +       }
>> +}
>> +
>> +static void rockchip_pd_detach_dev(struct device *dev)
>> +{
>> +       struct generic_pm_domain *genpd = pd_to_genpd(dev->pm_domain);
>> +
>> +       dev_dbg(dev, "detaching from power domain %s\n", genpd->name);
>> +
>> +       pm_clk_destroy(dev);
>> +}
>> +
>> +static int rockchip_pd_start_dev(struct device *dev)
>> +{
>> +       struct generic_pm_domain *genpd = pd_to_genpd(dev->pm_domain);
>> +
>> +       dev_dbg(dev, "starting device in power domain '%s'\n", genpd->name);
>> +
>> +       return pm_clk_resume(dev);
>> +}
>> +
>> +static int rockchip_pd_stop_dev(struct device *dev)
>> +{
>> +       struct generic_pm_domain *genpd = pd_to_genpd(dev->pm_domain);
>> +
>> +       dev_dbg(dev, "stopping device in power domain '%s'\n", genpd->name);
>> +
>> +       return pm_clk_suspend(dev);
>> +}
>> +
>> +static int rockchip_pm_add_one_domain(struct rockchip_pmu *pmu,
>> +                                     struct device_node *node)
>> +{
>> +       const struct rockchip_domain_info *pd_info;
>> +       struct rockchip_pm_domain *pd;
>> +       struct clk *clk;
>> +       int clk_cnt;
>> +       int i;
>> +       u32 id;
>> +       bool is_on;
>> +       int error;
>> +
>> +       error = of_property_read_u32(node, "reg", &id);
>> +       if (error) {
>> +               dev_err(pmu->dev,
>> +                       "%s: failed to retrieve domain id (reg): %d\n",
>> +                       node->name, error);
>> +               return -EINVAL;
>> +       }
>> +
>> +       if (id >= pmu->info->num_domains) {
>> +               dev_err(pmu->dev, "%s: invalid domain id %d\n",
>> +                       node->name, id);
>> +               return -EINVAL;
>> +       }
>> +
>> +       pd_info = &pmu->info->domain_info[id];
>> +       if (!pd_info) {
>> +               dev_err(pmu->dev, "%s: undefined domain id %d\n",
>> +                       node->name, id);
>> +               return -EINVAL;
>> +       }
>> +
>> +       clk_cnt = of_count_phandle_with_args(node, "clocks", "#clock-cells");
>> +       pd = devm_kzalloc(pmu->dev,
>> +                         sizeof(*pd) + clk_cnt * sizeof(pd->clks[0]),
>> +                         GFP_KERNEL);
>> +       if (!pd)
>> +               return -ENOMEM;
>> +
>> +       pd->genpd.name = node->name;
>> +       pd->genpd.power_off = rockchip_pd_power_off;
>> +       pd->genpd.power_on = rockchip_pd_power_on;
>> +       pd->genpd.attach_dev = rockchip_pd_attach_dev;
>> +       pd->genpd.detach_dev = rockchip_pd_detach_dev;
>> +       pd->genpd.dev_ops.start = rockchip_pd_start_dev;
>> +       pd->genpd.dev_ops.stop = rockchip_pd_stop_dev;
>> +
>> +       pd->info = pd_info;
>> +       pd->pmu = pmu;
>> +       pd->num_clks = clk_cnt;
>> +
>> +       is_on = rockchip_pmu_domain_is_on(pd);
>> +       pm_genpd_init(&pd->genpd, NULL, !is_on);
>> +
>> +       for (i = 0; i < clk_cnt; i++) {
>> +               clk = of_clk_get(node, i);
>> +               if (IS_ERR(clk)) {
>> +                       error = PTR_ERR(clk);
>> +                       dev_err(pmu->dev,
>> +                               "%s: failed to get clk %s (index %d): %d\n",
>> +                               node->name, __clk_get_name(clk), i, error);
>> +                       goto err_out;
>> +               }
>> +
>> +               error = clk_prepare(clk);
>> +               if (error) {
>> +                       dev_err(pmu->dev,
>> +                               "%s: failed to prepare clk %s (index %d): %d\n",
>> +                               node->name, __clk_get_name(clk), i, error);
>> +                       clk_put(clk);
>> +                       goto err_out;
>> +               }
>> +
>> +               pd->clks[i] = clk;
>> +
>> +               dev_dbg(pmu->dev, "added clock '%s' to domain '%s'\n",
>> +                       __clk_get_name(clk), node->name);
>> +       }
>> +
>> +       pmu->genpd_data.domains[id] = &pd->genpd;
>> +       return 0;
>> +
>> +err_out:
>> +       while (--i >= 0) {
>> +               clk_unprepare(pd->clks[i]);
>> +               clk_put(pd->clks[i]);
>> +       }
>> +       return error;
>> +}
>> +
>> +static void rockchip_pm_remove_one_domain(struct rockchip_pm_domain *pd)
>> +{
>> +       int i;
>> +
>> +       for (i = 0; i < pd->num_clks; i++) {
>> +               clk_unprepare(pd->clks[i]);
>> +               clk_put(pd->clks[i]);
>> +       }
>> +
>> +       /* devm will free our memory */
>> +}
>> +
>> +static void rockchip_pm_domain_cleanup(struct rockchip_pmu *pmu)
>> +{
>> +       struct generic_pm_domain *genpd;
>> +       struct rockchip_pm_domain *pd;
>> +       int i;
>> +
>> +       for (i = 0; i < pmu->genpd_data.num_domains; i++) {
>> +               genpd = pmu->genpd_data.domains[i];
>> +               if (genpd) {
>> +                       pd = to_rockchip_pd(genpd);
>> +                       rockchip_pm_remove_one_domain(pd);
>> +               }
>> +       }
>> +
>> +       /* devm will free our memory */
>> +}
>> +
>> +static int rockchip_pm_domain_probe(struct platform_device *pdev)
>> +{
>> +       struct device *dev = &pdev->dev;
>> +       struct device_node *np = dev->of_node;
>> +       struct device_node *node;
>> +       struct rockchip_pmu *pmu;
>> +       const struct of_device_id *match;
>> +       const struct rockchip_pmu_info *pmu_info;
>> +       int error;
>> +
>> +       if (!np) {
>> +               dev_err(dev, "device tree node not found\n");
>> +               return -ENXIO;
>> +       }
>> +
>> +       match = of_match_device(dev->driver->of_match_table, dev);
>> +       if (!match || !match->data) {
>> +               dev_err(dev, "missing pmu data\n");
>> +               return -EINVAL;
>> +       }
>> +
>> +       pmu_info = match->data;
>> +
>> +       pmu = devm_kzalloc(dev,
>> +                          sizeof(*pmu) +
>> +                               pmu_info->num_domains * sizeof(pmu->domains[0]),
>> +                          GFP_KERNEL);
>> +       if (!pmu)
>> +               return -ENOMEM;
>> +
>> +       pmu->dev = &pdev->dev;
>> +       mutex_init(&pmu->mutex);
>> +
>> +       pmu->info = pmu_info;
>> +
>> +       pmu->genpd_data.domains = pmu->domains;
>> +       pmu->genpd_data.num_domains = pmu_info->num_domains;
>> +
>> +       node = of_parse_phandle(np, "rockchip,pmu", 0);
>> +       pmu->regmap = syscon_node_to_regmap(node);
>> +       of_node_put(node);
>> +       if (IS_ERR(pmu->regmap)) {
>> +               error = PTR_ERR(pmu->regmap);
>> +               dev_err(dev, "failed to get PMU regmap: %d\n", error);
>> +               return error;
>> +       }
>> +
>> +       error = -ENXIO;
>> +
>> +       for_each_available_child_of_node(np, node) {
>> +               error = rockchip_pm_add_one_domain(pmu, node);
>> +               if (error) {
>> +                       dev_err(dev, "failed to handle node %s: %d\n",
>> +                               node->name, error);
>> +                       goto err_out;
>> +               }
>> +       }
>> +
>> +       if (error) {
>> +               dev_dbg(dev, "no power domains defined\n");
> Perhaps dev_err() or dev_warn(), to ensure this message is seen.
> But, not have defined power domains doesn't seem like a fatal error.
> Perhaps just return 0?
>
> -Daniel
>

Fixed.


Thanks!
>> +               goto err_out;
>> +       }
>> +
>> +       of_genpd_add_provider_onecell(np, &pmu->genpd_data);
>> +
>> +       return 0;
>> +
>> +err_out:
>> +       rockchip_pm_domain_cleanup(pmu);
>> +       return error;
>> +}
>> +
>> +static const struct rockchip_domain_info rk3288_pm_domains[] = {
>> +       [RK3288_PD_GPU]         = DOMAIN_RK3288(9, 9, 2),
>> +       [RK3288_PD_VIO]         = DOMAIN_RK3288(7, 7, 4),
>> +       [RK3288_PD_VIDEO]       = DOMAIN_RK3288(8, 8, 3),
>> +       [RK3288_PD_HEVC]        = DOMAIN_RK3288(14, 10, 9),
>> +};
>> +
>> +static const struct rockchip_pmu_info rk3288_pmu = {
>> +       .pwr_offset = 0x08,
>> +       .status_offset = 0x0c,
>> +       .req_offset = 0x10,
>> +       .idle_offset = 0x14,
>> +       .ack_offset = 0x14,
>> +       .num_domains = ARRAY_SIZE(rk3288_pm_domains),
>> +       .domain_info = rk3288_pm_domains,
>> +};
>> +
>> +static const struct of_device_id rockchip_pm_domain_dt_match[] = {
>> +       {
>> +               .compatible = "rockchip,rk3288-power-controller",
>> +               .data = (void *)&rk3288_pmu,
>> +       },
>> +       { /* sentinel */ },
>> +};
>> +MODULE_DEVICE_TABLE(of, rockchip_pm_domain_dt_match);
>> +
>> +static struct platform_driver rockchip_pm_domain_driver = {
>> +       .probe = rockchip_pm_domain_probe,
>> +       .driver = {
>> +               .name   = "rockchip-pm-domain",
>> +               .owner  = THIS_MODULE,
>> +               .of_match_table = rockchip_pm_domain_dt_match,
>> +               /*
>> +                * We can't forcibly eject devices form power domain,
>> +                * so we can't really remove power domains once they
>> +                * were added.
>> +                */
>> +               .suppress_bind_attrs = true,
>> +       },
>> +};
>> +
>> +static int __init rockchip_pm_domain_drv_register(void)
>> +{
>> +       return platform_driver_register(&rockchip_pm_domain_driver);
>> +}
>> +postcore_initcall(rockchip_pm_domain_drv_register);
>> diff --git a/include/dt-bindings/power-domain/rk3288.h b/include/dt-bindings/power-domain/rk3288.h
>> new file mode 100644
>> index 0000000..ca68c11
>> --- /dev/null
>> +++ b/include/dt-bindings/power-domain/rk3288.h
>> @@ -0,0 +1,11 @@
>> +#ifndef __DT_BINDINGS_POWER_DOMAIN_RK3288_H__
>> +#define __DT_BINDINGS_POWER_DOMAIN_RK3288_H__
>> +
>> +/* RK3288 power domain index */
>> +#define RK3288_PD_GPU          0
>> +#define RK3288_PD_VIO          1
>> +#define RK3288_PD_VIDEO        2
>> +#define RK3288_PD_HEVC         3
>> +#define RK3288_PD_PERI         4
>> +
>> +#endif
>> --
>> 1.9.1
>>
>>
>>
>> _______________________________________________
>> Linux-rockchip mailing list
>> Linux-rockchip@lists.infradead.org
>> http://lists.infradead.org/mailman/listinfo/linux-rockchip
>
>

-- 
Best regards,
Caesar



^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2014-11-17  3:54 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-11-12 22:01 [PATCH v11 0/3] ARM: rk3288: Add PM Domain support Caesar Wang
2014-11-12 22:01 ` [PATCH v11 1/3] dt-bindings: add document of Rockchip power domain Caesar Wang
2014-11-12 22:01 ` [PATCH v11 2/3] power-domain: rockchip: add power doamin driver Caesar Wang
2014-11-13 17:29   ` Doug Anderson
2014-11-13 19:35   ` Daniel Kurtz
2014-11-13 20:20     ` Dmitry Torokhov
2014-11-17  3:53     ` Caesar Wang
2014-11-14  2:57   ` Daniel Kurtz
2014-11-12 22:01 ` [PATCH v11 3/3] ARM: dts: add rk3288 power-domain node Caesar Wang
2014-11-13 17:33   ` Doug Anderson

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).