pd: rk3368: add rk3368 power domain support (as pd clk)
authordkl <dkl@rock-chips.com>
Sat, 14 Mar 2015 12:03:26 +0000 (20:03 +0800)
committerdkl <dkl@rock-chips.com>
Mon, 16 Mar 2015 09:53:51 +0000 (17:53 +0800)
Signed-off-by: dkl <dkl@rock-chips.com>
arch/arm64/boot/dts/rk3368-clocks.dtsi
arch/arm64/boot/dts/rk3368.dtsi
drivers/clk/rockchip/Makefile
drivers/clk/rockchip/clk-pd.c
drivers/clk/rockchip/pd-rk3368.c [new file with mode: 0644]
include/dt-bindings/clock/rockchip.h
include/linux/rockchip/cru.h
include/linux/rockchip/pmu.h [changed mode: 0755->0644]

index b9fef1688692ec881bfabb7154a3e8207f7c7621..ced3d18b012029b11b8d2cdf8bac7af650be000b 100644 (file)
                        };
                };
 
+               pd_cons {
+                       compatible = "rockchip,rk-pd-cons";
+
+                       pd_gpu_0: pd_gpu_0 {
+                               compatible = "rockchip,rk-pd-clock";
+                               clock-output-names = "pd_gpu_0";
+                               rockchip,pd-id = <CLK_PD_GPU_0>;
+                               #clock-cells = <0>;
+                       };
+
+                       pd_gpu_1: pd_gpu_1 {
+                               compatible = "rockchip,rk-pd-clock";
+                               clocks = <&pd_gpu_0>;
+                               clock-output-names = "pd_gpu_1";
+                               rockchip,pd-id = <CLK_PD_GPU_1>;
+                               #clock-cells = <0>;
+                       };
+
+                       pd_video: pd_video {
+                               compatible = "rockchip,rk-pd-clock";
+                               clock-output-names = "pd_video";
+                               rockchip,pd-id = <CLK_PD_VIDEO>;
+                               #clock-cells = <0>;
+                       };
+
+                       pd_vio: pd_vio {
+                               compatible = "rockchip,rk-pd-clock";
+                               clock-output-names = "pd_vio";
+                               rockchip,pd-id = <CLK_PD_VIO>;
+                               #clock-cells = <0>;
+                       };
+
+                       pd_hevc: pd_hevc {
+                               compatible = "rockchip,rk-pd-clock";
+                               clocks = <&pd_video>;
+                               clock-output-names = "pd_hevc";
+                               rockchip,pd-id = <CLK_PD_VIRT>;
+                               #clock-cells = <0>;
+                       };
+
+                       pd_vop: pd_vop {
+                               compatible = "rockchip,rk-pd-clock";
+                               clocks = <&pd_vio>;
+                               clock-output-names = "pd_vop";
+                               rockchip,pd-id = <CLK_PD_VIRT>;
+                               #clock-cells = <0>;
+                       };
+
+                       pd_isp: pd_isp {
+                               compatible = "rockchip,rk-pd-clock";
+                               clocks = <&pd_vio>;
+                               clock-output-names = "pd_isp";
+                               rockchip,pd-id = <CLK_PD_VIRT>;
+                               #clock-cells = <0>;
+                       };
+
+                       pd_iep: pd_iep {
+                               compatible = "rockchip,rk-pd-clock";
+                               clocks = <&pd_vio>;
+                               clock-output-names = "pd_iep";
+                               rockchip,pd-id = <CLK_PD_VIRT>;
+                               #clock-cells = <0>;
+                       };
+
+                       pd_rga: pd_rga {
+                               compatible = "rockchip,rk-pd-clock";
+                               clocks = <&pd_vio>;
+                               clock-output-names = "pd_rga";
+                               rockchip,pd-id = <CLK_PD_VIRT>;
+                               #clock-cells = <0>;
+                       };
+
+                       pd_mipicsi: pd_mipicsi {
+                               compatible = "rockchip,rk-pd-clock";
+                               clocks = <&pd_vio>;
+                               clock-output-names = "pd_mipicsi";
+                               rockchip,pd-id = <CLK_PD_VIRT>;
+                               #clock-cells = <0>;
+                       };
+
+                       pd_mipidsi: pd_mipidsi {
+                               compatible = "rockchip,rk-pd-clock";
+                               clocks = <&pd_vio>;
+                               clock-output-names = "pd_mipidsi";
+                               rockchip,pd-id = <CLK_PD_VIRT>;
+                               #clock-cells = <0>;
+                       };
+
+                       pd_lvds: pd_lvds {
+                               compatible = "rockchip,rk-pd-clock";
+                               clocks = <&pd_vio>;
+                               clock-output-names = "pd_lvds";
+                               rockchip,pd-id = <CLK_PD_VIRT>;
+                               #clock-cells = <0>;
+                       };
+
+                       pd_hdmi: pd_hdmi {
+                               compatible = "rockchip,rk-pd-clock";
+                               clocks = <&pd_vio>;
+                               clock-output-names = "pd_hdmi";
+                               rockchip,pd-id = <CLK_PD_VIRT>;
+                               #clock-cells = <0>;
+                       };
+
+                       pd_edp: pd_edp {
+                               compatible = "rockchip,rk-pd-clock";
+                               clocks = <&pd_vio>;
+                               clock-output-names = "pd_edp";
+                               rockchip,pd-id = <CLK_PD_VIRT>;
+                               #clock-cells = <0>;
+                       };
+               };
+
                clock_regs {
                        compatible = "rockchip,rk-clock-regs";
                        #address-cells = <1>;
index f950ebc53018921e92381f29230b480de05a2ece..d1b7500af1baf8dccd7db1e6d26f8dc49dcb2607 100755 (executable)
        rockchip_clocks_enable: clocks-enable {
                compatible = "rockchip,clocks-enable";
                clocks =
+               <&pd_vio>,
+               <&pd_video>,
+               <&pd_gpu_0>,
+               <&pd_gpu_1>,
+
                        /*PLL*/
                        <&clk_apllb>,
                        <&clk_aplll>,
index 5ee45b65db56b74e5a9b8ee7694ac78730f5f8ae..9c9e0e099596d80c736aec5d0e412f358878d817 100644 (file)
@@ -2,3 +2,4 @@ obj-y   += clk.o
 obj-y  += clk-ops.o
 obj-y  += clk-pll.o
 obj-y  += clk-pd.o
+obj-y  += pd-rk3368.o
index 14fc1e47e97eff296c0a22eec032591f1bafefc0..4748cb3b15e9a836620843a86986a14f2588fca4 100644 (file)
@@ -105,7 +105,7 @@ static int clk_pd_endisable(struct clk_hw *hw, bool enable)
        if (pd->lock)
                spin_lock_irqsave(pd->lock, flags);
 
-       /* ret = rockchip_pmu_ops.set_power_domain(pd->id, enable); */
+       ret = rockchip_pmu_ops.set_power_domain(pd->id, enable);
 
        if (pd->lock)
                spin_unlock_irqrestore(pd->lock, flags);
@@ -135,14 +135,12 @@ static void clk_pd_disable(struct clk_hw *hw)
        __clk_pd_notify(hw->clk, RK_CLK_PD_POST_DISABLE);
 }
 
-/*
 static int clk_pd_is_enabled(struct clk_hw *hw)
 {
        struct clk_pd *pd = to_clk_pd(hw);
 
        return rockchip_pmu_ops.power_domain_is_on(pd->id);
 }
-*/
 
 static int clk_pd_prepare(struct clk_hw *hw)
 {
@@ -161,7 +159,7 @@ const struct clk_ops clk_pd_ops = {
        .unprepare = clk_pd_unprepare,
        .enable = clk_pd_enable,
        .disable = clk_pd_disable,
-       /*.is_enabled = clk_pd_is_enabled,*/
+       .is_enabled = clk_pd_is_enabled,
 };
 
 static int clk_pd_virt_enable(struct clk_hw *hw)
diff --git a/drivers/clk/rockchip/pd-rk3368.c b/drivers/clk/rockchip/pd-rk3368.c
new file mode 100644 (file)
index 0000000..2f6cb2c
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * Power domain support for Rockchip RK3368
+ *
+ * Copyright (C) 2014-2015 ROCKCHIP, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/rockchip/pmu.h>
+#include <linux/rockchip/cru.h>
+
+#include "clk-ops.h"
+
+static void __iomem *rk_pmu_base;
+
+static u32 pmu_readl(u32 offset)
+{
+       return readl_relaxed(rk_pmu_base + (offset));
+}
+
+static void pmu_writel(u32 val, u32 offset)
+{
+       writel_relaxed(val, rk_pmu_base + (offset));
+       dsb(sy);
+}
+
+static const u8 pmu_pd_map[] = {
+       [PD_PERI] = 13,
+       [PD_VIDEO] = 14,
+       [PD_VIO] = 15,
+       [PD_GPU_0] = 16,
+       [PD_GPU_1] = 17,
+};
+
+static const u8 pmu_st_map[] = {
+       [PD_PERI] = 12,
+       [PD_VIDEO] = 13,
+       [PD_VIO] = 14,
+       [PD_GPU_0] = 15,
+       [PD_GPU_1] = 16,
+};
+
+static bool rk3368_pmu_power_domain_is_on(enum pmu_power_domain pd)
+{
+       /* 1'b0: power on, 1'b1: power off */
+       return !(pmu_readl(RK3368_PMU_PWRDN_ST) & BIT(pmu_st_map[pd]));
+}
+
+static DEFINE_SPINLOCK(pmu_idle_lock);
+
+static const u8 pmu_idle_map[] = {
+       [IDLE_REQ_GPU] = 2,
+       [IDLE_REQ_BUS] = 4,
+       [IDLE_REQ_PERI] = 6,
+       [IDLE_REQ_VIDEO] = 7,
+       [IDLE_REQ_VIO] = 8,
+};
+
+static int rk3368_pmu_set_idle_request(enum pmu_idle_req req, bool idle)
+{
+       u32 bit = pmu_idle_map[req];
+       u32 idle_mask = BIT(bit) | BIT(bit + 16);
+       u32 idle_target = (idle << bit) | (idle << (bit + 16));
+       u32 mask = BIT(bit);
+       u32 val;
+       unsigned long flags;
+
+       spin_lock_irqsave(&pmu_idle_lock, flags);
+
+       val = pmu_readl(RK3368_PMU_IDLE_REQ);
+       if (idle)
+               val |=  mask;
+       else
+               val &= ~mask;
+       pmu_writel(val, RK3368_PMU_IDLE_REQ);
+       dsb(sy);
+
+       while ((pmu_readl(RK3368_PMU_IDLE_ST) & idle_mask) != idle_target)
+               ;
+
+       spin_unlock_irqrestore(&pmu_idle_lock, flags);
+
+       return 0;
+}
+
+static DEFINE_SPINLOCK(pmu_pd_lock);
+
+static noinline void rk3368_do_pmu_set_power_domain
+                                       (enum pmu_power_domain domain, bool on)
+{
+       u8 pd = pmu_pd_map[domain];
+       u32 val = pmu_readl(RK3368_PMU_PWRDN_CON);
+
+       if (on)
+               val &= ~BIT(pd);
+       else
+               val |=  BIT(pd);
+
+       pmu_writel(val, RK3368_PMU_PWRDN_CON);
+       dsb(sy);
+
+       while ((pmu_readl(RK3368_PMU_PWRDN_ST) & BIT(pmu_st_map[domain])) == on)
+               ;
+}
+
+static int rk3368_pmu_set_power_domain(enum pmu_power_domain pd, bool on)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&pmu_pd_lock, flags);
+
+       if (rk3368_pmu_power_domain_is_on(pd) == on)
+               goto out;
+
+       if (!on) {
+               /* if power down, idle request to NIU first */
+               if (pd == PD_VIO)
+                       rk3368_pmu_set_idle_request(IDLE_REQ_VIO, true);
+               else if (pd == PD_VIDEO)
+                       rk3368_pmu_set_idle_request(IDLE_REQ_VIDEO, true);
+               else if (pd == PD_GPU_0)
+                       rk3368_pmu_set_idle_request(IDLE_REQ_GPU, true);
+               else if (pd == PD_PERI)
+                       rk3368_pmu_set_idle_request(IDLE_REQ_PERI, true);
+       }
+
+       rk3368_do_pmu_set_power_domain(pd, on);
+
+       if (on) {
+               /* if power up, idle request release to NIU */
+               if (pd == PD_VIO)
+                       rk3368_pmu_set_idle_request(IDLE_REQ_VIO, false);
+               else if (pd == PD_VIDEO)
+                       rk3368_pmu_set_idle_request(IDLE_REQ_VIDEO, false);
+               else if (pd == PD_GPU_0)
+                       rk3368_pmu_set_idle_request(IDLE_REQ_GPU, false);
+               else if (pd == PD_PERI)
+                       rk3368_pmu_set_idle_request(IDLE_REQ_PERI, false);
+       }
+
+out:
+       spin_unlock_irqrestore(&pmu_pd_lock, flags);
+       return 0;
+}
+
+static int rk3368_sys_set_power_domain(enum pmu_power_domain pd, bool on)
+{
+       u32 clks_ungating[RK3368_CRU_CLKGATES_CON_CNT];
+       u32 clks_save[RK3368_CRU_CLKGATES_CON_CNT];
+       u32 i, ret;
+
+       for (i = 0; i < RK3368_CRU_CLKGATES_CON_CNT; i++) {
+               clks_save[i] = cru_readl(RK3368_CRU_CLKGATES_CON(i));
+               clks_ungating[i] = 0;
+       }
+
+       for (i = 0; i < RK3368_CRU_CLKGATES_CON_CNT; i++)
+               cru_writel(0xffff0000, RK3368_CRU_CLKGATES_CON(i));
+
+       ret = rk3368_pmu_set_power_domain(pd, on);
+
+       for (i = 0; i < RK3368_CRU_CLKGATES_CON_CNT; i++)
+               cru_writel(clks_save[i] | 0xffff0000,
+                          RK3368_CRU_CLKGATES_CON(i));
+
+       return ret;
+}
+
+static int __init rk3368_init_rockchip_pmu_ops(void)
+{
+       struct device_node *node;
+
+       node = of_find_compatible_node(NULL, NULL, "rockchip,rk3368-pmu");
+       if (!node) {
+               pr_err("%s: could not find pmu dt node\n", __func__);
+               return -ENXIO;
+       }
+
+       rk_pmu_base = of_iomap(node, 0);
+       if (!rk_pmu_base) {
+               pr_err("%s: could not map pmu registers\n", __func__);
+               return -ENXIO;
+       }
+
+       rockchip_pmu_ops.set_power_domain = rk3368_sys_set_power_domain;
+       rockchip_pmu_ops.power_domain_is_on = rk3368_pmu_power_domain_is_on;
+
+       return 0;
+}
+
+arch_initcall(rk3368_init_rockchip_pmu_ops);
index dacc17a610ea113e80ddaa32946013b350f46e07..4f8a6d4ea601378d78f7eda1855972d476cdbfd6 100644 (file)
@@ -88,6 +88,9 @@
 #define CLK_PD_SCU             11
 #define CLK_PD_VIDEO           12
 #define CLK_PD_VIO             13
+#define CLK_PD_GPU_0           14
+#define CLK_PD_GPU_1           15
+
 #define CLK_PD_VIRT            255
 
 /* reset flag */
index 4bd4cfbed11909e8c160c6f683e0a4fafb859517..95924f555f2fdc994a2b9e4a0ec565eb9723313d 100755 (executable)
@@ -247,6 +247,7 @@ enum rk312x_cru_clk_gate {
 /*******************CRU OFFSET*********************/
 #define RK3368_CRU_CLKSEL_CON          0x100
 #define RK3368_CRU_CLKGATE_CON         0x200
+#define RK3368_CRU_CLKGATES_CON_CNT     25
 
 #define RK3368_PLL_CONS(id, i)         ((id) * 0x10 + ((i) * 4))
 #define RK3368_CRU_CLKSELS_CON(i)      (RK3368_CRU_CLKSEL_CON + ((i) * 4))
old mode 100755 (executable)
new mode 100644 (file)
index 02f379c..ece7b27
 #define RK312X_PMU_SYS_REG2                    0x40
 #define RK312X_PMU_SYS_REG3                    0x44
 
+#define RK3368_PMU_PWRDN_CON           0x0c
+#define RK3368_PMU_PWRDN_ST            0x10
+#define RK3368_PMU_IDLE_REQ            0x3c
+#define RK3368_PMU_IDLE_ST             0x40
 
 enum pmu_power_domain {
        PD_BCPU,
@@ -102,6 +106,8 @@ enum pmu_power_domain {
        PD_SCU,
        PD_VIDEO,
        PD_VIO,
+       PD_GPU_0,
+       PD_GPU_1,
 };
 
 enum pmu_idle_req {