clk: rockchip: Add support for cpu boost

Change-Id: Ie473d60c1076e6b137e2bc7407db73624cd6145f
Signed-off-by: Finley Xiao <finley.xiao@rock-chips.com>
Signed-off-by: Elaine Zhang <zhangqing@rock-chips.com>
This commit is contained in:
Finley Xiao
2019-10-24 20:16:08 +08:00
committed by Tao Huang
parent 29d9818f3e
commit f44db7e0f1
3 changed files with 394 additions and 1 deletions

View File

@@ -51,6 +51,7 @@
*/
struct rockchip_cpuclk {
struct clk_hw hw;
struct clk_hw *pll_hw;
struct clk_mux cpu_mux;
const struct clk_ops *cpu_mux_ops;
@@ -133,6 +134,8 @@ static int rockchip_cpuclk_pre_rate_change(struct rockchip_cpuclk *cpuclk,
return -EINVAL;
}
rockchip_boost_enable_recovery_sw_low(cpuclk->pll_hw);
alt_prate = clk_get_rate(cpuclk->alt_parent);
spin_lock_irqsave(cpuclk->lock, flags);
@@ -175,6 +178,7 @@ static int rockchip_cpuclk_pre_rate_change(struct rockchip_cpuclk *cpuclk,
reg_data->mux_core_shift),
cpuclk->reg_base + reg_data->core_reg);
}
rockchip_boost_add_core_div(cpuclk->pll_hw, alt_prate);
spin_unlock_irqrestore(cpuclk->lock, flags);
return 0;
@@ -216,6 +220,8 @@ static int rockchip_cpuclk_post_rate_change(struct rockchip_cpuclk *cpuclk,
if (ndata->old_rate > ndata->new_rate)
rockchip_cpuclk_set_dividers(cpuclk, rate);
rockchip_boost_disable_recovery_sw(cpuclk->pll_hw);
spin_unlock_irqrestore(cpuclk->lock, flags);
return 0;
}
@@ -251,7 +257,7 @@ struct clk *rockchip_clk_register_cpuclk(const char *name,
{
struct rockchip_cpuclk *cpuclk;
struct clk_init_data init;
struct clk *clk, *cclk;
struct clk *clk, *cclk, *pll_clk;
int ret;
if (num_parents < 2) {
@@ -281,6 +287,17 @@ struct clk *rockchip_clk_register_cpuclk(const char *name,
cpuclk->reg_data = reg_data;
cpuclk->clk_nb.notifier_call = rockchip_cpuclk_notifier_cb;
cpuclk->hw.init = &init;
if (reg_data->pll_name) {
pll_clk = __clk_lookup(reg_data->pll_name);
if (!pll_clk) {
pr_err("%s: could not lookup pll clock: (%s)\n",
__func__, reg_data->pll_name);
ret = -EINVAL;
goto free_cpuclk;
}
cpuclk->pll_hw = __clk_get_hw(pll_clk);
rockchip_boost_init(cpuclk->pll_hw);
}
cpuclk->alt_parent = __clk_lookup(parent_names[reg_data->mux_core_alt]);
if (!cpuclk->alt_parent) {

View File

@@ -16,6 +16,7 @@
#include <linux/regmap.h>
#include <linux/clk.h>
#include <linux/gcd.h>
#include <linux/mfd/syscon.h>
#include "clk.h"
#define PLL_MODE_MASK 0x3
@@ -44,12 +45,24 @@ struct rockchip_clk_pll {
spinlock_t *lock;
struct rockchip_clk_provider *ctx;
bool boost_enabled;
u32 boost_backup_pll_usage;
unsigned long boost_backup_pll_rate;
unsigned long boost_low_rate;
unsigned long boost_high_rate;
struct regmap *boost;
#ifdef CONFIG_DEBUG_FS
struct hlist_node debug_node;
#endif
};
#define to_rockchip_clk_pll(_hw) container_of(_hw, struct rockchip_clk_pll, hw)
#define to_rockchip_clk_pll_nb(nb) \
container_of(nb, struct rockchip_clk_pll, clk_nb)
static void rockchip_boost_disable_low(struct rockchip_clk_pll *pll);
#define MHZ (1000UL * 1000UL)
#define KHZ (1000UL)
@@ -72,6 +85,10 @@ struct rockchip_clk_pll {
#define MAX_FOUTVCO_FREQ (2000 * MHZ)
static struct rockchip_pll_rate_table auto_table;
#ifdef CONFIG_DEBUG_FS
static HLIST_HEAD(clk_boost_list);
static DEFINE_MUTEX(clk_boost_lock);
#endif
int rockchip_pll_clk_adaptive_scaling(struct clk *clk, int sel)
{
@@ -393,6 +410,30 @@ static int rockchip_rk3036_pll_wait_lock(struct rockchip_clk_pll *pll)
return ret;
}
static unsigned long
rockchip_rk3036_pll_con_to_rate(struct rockchip_clk_pll *pll,
u32 con0, u32 con1)
{
unsigned int fbdiv, postdiv1, refdiv, postdiv2;
u64 rate64 = 24000000;
fbdiv = ((con0 >> RK3036_PLLCON0_FBDIV_SHIFT) &
RK3036_PLLCON0_FBDIV_MASK);
postdiv1 = ((con0 >> RK3036_PLLCON0_POSTDIV1_SHIFT) &
RK3036_PLLCON0_POSTDIV1_MASK);
refdiv = ((con1 >> RK3036_PLLCON1_REFDIV_SHIFT) &
RK3036_PLLCON1_REFDIV_MASK);
postdiv2 = ((con1 >> RK3036_PLLCON1_POSTDIV2_SHIFT) &
RK3036_PLLCON1_POSTDIV2_MASK);
rate64 *= fbdiv;
do_div(rate64, refdiv);
do_div(rate64, postdiv1);
do_div(rate64, postdiv2);
return (unsigned long)rate64;
}
static void rockchip_rk3036_pll_get_params(struct rockchip_clk_pll *pll,
struct rockchip_pll_rate_table *rate)
{
@@ -491,6 +532,8 @@ static int rockchip_rk3036_pll_set_params(struct rockchip_clk_pll *pll,
pllcon |= rate->frac << RK3036_PLLCON2_FRAC_SHIFT;
writel_relaxed(pllcon, pll->reg_base + RK3036_PLLCON(2));
rockchip_boost_disable_low(pll);
/* wait for the pll to lock */
ret = rockchip_rk3036_pll_wait_lock(pll);
if (ret) {
@@ -1282,3 +1325,314 @@ err_mux:
kfree(pll);
return mux_clk;
}
static unsigned long rockchip_pll_con_to_rate(struct rockchip_clk_pll *pll,
u32 con0, u32 con1)
{
switch (pll->type) {
case pll_rk3036:
case pll_rk3328:
return rockchip_rk3036_pll_con_to_rate(pll, con0, con1);
case pll_rk3066:
break;
case pll_rk3399:
break;
default:
pr_warn("%s: Unknown pll type\n", __func__);
}
return 0;
}
void rockchip_boost_init(struct clk_hw *hw)
{
struct rockchip_clk_pll *pll;
struct device_node *np;
u32 value, con0, con1;
if (!hw)
return;
pll = to_rockchip_clk_pll(hw);
np = of_parse_phandle(pll->ctx->cru_node, "rockchip,boost", 0);
if (!np) {
pr_debug("%s: failed to get boost np\n", __func__);
return;
}
pll->boost = syscon_node_to_regmap(np);
if (IS_ERR(pll->boost)) {
pr_debug("%s: failed to get boost regmap\n", __func__);
return;
}
if (!of_property_read_u32(np, "rockchip,boost-low-con0", &con0) &&
!of_property_read_u32(np, "rockchip,boost-low-con1", &con1)) {
pr_debug("boost-low-con=0x%x 0x%x\n", con0, con1);
regmap_write(pll->boost, BOOST_PLL_L_CON(0),
HIWORD_UPDATE(con0, BOOST_PLL_CON_MASK, 0));
regmap_write(pll->boost, BOOST_PLL_L_CON(1),
HIWORD_UPDATE(con1, BOOST_PLL_CON_MASK, 0));
pll->boost_low_rate = rockchip_pll_con_to_rate(pll, con0,
con1);
pr_debug("boost-low-rate=%lu\n", pll->boost_low_rate);
}
if (!of_property_read_u32(np, "rockchip,boost-high-con0", &con0) &&
!of_property_read_u32(np, "rockchip,boost-high-con1", &con1)) {
pr_debug("boost-high-con=0x%x 0x%x\n", con0, con1);
regmap_write(pll->boost, BOOST_PLL_H_CON(0),
HIWORD_UPDATE(con0, BOOST_PLL_CON_MASK, 0));
regmap_write(pll->boost, BOOST_PLL_H_CON(1),
HIWORD_UPDATE(con1, BOOST_PLL_CON_MASK, 0));
pll->boost_high_rate = rockchip_pll_con_to_rate(pll, con0,
con1);
pr_debug("boost-high-rate=%lu\n", pll->boost_high_rate);
}
if (!of_property_read_u32(np, "rockchip,boost-backup-pll", &value)) {
pr_debug("boost-backup-pll=0x%x\n", value);
regmap_write(pll->boost, BOOST_CLK_CON,
HIWORD_UPDATE(value, BOOST_BACKUP_PLL_MASK,
BOOST_BACKUP_PLL_SHIFT));
}
if (!of_property_read_u32(np, "rockchip,boost-backup-pll-usage",
&pll->boost_backup_pll_usage)) {
pr_debug("boost-backup-pll-usage=0x%x\n",
pll->boost_backup_pll_usage);
regmap_write(pll->boost, BOOST_CLK_CON,
HIWORD_UPDATE(pll->boost_backup_pll_usage,
BOOST_BACKUP_PLL_USAGE_MASK,
BOOST_BACKUP_PLL_USAGE_SHIFT));
}
if (!of_property_read_u32(np, "rockchip,boost-switch-threshold",
&value)) {
pr_debug("boost-switch-threshold=0x%x\n", value);
regmap_write(pll->boost, BOOST_SWITCH_THRESHOLD, value);
}
if (!of_property_read_u32(np, "rockchip,boost-statis-threshold",
&value)) {
pr_debug("boost-statis-threshold=0x%x\n", value);
regmap_write(pll->boost, BOOST_STATIS_THRESHOLD, value);
}
if (!of_property_read_u32(np, "rockchip,boost-statis-enable",
&value)) {
pr_debug("boost-statis-enable=0x%x\n", value);
regmap_write(pll->boost, BOOST_BOOST_CON,
HIWORD_UPDATE(value, BOOST_STATIS_ENABLE_MASK,
BOOST_STATIS_ENABLE_SHIFT));
}
if (!of_property_read_u32(np, "rockchip,boost-enable", &value)) {
pr_debug("boost-enable=0x%x\n", value);
regmap_write(pll->boost, BOOST_BOOST_CON,
HIWORD_UPDATE(value, BOOST_ENABLE_MASK,
BOOST_ENABLE_SHIFT));
if (value)
pll->boost_enabled = true;
}
#ifdef CONFIG_DEBUG_FS
if (pll->boost_enabled) {
mutex_lock(&clk_boost_lock);
hlist_add_head(&pll->debug_node, &clk_boost_list);
mutex_unlock(&clk_boost_lock);
}
#endif
}
void rockchip_boost_enable_recovery_sw_low(struct clk_hw *hw)
{
struct rockchip_clk_pll *pll;
unsigned int val;
if (!hw)
return;
pll = to_rockchip_clk_pll(hw);
if (!pll->boost_enabled)
return;
regmap_write(pll->boost, BOOST_BOOST_CON,
HIWORD_UPDATE(1, BOOST_RECOVERY_MASK,
BOOST_RECOVERY_SHIFT));
do {
regmap_read(pll->boost, BOOST_FSM_STATUS, &val);
} while (!(val & BOOST_BUSY_STATE));
regmap_write(pll->boost, BOOST_BOOST_CON,
HIWORD_UPDATE(1, BOOST_SW_CTRL_MASK,
BOOST_SW_CTRL_SHIFT) |
HIWORD_UPDATE(1, BOOST_LOW_FREQ_EN_MASK,
BOOST_LOW_FREQ_EN_SHIFT));
}
static void rockchip_boost_disable_low(struct rockchip_clk_pll *pll)
{
if (!pll->boost_enabled)
return;
regmap_write(pll->boost, BOOST_BOOST_CON,
HIWORD_UPDATE(0, BOOST_LOW_FREQ_EN_MASK,
BOOST_LOW_FREQ_EN_SHIFT));
}
void rockchip_boost_disable_recovery_sw(struct clk_hw *hw)
{
struct rockchip_clk_pll *pll;
if (!hw)
return;
pll = to_rockchip_clk_pll(hw);
if (!pll->boost_enabled)
return;
regmap_write(pll->boost, BOOST_BOOST_CON,
HIWORD_UPDATE(0, BOOST_RECOVERY_MASK,
BOOST_RECOVERY_SHIFT));
regmap_write(pll->boost, BOOST_BOOST_CON,
HIWORD_UPDATE(0, BOOST_SW_CTRL_MASK,
BOOST_SW_CTRL_SHIFT));
}
void rockchip_boost_add_core_div(struct clk_hw *hw, unsigned long prate)
{
struct rockchip_clk_pll *pll;
unsigned int div;
if (!hw)
return;
pll = to_rockchip_clk_pll(hw);
if (!pll->boost_enabled || pll->boost_backup_pll_rate == prate)
return;
/* todo */
if (pll->boost_backup_pll_usage == BOOST_BACKUP_PLL_USAGE_TARGET)
return;
/*
* cpu clock rate should be less than or equal to
* low rate when change pll rate in boost module
*/
if (pll->boost_low_rate && prate > pll->boost_low_rate) {
div = DIV_ROUND_UP(prate, pll->boost_low_rate) - 1;
regmap_write(pll->boost, BOOST_CLK_CON,
HIWORD_UPDATE(div, BOOST_CORE_DIV_MASK,
BOOST_CORE_DIV_SHIFT));
pll->boost_backup_pll_rate = prate;
}
}
#ifdef CONFIG_DEBUG_FS
#include <linux/debugfs.h>
static int boost_summary_show(struct seq_file *s, void *data)
{
struct rockchip_clk_pll *pll = (struct rockchip_clk_pll *)s->private;
u32 boost_count = 0;
u32 freq_cnt0 = 0, freq_cnt1 = 0;
u64 freq_cnt = 0, high_freq_time = 0;
u32 short_count = 0, short_threshold = 0;
u32 interval_time = 0;
seq_puts(s, " device boost_count high_freq_count high_freq_time short_count short_threshold interval_count\n");
seq_puts(s, "------------------------------------------------------------------------------------------------------\n");
seq_printf(s, " %s\n", clk_hw_get_name(&pll->hw));
regmap_read(pll->boost, BOOST_SWITCH_CNT, &boost_count);
regmap_read(pll->boost, BOOST_HIGH_PERF_CNT0, &freq_cnt0);
regmap_read(pll->boost, BOOST_HIGH_PERF_CNT1, &freq_cnt1);
freq_cnt = ((u64)freq_cnt1 << 32) + (u64)freq_cnt0;
high_freq_time = freq_cnt;
do_div(high_freq_time, 24);
regmap_read(pll->boost, BOOST_SHORT_SWITCH_CNT, &short_count);
regmap_read(pll->boost, BOOST_STATIS_THRESHOLD, &short_threshold);
regmap_read(pll->boost, BOOST_SWITCH_THRESHOLD, &interval_time);
seq_printf(s, "%22u %17llu %15llu %12u %16u %15u\n",
boost_count, freq_cnt, high_freq_time, short_count,
short_threshold, interval_time);
return 0;
}
static int boost_summary_open(struct inode *inode, struct file *file)
{
return single_open(file, boost_summary_show, inode->i_private);
}
static const struct file_operations boost_summary_fops = {
.open = boost_summary_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int boost_config_show(struct seq_file *s, void *data)
{
struct rockchip_clk_pll *pll = (struct rockchip_clk_pll *)s->private;
seq_printf(s, "boost_enabled: %d\n", pll->boost_enabled);
seq_printf(s, "boost_low_rate: %lu\n", pll->boost_low_rate);
seq_printf(s, "boost_high_rate: %lu\n", pll->boost_high_rate);
return 0;
}
static int boost_config_open(struct inode *inode, struct file *file)
{
return single_open(file, boost_config_show, inode->i_private);
}
static const struct file_operations boost_config_fops = {
.open = boost_config_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int boost_debug_create_one(struct rockchip_clk_pll *pll,
struct dentry *rootdir)
{
struct dentry *pdentry, *d;
pdentry = debugfs_lookup(clk_hw_get_name(&pll->hw), rootdir);
if (!pdentry) {
pr_err("%s: failed to lookup %s dentry\n", __func__,
clk_hw_get_name(&pll->hw));
return -ENOMEM;
}
d = debugfs_create_file("boost_summary", 0444, pdentry,
pll, &boost_summary_fops);
if (!d) {
pr_err("%s: failed to create boost_summary file\n", __func__);
return -ENOMEM;
}
d = debugfs_create_file("boost_config", 0444, pdentry,
pll, &boost_config_fops);
if (!d) {
pr_err("%s: failed to create boost config file\n", __func__);
return -ENOMEM;
}
return 0;
}
static int __init boost_debug_init(void)
{
struct rockchip_clk_pll *pll;
struct dentry *rootdir;
rootdir = debugfs_lookup("clk", NULL);
if (!rootdir) {
pr_err("%s: failed to lookup clk dentry\n", __func__);
return -ENOMEM;
}
mutex_lock(&clk_boost_lock);
hlist_for_each_entry(pll, &clk_boost_list, debug_node)
boost_debug_create_one(pll, rootdir);
mutex_unlock(&clk_boost_lock);
return 0;
}
late_initcall(boost_debug_init);
#endif

View File

@@ -37,12 +37,25 @@ struct clk;
#define BOOST_SWITCH_THRESHOLD 0x0024
#define BOOST_FSM_STATUS 0x0028
#define BOOST_PLL_L_CON(x) ((x) * 0x4 + 0x2c)
#define BOOST_PLL_CON_MASK 0xffff
#define BOOST_CORE_DIV_MASK 0x1f
#define BOOST_CORE_DIV_SHIFT 0
#define BOOST_BACKUP_PLL_MASK 0x3
#define BOOST_BACKUP_PLL_SHIFT 8
#define BOOST_BACKUP_PLL_USAGE_MASK 0x1
#define BOOST_BACKUP_PLL_USAGE_SHIFT 12
#define BOOST_BACKUP_PLL_USAGE_BORROW 0
#define BOOST_BACKUP_PLL_USAGE_TARGET 1
#define BOOST_ENABLE_MASK 0x1
#define BOOST_ENABLE_SHIFT 0
#define BOOST_RECOVERY_MASK 0x1
#define BOOST_RECOVERY_SHIFT 1
#define BOOST_SW_CTRL_MASK 0x1
#define BOOST_SW_CTRL_SHIFT 2
#define BOOST_LOW_FREQ_EN_MASK 0x1
#define BOOST_LOW_FREQ_EN_SHIFT 3
#define BOOST_STATIS_ENABLE_MASK 0x1
#define BOOST_STATIS_ENABLE_SHIFT 4
#define BOOST_BUSY_STATE BIT(8)
#define PX30_PLL_CON(x) ((x) * 0x4)
@@ -318,6 +331,14 @@ struct clk *rockchip_clk_register_pll(struct rockchip_clk_provider *ctx,
struct rockchip_pll_rate_table *rate_table,
unsigned long flags, u8 clk_pll_flags);
void rockchip_boost_init(struct clk_hw *hw);
void rockchip_boost_enable_recovery_sw_low(struct clk_hw *hw);
void rockchip_boost_disable_recovery_sw(struct clk_hw *hw);
void rockchip_boost_add_core_div(struct clk_hw *hw, unsigned long prate);
struct rockchip_cpuclk_clksel {
int reg;
u32 val;
@@ -347,6 +368,7 @@ struct rockchip_cpuclk_reg_data {
u8 mux_core_main;
u8 mux_core_shift;
u32 mux_core_mask;
const char *pll_name;
};
struct clk *rockchip_clk_register_cpuclk(const char *name,