From d2b92a90eabd35e21909bf52f85baeefb8f5c0f1 Mon Sep 17 00:00:00 2001 From: Finley Xiao Date: Sat, 25 Mar 2017 20:02:35 +0800 Subject: [PATCH] clk: rockchip: support setting ddr clock via SCPI and SIP Version 2 APIs On rk3368, let a mcu scaling ddr clock via SCPI (System Control and Power Interface) APIs. Change-Id: I95342b876caad991e6d1319c5e4ec793365c7981 Signed-off-by: Finley Xiao Signed-off-by: Elaine Zhang Signed-off-by: YouMin Chen --- drivers/clk/rockchip/clk-ddr.c | 149 +++++++++++++++++++++++++++++++++ drivers/clk/rockchip/clk.h | 6 ++ 2 files changed, 155 insertions(+) diff --git a/drivers/clk/rockchip/clk-ddr.c b/drivers/clk/rockchip/clk-ddr.c index 86718c54e56b..2827255ae290 100644 --- a/drivers/clk/rockchip/clk-ddr.c +++ b/drivers/clk/rockchip/clk-ddr.c @@ -8,10 +8,17 @@ #include #include #include +#include +#include #include #include +#include +#include + #include "clk.h" +#define MHZ (1000000) + struct rockchip_ddrclk { struct clk_hw hw; void __iomem *reg_base; @@ -26,6 +33,30 @@ struct rockchip_ddrclk { #define to_rockchip_ddrclk_hw(hw) container_of(hw, struct rockchip_ddrclk, hw) +struct share_params_ddrclk { + u32 hz; + u32 lcdc_type; +}; + +struct rockchip_ddrclk_data { + void __iomem *params; + int (*dmcfreq_wait_complete)(void); +}; + +static struct rockchip_ddrclk_data ddr_data = {NULL, NULL}; + +void rockchip_set_ddrclk_params(void __iomem *params) +{ + ddr_data.params = params; +} +EXPORT_SYMBOL(rockchip_set_ddrclk_params); + +void rockchip_set_ddrclk_dmcfreq_wait_complete(int (*func)(void)) +{ + ddr_data.dmcfreq_wait_complete = func; +} +EXPORT_SYMBOL(rockchip_set_ddrclk_dmcfreq_wait_complete); + static int rockchip_ddrclk_sip_set_rate(struct clk_hw *hw, unsigned long drate, unsigned long prate) { @@ -87,6 +118,118 @@ static const struct clk_ops rockchip_ddrclk_sip_ops = { .get_parent = rockchip_ddrclk_get_parent, }; +static u32 ddr_clk_cached; + +static int rockchip_ddrclk_scpi_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long prate) +{ + u32 ret; + u32 lcdc_type = 0; + struct share_params_ddrclk *p; + + p = (struct share_params_ddrclk *)ddr_data.params; + if (p) + lcdc_type = p->lcdc_type; + + ret = scpi_ddr_set_clk_rate(drate / MHZ, lcdc_type); + if (ret) { + ddr_clk_cached = ret; + ret = 0; + } else { + ddr_clk_cached = 0; + ret = -1; + } + + return ret; +} + +static unsigned long rockchip_ddrclk_scpi_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + if (ddr_clk_cached) + return (MHZ * ddr_clk_cached); + else + return (MHZ * scpi_ddr_get_clk_rate()); +} + +static long rockchip_ddrclk_scpi_round_rate(struct clk_hw *hw, + unsigned long rate, + unsigned long *prate) +{ + rate = rate / MHZ; + rate = (rate / 12) * 12; + + return (rate * MHZ); +} + +static const struct clk_ops rockchip_ddrclk_scpi_ops = { + .recalc_rate = rockchip_ddrclk_scpi_recalc_rate, + .set_rate = rockchip_ddrclk_scpi_set_rate, + .round_rate = rockchip_ddrclk_scpi_round_rate, + .get_parent = rockchip_ddrclk_get_parent, +}; + +static int rockchip_ddrclk_sip_set_rate_v2(struct clk_hw *hw, + unsigned long drate, + unsigned long prate) +{ + struct share_params_ddrclk *p; + struct arm_smccc_res res; + + p = (struct share_params_ddrclk *)ddr_data.params; + if (p) + p->hz = drate; + + res = sip_smc_dram(SHARE_PAGE_TYPE_DDR, 0, + ROCKCHIP_SIP_CONFIG_DRAM_SET_RATE); + + if ((int)res.a1 == SIP_RET_SET_RATE_TIMEOUT) { + if (ddr_data.dmcfreq_wait_complete) + ddr_data.dmcfreq_wait_complete(); + } + + return res.a0; +} + +static unsigned long rockchip_ddrclk_sip_recalc_rate_v2 + (struct clk_hw *hw, unsigned long parent_rate) +{ + struct arm_smccc_res res; + + res = sip_smc_dram(SHARE_PAGE_TYPE_DDR, 0, + ROCKCHIP_SIP_CONFIG_DRAM_GET_RATE); + if (!res.a0) + return res.a1; + else + return 0; +} + +static long rockchip_ddrclk_sip_round_rate_v2(struct clk_hw *hw, + unsigned long rate, + unsigned long *prate) +{ + struct share_params_ddrclk *p; + struct arm_smccc_res res; + + p = (struct share_params_ddrclk *)ddr_data.params; + if (p) + p->hz = rate; + + res = sip_smc_dram(SHARE_PAGE_TYPE_DDR, 0, + ROCKCHIP_SIP_CONFIG_DRAM_ROUND_RATE); + if (!res.a0) + return res.a1; + else + return 0; +} + +static const struct clk_ops rockchip_ddrclk_sip_ops_v2 = { + .recalc_rate = rockchip_ddrclk_sip_recalc_rate_v2, + .set_rate = rockchip_ddrclk_sip_set_rate_v2, + .round_rate = rockchip_ddrclk_sip_round_rate_v2, + .get_parent = rockchip_ddrclk_get_parent, +}; + struct clk *rockchip_clk_register_ddrclk(const char *name, int flags, const char *const *parent_names, u8 num_parents, int mux_offset, @@ -114,6 +257,12 @@ struct clk *rockchip_clk_register_ddrclk(const char *name, int flags, case ROCKCHIP_DDRCLK_SIP: init.ops = &rockchip_ddrclk_sip_ops; break; + case ROCKCHIP_DDRCLK_SCPI: + init.ops = &rockchip_ddrclk_scpi_ops; + break; + case ROCKCHIP_DDRCLK_SIP_V2: + init.ops = &rockchip_ddrclk_sip_ops_v2; + break; default: pr_err("%s: unsupported ddrclk type %d\n", __func__, ddr_flag); kfree(ddrclk); diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h index 4985c5a5c69c..06f000499126 100644 --- a/drivers/clk/rockchip/clk.h +++ b/drivers/clk/rockchip/clk.h @@ -467,8 +467,14 @@ struct clk *rockchip_clk_register_mmc(const char *name, /* * DDRCLK flags, including method of setting the rate * ROCKCHIP_DDRCLK_SIP: use SIP call to bl31 to change ddrclk rate. + * ROCKCHIP_DDRCLK_SCPI: use SCPI APIs to let mcu change ddrclk rate. */ #define ROCKCHIP_DDRCLK_SIP BIT(0) +#define ROCKCHIP_DDRCLK_SCPI 0x02 +#define ROCKCHIP_DDRCLK_SIP_V2 0x03 + +void rockchip_set_ddrclk_params(void __iomem *params); +void rockchip_set_ddrclk_dmcfreq_wait_complete(int (*func)(void)); struct clk *rockchip_clk_register_ddrclk(const char *name, int flags, const char *const *parent_names,