mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-06 19:08:57 +09:00
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 <finley.xiao@rock-chips.com> Signed-off-by: Elaine Zhang <zhangqing@rock-chips.com> Signed-off-by: YouMin Chen <cym@rock-chips.com>
This commit is contained in:
@@ -8,10 +8,17 @@
|
|||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/clk-provider.h>
|
#include <linux/clk-provider.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/rockchip/rockchip_sip.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <soc/rockchip/rockchip_sip.h>
|
#include <soc/rockchip/rockchip_sip.h>
|
||||||
|
#include <soc/rockchip/scpi.h>
|
||||||
|
#include <uapi/drm/drm_mode.h>
|
||||||
|
|
||||||
#include "clk.h"
|
#include "clk.h"
|
||||||
|
|
||||||
|
#define MHZ (1000000)
|
||||||
|
|
||||||
struct rockchip_ddrclk {
|
struct rockchip_ddrclk {
|
||||||
struct clk_hw hw;
|
struct clk_hw hw;
|
||||||
void __iomem *reg_base;
|
void __iomem *reg_base;
|
||||||
@@ -26,6 +33,30 @@ struct rockchip_ddrclk {
|
|||||||
|
|
||||||
#define to_rockchip_ddrclk_hw(hw) container_of(hw, struct rockchip_ddrclk, hw)
|
#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,
|
static int rockchip_ddrclk_sip_set_rate(struct clk_hw *hw, unsigned long drate,
|
||||||
unsigned long prate)
|
unsigned long prate)
|
||||||
{
|
{
|
||||||
@@ -87,6 +118,118 @@ static const struct clk_ops rockchip_ddrclk_sip_ops = {
|
|||||||
.get_parent = rockchip_ddrclk_get_parent,
|
.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,
|
struct clk *rockchip_clk_register_ddrclk(const char *name, int flags,
|
||||||
const char *const *parent_names,
|
const char *const *parent_names,
|
||||||
u8 num_parents, int mux_offset,
|
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:
|
case ROCKCHIP_DDRCLK_SIP:
|
||||||
init.ops = &rockchip_ddrclk_sip_ops;
|
init.ops = &rockchip_ddrclk_sip_ops;
|
||||||
break;
|
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:
|
default:
|
||||||
pr_err("%s: unsupported ddrclk type %d\n", __func__, ddr_flag);
|
pr_err("%s: unsupported ddrclk type %d\n", __func__, ddr_flag);
|
||||||
kfree(ddrclk);
|
kfree(ddrclk);
|
||||||
|
|||||||
@@ -467,8 +467,14 @@ struct clk *rockchip_clk_register_mmc(const char *name,
|
|||||||
/*
|
/*
|
||||||
* DDRCLK flags, including method of setting the rate
|
* DDRCLK flags, including method of setting the rate
|
||||||
* ROCKCHIP_DDRCLK_SIP: use SIP call to bl31 to change ddrclk 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_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,
|
struct clk *rockchip_clk_register_ddrclk(const char *name, int flags,
|
||||||
const char *const *parent_names,
|
const char *const *parent_names,
|
||||||
|
|||||||
Reference in New Issue
Block a user