From 68bdc6aff1567f1386809f7b86971a78d6c06036 Mon Sep 17 00:00:00 2001 From: dkl Date: Sat, 14 Mar 2015 20:03:26 +0800 Subject: [PATCH] pd: rk3368: add rk3368 power domain support (as pd clk) Signed-off-by: dkl --- arch/arm64/boot/dts/rk3368-clocks.dtsi | 113 ++++++++++++++ arch/arm64/boot/dts/rk3368.dtsi | 5 + drivers/clk/rockchip/Makefile | 1 + drivers/clk/rockchip/clk-pd.c | 6 +- drivers/clk/rockchip/pd-rk3368.c | 201 +++++++++++++++++++++++++ include/dt-bindings/clock/rockchip.h | 3 + include/linux/rockchip/cru.h | 1 + include/linux/rockchip/pmu.h | 6 + 8 files changed, 332 insertions(+), 4 deletions(-) create mode 100644 drivers/clk/rockchip/pd-rk3368.c mode change 100755 => 100644 include/linux/rockchip/pmu.h diff --git a/arch/arm64/boot/dts/rk3368-clocks.dtsi b/arch/arm64/boot/dts/rk3368-clocks.dtsi index b9fef1688692..ced3d18b0120 100644 --- a/arch/arm64/boot/dts/rk3368-clocks.dtsi +++ b/arch/arm64/boot/dts/rk3368-clocks.dtsi @@ -161,6 +161,119 @@ }; }; + 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 = ; + #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 = ; + #clock-cells = <0>; + }; + + pd_video: pd_video { + compatible = "rockchip,rk-pd-clock"; + clock-output-names = "pd_video"; + rockchip,pd-id = ; + #clock-cells = <0>; + }; + + pd_vio: pd_vio { + compatible = "rockchip,rk-pd-clock"; + clock-output-names = "pd_vio"; + rockchip,pd-id = ; + #clock-cells = <0>; + }; + + pd_hevc: pd_hevc { + compatible = "rockchip,rk-pd-clock"; + clocks = <&pd_video>; + clock-output-names = "pd_hevc"; + rockchip,pd-id = ; + #clock-cells = <0>; + }; + + pd_vop: pd_vop { + compatible = "rockchip,rk-pd-clock"; + clocks = <&pd_vio>; + clock-output-names = "pd_vop"; + rockchip,pd-id = ; + #clock-cells = <0>; + }; + + pd_isp: pd_isp { + compatible = "rockchip,rk-pd-clock"; + clocks = <&pd_vio>; + clock-output-names = "pd_isp"; + rockchip,pd-id = ; + #clock-cells = <0>; + }; + + pd_iep: pd_iep { + compatible = "rockchip,rk-pd-clock"; + clocks = <&pd_vio>; + clock-output-names = "pd_iep"; + rockchip,pd-id = ; + #clock-cells = <0>; + }; + + pd_rga: pd_rga { + compatible = "rockchip,rk-pd-clock"; + clocks = <&pd_vio>; + clock-output-names = "pd_rga"; + rockchip,pd-id = ; + #clock-cells = <0>; + }; + + pd_mipicsi: pd_mipicsi { + compatible = "rockchip,rk-pd-clock"; + clocks = <&pd_vio>; + clock-output-names = "pd_mipicsi"; + rockchip,pd-id = ; + #clock-cells = <0>; + }; + + pd_mipidsi: pd_mipidsi { + compatible = "rockchip,rk-pd-clock"; + clocks = <&pd_vio>; + clock-output-names = "pd_mipidsi"; + rockchip,pd-id = ; + #clock-cells = <0>; + }; + + pd_lvds: pd_lvds { + compatible = "rockchip,rk-pd-clock"; + clocks = <&pd_vio>; + clock-output-names = "pd_lvds"; + rockchip,pd-id = ; + #clock-cells = <0>; + }; + + pd_hdmi: pd_hdmi { + compatible = "rockchip,rk-pd-clock"; + clocks = <&pd_vio>; + clock-output-names = "pd_hdmi"; + rockchip,pd-id = ; + #clock-cells = <0>; + }; + + pd_edp: pd_edp { + compatible = "rockchip,rk-pd-clock"; + clocks = <&pd_vio>; + clock-output-names = "pd_edp"; + rockchip,pd-id = ; + #clock-cells = <0>; + }; + }; + clock_regs { compatible = "rockchip,rk-clock-regs"; #address-cells = <1>; diff --git a/arch/arm64/boot/dts/rk3368.dtsi b/arch/arm64/boot/dts/rk3368.dtsi index f950ebc53018..d1b7500af1ba 100755 --- a/arch/arm64/boot/dts/rk3368.dtsi +++ b/arch/arm64/boot/dts/rk3368.dtsi @@ -555,6 +555,11 @@ 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>, diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile index 5ee45b65db56..9c9e0e099596 100644 --- a/drivers/clk/rockchip/Makefile +++ b/drivers/clk/rockchip/Makefile @@ -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 diff --git a/drivers/clk/rockchip/clk-pd.c b/drivers/clk/rockchip/clk-pd.c index 14fc1e47e97e..4748cb3b15e9 100644 --- a/drivers/clk/rockchip/clk-pd.c +++ b/drivers/clk/rockchip/clk-pd.c @@ -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 index 000000000000..2f6cb2c879a0 --- /dev/null +++ b/drivers/clk/rockchip/pd-rk3368.c @@ -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 +#include +#include +#include + +#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); diff --git a/include/dt-bindings/clock/rockchip.h b/include/dt-bindings/clock/rockchip.h index dacc17a610ea..4f8a6d4ea601 100644 --- a/include/dt-bindings/clock/rockchip.h +++ b/include/dt-bindings/clock/rockchip.h @@ -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 */ diff --git a/include/linux/rockchip/cru.h b/include/linux/rockchip/cru.h index 4bd4cfbed119..95924f555f2f 100755 --- a/include/linux/rockchip/cru.h +++ b/include/linux/rockchip/cru.h @@ -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)) diff --git a/include/linux/rockchip/pmu.h b/include/linux/rockchip/pmu.h old mode 100755 new mode 100644 index 02f379c929f8..ece7b277f5b9 --- a/include/linux/rockchip/pmu.h +++ b/include/linux/rockchip/pmu.h @@ -86,6 +86,10 @@ #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 { -- 2.34.1