aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTaniya Das <tdas@codeaurora.org>2018-08-10 21:53:55 -0400
committerStephen Boyd <sboyd@kernel.org>2018-08-27 16:36:25 -0400
commitcc4f6944d0e333ed57a2f300afd7c8cb6df228d5 (patch)
treee1adfa849de56109b15d1bb47e9220706492da18
parent5b394b2ddf0347bef56e50c69a58773c94343ff3 (diff)
clk: qcom: Add support for RCG to register for DFS
Dynamic Frequency switch is a feature of clock controller by which request from peripherals allows automatic switching frequency of input clock without SW intervention. There are various performance levels associated with a root clock. When the input performance state changes, the source clocks and division ratios of the new performance state are loaded on to RCG via HW and the RCG switches to new clock frequency when the RCG is in DFS HW enabled mode. Register the root clock generators(RCG) to switch to use the dfs clock ops in the cases where DFS is enabled. The clk_round_rate() called by the clock consumer would invoke the dfs determine clock ops and would read the DFS performance level registers to identify all the frequencies supported and update the frequency table. The DFS clock consumers would maintain these frequency mapping and request the desired performance levels. Signed-off-by: Taniya Das <tdas@codeaurora.org> [sboyd@kernel.org: Rework registration logic to stop copying, change recalc_rate() to index directly into the table if possible and fallback to calculating on the fly with an assumed correct parent] Signed-off-by: Stephen Boyd <sboyd@kernel.org>
-rw-r--r--drivers/clk/qcom/clk-rcg.h11
-rw-r--r--drivers/clk/qcom/clk-rcg2.c194
2 files changed, 205 insertions, 0 deletions
diff --git a/drivers/clk/qcom/clk-rcg.h b/drivers/clk/qcom/clk-rcg.h
index dbd5a9e83554..e5eca8a1abe4 100644
--- a/drivers/clk/qcom/clk-rcg.h
+++ b/drivers/clk/qcom/clk-rcg.h
@@ -163,4 +163,15 @@ extern const struct clk_ops clk_pixel_ops;
163extern const struct clk_ops clk_gfx3d_ops; 163extern const struct clk_ops clk_gfx3d_ops;
164extern const struct clk_ops clk_rcg2_shared_ops; 164extern const struct clk_ops clk_rcg2_shared_ops;
165 165
166struct clk_rcg_dfs_data {
167 struct clk_rcg2 *rcg;
168 struct clk_init_data *init;
169};
170
171#define DEFINE_RCG_DFS(r) \
172 { .rcg = &r##_src, .init = &r##_init }
173
174extern int qcom_cc_register_rcg_dfs(struct regmap *regmap,
175 const struct clk_rcg_dfs_data *rcgs,
176 size_t len);
166#endif 177#endif
diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c
index 52208d4165f4..d5d77f9ad170 100644
--- a/drivers/clk/qcom/clk-rcg2.c
+++ b/drivers/clk/qcom/clk-rcg2.c
@@ -12,6 +12,7 @@
12#include <linux/delay.h> 12#include <linux/delay.h>
13#include <linux/regmap.h> 13#include <linux/regmap.h>
14#include <linux/math64.h> 14#include <linux/math64.h>
15#include <linux/slab.h>
15 16
16#include <asm/div64.h> 17#include <asm/div64.h>
17 18
@@ -40,6 +41,14 @@
40#define N_REG 0xc 41#define N_REG 0xc
41#define D_REG 0x10 42#define D_REG 0x10
42 43
44/* Dynamic Frequency Scaling */
45#define MAX_PERF_LEVEL 8
46#define SE_CMD_DFSR_OFFSET 0x14
47#define SE_CMD_DFS_EN BIT(0)
48#define SE_PERF_DFSR(level) (0x1c + 0x4 * (level))
49#define SE_PERF_M_DFSR(level) (0x5c + 0x4 * (level))
50#define SE_PERF_N_DFSR(level) (0x9c + 0x4 * (level))
51
43enum freq_policy { 52enum freq_policy {
44 FLOOR, 53 FLOOR,
45 CEIL, 54 CEIL,
@@ -929,3 +938,188 @@ const struct clk_ops clk_rcg2_shared_ops = {
929 .set_rate_and_parent = clk_rcg2_shared_set_rate_and_parent, 938 .set_rate_and_parent = clk_rcg2_shared_set_rate_and_parent,
930}; 939};
931EXPORT_SYMBOL_GPL(clk_rcg2_shared_ops); 940EXPORT_SYMBOL_GPL(clk_rcg2_shared_ops);
941
942/* Common APIs to be used for DFS based RCGR */
943static void clk_rcg2_dfs_populate_freq(struct clk_hw *hw, unsigned int l,
944 struct freq_tbl *f)
945{
946 struct clk_rcg2 *rcg = to_clk_rcg2(hw);
947 struct clk_hw *p;
948 unsigned long prate = 0;
949 u32 val, mask, cfg, mode;
950 int i, num_parents;
951
952 regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + SE_PERF_DFSR(l), &cfg);
953
954 mask = BIT(rcg->hid_width) - 1;
955 f->pre_div = 1;
956 if (cfg & mask)
957 f->pre_div = cfg & mask;
958
959 cfg &= CFG_SRC_SEL_MASK;
960 cfg >>= CFG_SRC_SEL_SHIFT;
961
962 num_parents = clk_hw_get_num_parents(hw);
963 for (i = 0; i < num_parents; i++) {
964 if (cfg == rcg->parent_map[i].cfg) {
965 f->src = rcg->parent_map[i].src;
966 p = clk_hw_get_parent_by_index(&rcg->clkr.hw, i);
967 prate = clk_hw_get_rate(p);
968 }
969 }
970
971 mode = cfg & CFG_MODE_MASK;
972 mode >>= CFG_MODE_SHIFT;
973 if (mode) {
974 mask = BIT(rcg->mnd_width) - 1;
975 regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + SE_PERF_M_DFSR(l),
976 &val);
977 val &= mask;
978 f->m = val;
979
980 regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + SE_PERF_N_DFSR(l),
981 &val);
982 val = ~val;
983 val &= mask;
984 val += f->m;
985 f->n = val;
986 }
987
988 f->freq = calc_rate(prate, f->m, f->n, mode, f->pre_div);
989}
990
991static int clk_rcg2_dfs_populate_freq_table(struct clk_rcg2 *rcg)
992{
993 struct freq_tbl *freq_tbl;
994 int i;
995
996 freq_tbl = kcalloc(MAX_PERF_LEVEL, sizeof(*freq_tbl), GFP_KERNEL);
997 if (!freq_tbl)
998 return -ENOMEM;
999 rcg->freq_tbl = freq_tbl;
1000
1001 for (i = 0; i < MAX_PERF_LEVEL; i++)
1002 clk_rcg2_dfs_populate_freq(&rcg->clkr.hw, i, freq_tbl + i);
1003
1004 return 0;
1005}
1006
1007static int clk_rcg2_dfs_determine_rate(struct clk_hw *hw,
1008 struct clk_rate_request *req)
1009{
1010 struct clk_rcg2 *rcg = to_clk_rcg2(hw);
1011 int ret;
1012
1013 if (!rcg->freq_tbl) {
1014 ret = clk_rcg2_dfs_populate_freq_table(rcg);
1015 if (ret) {
1016 pr_err("Failed to update DFS tables for %s\n",
1017 clk_hw_get_name(hw));
1018 return ret;
1019 }
1020 }
1021
1022 return clk_rcg2_determine_rate(hw, req);
1023}
1024
1025static unsigned long
1026clk_rcg2_dfs_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
1027{
1028 struct clk_rcg2 *rcg = to_clk_rcg2(hw);
1029 u32 level, mask, cfg, m = 0, n = 0, mode, pre_div;
1030
1031 regmap_read(rcg->clkr.regmap,
1032 rcg->cmd_rcgr + SE_CMD_DFSR_OFFSET, &level);
1033 level &= GENMASK(4, 1);
1034 level >>= 1;
1035
1036 if (rcg->freq_tbl)
1037 return rcg->freq_tbl[level].freq;
1038
1039 /*
1040 * Assume that parent_rate is actually the parent because
1041 * we can't do any better at figuring it out when the table
1042 * hasn't been populated yet. We only populate the table
1043 * in determine_rate because we can't guarantee the parents
1044 * will be registered with the framework until then.
1045 */
1046 regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + SE_PERF_DFSR(level),
1047 &cfg);
1048
1049 mask = BIT(rcg->hid_width) - 1;
1050 pre_div = 1;
1051 if (cfg & mask)
1052 pre_div = cfg & mask;
1053
1054 mode = cfg & CFG_MODE_MASK;
1055 mode >>= CFG_MODE_SHIFT;
1056 if (mode) {
1057 mask = BIT(rcg->mnd_width) - 1;
1058 regmap_read(rcg->clkr.regmap,
1059 rcg->cmd_rcgr + SE_PERF_M_DFSR(level), &m);
1060 m &= mask;
1061
1062 regmap_read(rcg->clkr.regmap,
1063 rcg->cmd_rcgr + SE_PERF_N_DFSR(level), &n);
1064 n = ~n;
1065 n &= mask;
1066 n += m;
1067 }
1068
1069 return calc_rate(parent_rate, m, n, mode, pre_div);
1070}
1071
1072static const struct clk_ops clk_rcg2_dfs_ops = {
1073 .is_enabled = clk_rcg2_is_enabled,
1074 .get_parent = clk_rcg2_get_parent,
1075 .determine_rate = clk_rcg2_dfs_determine_rate,
1076 .recalc_rate = clk_rcg2_dfs_recalc_rate,
1077};
1078
1079static int clk_rcg2_enable_dfs(const struct clk_rcg_dfs_data *data,
1080 struct regmap *regmap)
1081{
1082 struct clk_rcg2 *rcg = data->rcg;
1083 struct clk_init_data *init = data->init;
1084 u32 val;
1085 int ret;
1086
1087 ret = regmap_read(regmap, rcg->cmd_rcgr + SE_CMD_DFSR_OFFSET, &val);
1088 if (ret)
1089 return -EINVAL;
1090
1091 if (!(val & SE_CMD_DFS_EN))
1092 return 0;
1093