diff --git a/drivers/clk/rockchip/clk-cpu.c b/drivers/clk/rockchip/clk-cpu.c index 0dc478a19451..73f4905631e0 100644 --- a/drivers/clk/rockchip/clk-cpu.c +++ b/drivers/clk/rockchip/clk-cpu.c @@ -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) { diff --git a/drivers/clk/rockchip/clk-pll.c b/drivers/clk/rockchip/clk-pll.c index 39b0402aec57..d653403311a4 100644 --- a/drivers/clk/rockchip/clk-pll.c +++ b/drivers/clk/rockchip/clk-pll.c @@ -16,6 +16,7 @@ #include #include #include +#include #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 + +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 diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h index c541bc1ffacb..f4ced806b2e7 100644 --- a/drivers/clk/rockchip/clk.h +++ b/drivers/clk/rockchip/clk.h @@ -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,