.. | .. |
---|
10 | 10 | #include <linux/export.h> |
---|
11 | 11 | #include <linux/clk-provider.h> |
---|
12 | 12 | #include <linux/delay.h> |
---|
| 13 | +#include <linux/rational.h> |
---|
13 | 14 | #include <linux/regmap.h> |
---|
14 | 15 | #include <linux/math64.h> |
---|
| 16 | +#include <linux/slab.h> |
---|
15 | 17 | |
---|
16 | 18 | #include <asm/div64.h> |
---|
17 | 19 | |
---|
.. | .. |
---|
40 | 42 | #define N_REG 0xc |
---|
41 | 43 | #define D_REG 0x10 |
---|
42 | 44 | |
---|
| 45 | +#define RCG_CFG_OFFSET(rcg) ((rcg)->cmd_rcgr + (rcg)->cfg_off + CFG_REG) |
---|
| 46 | +#define RCG_M_OFFSET(rcg) ((rcg)->cmd_rcgr + (rcg)->cfg_off + M_REG) |
---|
| 47 | +#define RCG_N_OFFSET(rcg) ((rcg)->cmd_rcgr + (rcg)->cfg_off + N_REG) |
---|
| 48 | +#define RCG_D_OFFSET(rcg) ((rcg)->cmd_rcgr + (rcg)->cfg_off + D_REG) |
---|
| 49 | + |
---|
| 50 | +/* Dynamic Frequency Scaling */ |
---|
| 51 | +#define MAX_PERF_LEVEL 8 |
---|
| 52 | +#define SE_CMD_DFSR_OFFSET 0x14 |
---|
| 53 | +#define SE_CMD_DFS_EN BIT(0) |
---|
| 54 | +#define SE_PERF_DFSR(level) (0x1c + 0x4 * (level)) |
---|
| 55 | +#define SE_PERF_M_DFSR(level) (0x5c + 0x4 * (level)) |
---|
| 56 | +#define SE_PERF_N_DFSR(level) (0x9c + 0x4 * (level)) |
---|
| 57 | + |
---|
43 | 58 | enum freq_policy { |
---|
44 | 59 | FLOOR, |
---|
45 | 60 | CEIL, |
---|
.. | .. |
---|
65 | 80 | u32 cfg; |
---|
66 | 81 | int i, ret; |
---|
67 | 82 | |
---|
68 | | - ret = regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &cfg); |
---|
| 83 | + ret = regmap_read(rcg->clkr.regmap, RCG_CFG_OFFSET(rcg), &cfg); |
---|
69 | 84 | if (ret) |
---|
70 | 85 | goto err; |
---|
71 | 86 | |
---|
.. | .. |
---|
114 | 129 | int ret; |
---|
115 | 130 | u32 cfg = rcg->parent_map[index].cfg << CFG_SRC_SEL_SHIFT; |
---|
116 | 131 | |
---|
117 | | - ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, |
---|
| 132 | + ret = regmap_update_bits(rcg->clkr.regmap, RCG_CFG_OFFSET(rcg), |
---|
118 | 133 | CFG_SRC_SEL_MASK, cfg); |
---|
119 | 134 | if (ret) |
---|
120 | 135 | return ret; |
---|
.. | .. |
---|
153 | 168 | struct clk_rcg2 *rcg = to_clk_rcg2(hw); |
---|
154 | 169 | u32 cfg, hid_div, m = 0, n = 0, mode = 0, mask; |
---|
155 | 170 | |
---|
156 | | - regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &cfg); |
---|
| 171 | + regmap_read(rcg->clkr.regmap, RCG_CFG_OFFSET(rcg), &cfg); |
---|
157 | 172 | |
---|
158 | 173 | if (rcg->mnd_width) { |
---|
159 | 174 | mask = BIT(rcg->mnd_width) - 1; |
---|
160 | | - regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + M_REG, &m); |
---|
| 175 | + regmap_read(rcg->clkr.regmap, RCG_M_OFFSET(rcg), &m); |
---|
161 | 176 | m &= mask; |
---|
162 | | - regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + N_REG, &n); |
---|
| 177 | + regmap_read(rcg->clkr.regmap, RCG_N_OFFSET(rcg), &n); |
---|
163 | 178 | n = ~n; |
---|
164 | 179 | n &= mask; |
---|
165 | 180 | n += m; |
---|
.. | .. |
---|
192 | 207 | break; |
---|
193 | 208 | default: |
---|
194 | 209 | return -EINVAL; |
---|
195 | | - }; |
---|
| 210 | + } |
---|
196 | 211 | |
---|
197 | 212 | if (!f) |
---|
198 | 213 | return -EINVAL; |
---|
.. | .. |
---|
249 | 264 | |
---|
250 | 265 | static int __clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f) |
---|
251 | 266 | { |
---|
252 | | - u32 cfg, mask; |
---|
| 267 | + u32 cfg, mask, d_val, not2d_val, n_minus_m; |
---|
253 | 268 | struct clk_hw *hw = &rcg->clkr.hw; |
---|
254 | 269 | int ret, index = qcom_find_src_index(hw, rcg->parent_map, f->src); |
---|
255 | 270 | |
---|
.. | .. |
---|
259 | 274 | if (rcg->mnd_width && f->n) { |
---|
260 | 275 | mask = BIT(rcg->mnd_width) - 1; |
---|
261 | 276 | ret = regmap_update_bits(rcg->clkr.regmap, |
---|
262 | | - rcg->cmd_rcgr + M_REG, mask, f->m); |
---|
| 277 | + RCG_M_OFFSET(rcg), mask, f->m); |
---|
263 | 278 | if (ret) |
---|
264 | 279 | return ret; |
---|
265 | 280 | |
---|
266 | 281 | ret = regmap_update_bits(rcg->clkr.regmap, |
---|
267 | | - rcg->cmd_rcgr + N_REG, mask, ~(f->n - f->m)); |
---|
| 282 | + RCG_N_OFFSET(rcg), mask, ~(f->n - f->m)); |
---|
268 | 283 | if (ret) |
---|
269 | 284 | return ret; |
---|
270 | 285 | |
---|
| 286 | + /* Calculate 2d value */ |
---|
| 287 | + d_val = f->n; |
---|
| 288 | + |
---|
| 289 | + n_minus_m = f->n - f->m; |
---|
| 290 | + n_minus_m *= 2; |
---|
| 291 | + |
---|
| 292 | + d_val = clamp_t(u32, d_val, f->m, n_minus_m); |
---|
| 293 | + not2d_val = ~d_val & mask; |
---|
| 294 | + |
---|
271 | 295 | ret = regmap_update_bits(rcg->clkr.regmap, |
---|
272 | | - rcg->cmd_rcgr + D_REG, mask, ~f->n); |
---|
| 296 | + RCG_D_OFFSET(rcg), mask, not2d_val); |
---|
273 | 297 | if (ret) |
---|
274 | 298 | return ret; |
---|
275 | 299 | } |
---|
.. | .. |
---|
280 | 304 | cfg |= rcg->parent_map[index].cfg << CFG_SRC_SEL_SHIFT; |
---|
281 | 305 | if (rcg->mnd_width && f->n && (f->m != f->n)) |
---|
282 | 306 | cfg |= CFG_MODE_DUAL_EDGE; |
---|
283 | | - |
---|
284 | | - return regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, |
---|
| 307 | + return regmap_update_bits(rcg->clkr.regmap, RCG_CFG_OFFSET(rcg), |
---|
285 | 308 | mask, cfg); |
---|
286 | 309 | } |
---|
287 | 310 | |
---|
.. | .. |
---|
311 | 334 | break; |
---|
312 | 335 | default: |
---|
313 | 336 | return -EINVAL; |
---|
314 | | - }; |
---|
| 337 | + } |
---|
315 | 338 | |
---|
316 | 339 | if (!f) |
---|
317 | 340 | return -EINVAL; |
---|
.. | .. |
---|
625 | 648 | { 2, 9 }, |
---|
626 | 649 | { 4, 9 }, |
---|
627 | 650 | { 1, 1 }, |
---|
| 651 | + { 2, 3 }, |
---|
628 | 652 | { } |
---|
629 | 653 | }; |
---|
630 | 654 | |
---|
.. | .. |
---|
934 | 958 | .set_rate_and_parent = clk_rcg2_shared_set_rate_and_parent, |
---|
935 | 959 | }; |
---|
936 | 960 | EXPORT_SYMBOL_GPL(clk_rcg2_shared_ops); |
---|
| 961 | + |
---|
| 962 | +/* Common APIs to be used for DFS based RCGR */ |
---|
| 963 | +static void clk_rcg2_dfs_populate_freq(struct clk_hw *hw, unsigned int l, |
---|
| 964 | + struct freq_tbl *f) |
---|
| 965 | +{ |
---|
| 966 | + struct clk_rcg2 *rcg = to_clk_rcg2(hw); |
---|
| 967 | + struct clk_hw *p; |
---|
| 968 | + unsigned long prate = 0; |
---|
| 969 | + u32 val, mask, cfg, mode, src; |
---|
| 970 | + int i, num_parents; |
---|
| 971 | + |
---|
| 972 | + regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + SE_PERF_DFSR(l), &cfg); |
---|
| 973 | + |
---|
| 974 | + mask = BIT(rcg->hid_width) - 1; |
---|
| 975 | + f->pre_div = 1; |
---|
| 976 | + if (cfg & mask) |
---|
| 977 | + f->pre_div = cfg & mask; |
---|
| 978 | + |
---|
| 979 | + src = cfg & CFG_SRC_SEL_MASK; |
---|
| 980 | + src >>= CFG_SRC_SEL_SHIFT; |
---|
| 981 | + |
---|
| 982 | + num_parents = clk_hw_get_num_parents(hw); |
---|
| 983 | + for (i = 0; i < num_parents; i++) { |
---|
| 984 | + if (src == rcg->parent_map[i].cfg) { |
---|
| 985 | + f->src = rcg->parent_map[i].src; |
---|
| 986 | + p = clk_hw_get_parent_by_index(&rcg->clkr.hw, i); |
---|
| 987 | + prate = clk_hw_get_rate(p); |
---|
| 988 | + } |
---|
| 989 | + } |
---|
| 990 | + |
---|
| 991 | + mode = cfg & CFG_MODE_MASK; |
---|
| 992 | + mode >>= CFG_MODE_SHIFT; |
---|
| 993 | + if (mode) { |
---|
| 994 | + mask = BIT(rcg->mnd_width) - 1; |
---|
| 995 | + regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + SE_PERF_M_DFSR(l), |
---|
| 996 | + &val); |
---|
| 997 | + val &= mask; |
---|
| 998 | + f->m = val; |
---|
| 999 | + |
---|
| 1000 | + regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + SE_PERF_N_DFSR(l), |
---|
| 1001 | + &val); |
---|
| 1002 | + val = ~val; |
---|
| 1003 | + val &= mask; |
---|
| 1004 | + val += f->m; |
---|
| 1005 | + f->n = val; |
---|
| 1006 | + } |
---|
| 1007 | + |
---|
| 1008 | + f->freq = calc_rate(prate, f->m, f->n, mode, f->pre_div); |
---|
| 1009 | +} |
---|
| 1010 | + |
---|
| 1011 | +static int clk_rcg2_dfs_populate_freq_table(struct clk_rcg2 *rcg) |
---|
| 1012 | +{ |
---|
| 1013 | + struct freq_tbl *freq_tbl; |
---|
| 1014 | + int i; |
---|
| 1015 | + |
---|
| 1016 | + /* Allocate space for 1 extra since table is NULL terminated */ |
---|
| 1017 | + freq_tbl = kcalloc(MAX_PERF_LEVEL + 1, sizeof(*freq_tbl), GFP_KERNEL); |
---|
| 1018 | + if (!freq_tbl) |
---|
| 1019 | + return -ENOMEM; |
---|
| 1020 | + rcg->freq_tbl = freq_tbl; |
---|
| 1021 | + |
---|
| 1022 | + for (i = 0; i < MAX_PERF_LEVEL; i++) |
---|
| 1023 | + clk_rcg2_dfs_populate_freq(&rcg->clkr.hw, i, freq_tbl + i); |
---|
| 1024 | + |
---|
| 1025 | + return 0; |
---|
| 1026 | +} |
---|
| 1027 | + |
---|
| 1028 | +static int clk_rcg2_dfs_determine_rate(struct clk_hw *hw, |
---|
| 1029 | + struct clk_rate_request *req) |
---|
| 1030 | +{ |
---|
| 1031 | + struct clk_rcg2 *rcg = to_clk_rcg2(hw); |
---|
| 1032 | + int ret; |
---|
| 1033 | + |
---|
| 1034 | + if (!rcg->freq_tbl) { |
---|
| 1035 | + ret = clk_rcg2_dfs_populate_freq_table(rcg); |
---|
| 1036 | + if (ret) { |
---|
| 1037 | + pr_err("Failed to update DFS tables for %s\n", |
---|
| 1038 | + clk_hw_get_name(hw)); |
---|
| 1039 | + return ret; |
---|
| 1040 | + } |
---|
| 1041 | + } |
---|
| 1042 | + |
---|
| 1043 | + return clk_rcg2_determine_rate(hw, req); |
---|
| 1044 | +} |
---|
| 1045 | + |
---|
| 1046 | +static unsigned long |
---|
| 1047 | +clk_rcg2_dfs_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) |
---|
| 1048 | +{ |
---|
| 1049 | + struct clk_rcg2 *rcg = to_clk_rcg2(hw); |
---|
| 1050 | + u32 level, mask, cfg, m = 0, n = 0, mode, pre_div; |
---|
| 1051 | + |
---|
| 1052 | + regmap_read(rcg->clkr.regmap, |
---|
| 1053 | + rcg->cmd_rcgr + SE_CMD_DFSR_OFFSET, &level); |
---|
| 1054 | + level &= GENMASK(4, 1); |
---|
| 1055 | + level >>= 1; |
---|
| 1056 | + |
---|
| 1057 | + if (rcg->freq_tbl) |
---|
| 1058 | + return rcg->freq_tbl[level].freq; |
---|
| 1059 | + |
---|
| 1060 | + /* |
---|
| 1061 | + * Assume that parent_rate is actually the parent because |
---|
| 1062 | + * we can't do any better at figuring it out when the table |
---|
| 1063 | + * hasn't been populated yet. We only populate the table |
---|
| 1064 | + * in determine_rate because we can't guarantee the parents |
---|
| 1065 | + * will be registered with the framework until then. |
---|
| 1066 | + */ |
---|
| 1067 | + regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + SE_PERF_DFSR(level), |
---|
| 1068 | + &cfg); |
---|
| 1069 | + |
---|
| 1070 | + mask = BIT(rcg->hid_width) - 1; |
---|
| 1071 | + pre_div = 1; |
---|
| 1072 | + if (cfg & mask) |
---|
| 1073 | + pre_div = cfg & mask; |
---|
| 1074 | + |
---|
| 1075 | + mode = cfg & CFG_MODE_MASK; |
---|
| 1076 | + mode >>= CFG_MODE_SHIFT; |
---|
| 1077 | + if (mode) { |
---|
| 1078 | + mask = BIT(rcg->mnd_width) - 1; |
---|
| 1079 | + regmap_read(rcg->clkr.regmap, |
---|
| 1080 | + rcg->cmd_rcgr + SE_PERF_M_DFSR(level), &m); |
---|
| 1081 | + m &= mask; |
---|
| 1082 | + |
---|
| 1083 | + regmap_read(rcg->clkr.regmap, |
---|
| 1084 | + rcg->cmd_rcgr + SE_PERF_N_DFSR(level), &n); |
---|
| 1085 | + n = ~n; |
---|
| 1086 | + n &= mask; |
---|
| 1087 | + n += m; |
---|
| 1088 | + } |
---|
| 1089 | + |
---|
| 1090 | + return calc_rate(parent_rate, m, n, mode, pre_div); |
---|
| 1091 | +} |
---|
| 1092 | + |
---|
| 1093 | +static const struct clk_ops clk_rcg2_dfs_ops = { |
---|
| 1094 | + .is_enabled = clk_rcg2_is_enabled, |
---|
| 1095 | + .get_parent = clk_rcg2_get_parent, |
---|
| 1096 | + .determine_rate = clk_rcg2_dfs_determine_rate, |
---|
| 1097 | + .recalc_rate = clk_rcg2_dfs_recalc_rate, |
---|
| 1098 | +}; |
---|
| 1099 | + |
---|
| 1100 | +static int clk_rcg2_enable_dfs(const struct clk_rcg_dfs_data *data, |
---|
| 1101 | + struct regmap *regmap) |
---|
| 1102 | +{ |
---|
| 1103 | + struct clk_rcg2 *rcg = data->rcg; |
---|
| 1104 | + struct clk_init_data *init = data->init; |
---|
| 1105 | + u32 val; |
---|
| 1106 | + int ret; |
---|
| 1107 | + |
---|
| 1108 | + ret = regmap_read(regmap, rcg->cmd_rcgr + SE_CMD_DFSR_OFFSET, &val); |
---|
| 1109 | + if (ret) |
---|
| 1110 | + return -EINVAL; |
---|
| 1111 | + |
---|
| 1112 | + if (!(val & SE_CMD_DFS_EN)) |
---|
| 1113 | + return 0; |
---|
| 1114 | + |
---|
| 1115 | + /* |
---|
| 1116 | + * Rate changes with consumer writing a register in |
---|
| 1117 | + * their own I/O region |
---|
| 1118 | + */ |
---|
| 1119 | + init->flags |= CLK_GET_RATE_NOCACHE; |
---|
| 1120 | + init->ops = &clk_rcg2_dfs_ops; |
---|
| 1121 | + |
---|
| 1122 | + rcg->freq_tbl = NULL; |
---|
| 1123 | + |
---|
| 1124 | + return 0; |
---|
| 1125 | +} |
---|
| 1126 | + |
---|
| 1127 | +int qcom_cc_register_rcg_dfs(struct regmap *regmap, |
---|
| 1128 | + const struct clk_rcg_dfs_data *rcgs, size_t len) |
---|
| 1129 | +{ |
---|
| 1130 | + int i, ret; |
---|
| 1131 | + |
---|
| 1132 | + for (i = 0; i < len; i++) { |
---|
| 1133 | + ret = clk_rcg2_enable_dfs(&rcgs[i], regmap); |
---|
| 1134 | + if (ret) |
---|
| 1135 | + return ret; |
---|
| 1136 | + } |
---|
| 1137 | + |
---|
| 1138 | + return 0; |
---|
| 1139 | +} |
---|
| 1140 | +EXPORT_SYMBOL_GPL(qcom_cc_register_rcg_dfs); |
---|
| 1141 | + |
---|
| 1142 | +static int clk_rcg2_dp_set_rate(struct clk_hw *hw, unsigned long rate, |
---|
| 1143 | + unsigned long parent_rate) |
---|
| 1144 | +{ |
---|
| 1145 | + struct clk_rcg2 *rcg = to_clk_rcg2(hw); |
---|
| 1146 | + struct freq_tbl f = { 0 }; |
---|
| 1147 | + u32 mask = BIT(rcg->hid_width) - 1; |
---|
| 1148 | + u32 hid_div, cfg; |
---|
| 1149 | + int i, num_parents = clk_hw_get_num_parents(hw); |
---|
| 1150 | + unsigned long num, den; |
---|
| 1151 | + |
---|
| 1152 | + rational_best_approximation(parent_rate, rate, |
---|
| 1153 | + GENMASK(rcg->mnd_width - 1, 0), |
---|
| 1154 | + GENMASK(rcg->mnd_width - 1, 0), &den, &num); |
---|
| 1155 | + |
---|
| 1156 | + if (!num || !den) |
---|
| 1157 | + return -EINVAL; |
---|
| 1158 | + |
---|
| 1159 | + regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &cfg); |
---|
| 1160 | + hid_div = cfg; |
---|
| 1161 | + cfg &= CFG_SRC_SEL_MASK; |
---|
| 1162 | + cfg >>= CFG_SRC_SEL_SHIFT; |
---|
| 1163 | + |
---|
| 1164 | + for (i = 0; i < num_parents; i++) { |
---|
| 1165 | + if (cfg == rcg->parent_map[i].cfg) { |
---|
| 1166 | + f.src = rcg->parent_map[i].src; |
---|
| 1167 | + break; |
---|
| 1168 | + } |
---|
| 1169 | + } |
---|
| 1170 | + |
---|
| 1171 | + f.pre_div = hid_div; |
---|
| 1172 | + f.pre_div >>= CFG_SRC_DIV_SHIFT; |
---|
| 1173 | + f.pre_div &= mask; |
---|
| 1174 | + |
---|
| 1175 | + if (num != den) { |
---|
| 1176 | + f.m = num; |
---|
| 1177 | + f.n = den; |
---|
| 1178 | + } else { |
---|
| 1179 | + f.m = 0; |
---|
| 1180 | + f.n = 0; |
---|
| 1181 | + } |
---|
| 1182 | + |
---|
| 1183 | + return clk_rcg2_configure(rcg, &f); |
---|
| 1184 | +} |
---|
| 1185 | + |
---|
| 1186 | +static int clk_rcg2_dp_set_rate_and_parent(struct clk_hw *hw, |
---|
| 1187 | + unsigned long rate, unsigned long parent_rate, u8 index) |
---|
| 1188 | +{ |
---|
| 1189 | + return clk_rcg2_dp_set_rate(hw, rate, parent_rate); |
---|
| 1190 | +} |
---|
| 1191 | + |
---|
| 1192 | +static int clk_rcg2_dp_determine_rate(struct clk_hw *hw, |
---|
| 1193 | + struct clk_rate_request *req) |
---|
| 1194 | +{ |
---|
| 1195 | + struct clk_rcg2 *rcg = to_clk_rcg2(hw); |
---|
| 1196 | + unsigned long num, den; |
---|
| 1197 | + u64 tmp; |
---|
| 1198 | + |
---|
| 1199 | + /* Parent rate is a fixed phy link rate */ |
---|
| 1200 | + rational_best_approximation(req->best_parent_rate, req->rate, |
---|
| 1201 | + GENMASK(rcg->mnd_width - 1, 0), |
---|
| 1202 | + GENMASK(rcg->mnd_width - 1, 0), &den, &num); |
---|
| 1203 | + |
---|
| 1204 | + if (!num || !den) |
---|
| 1205 | + return -EINVAL; |
---|
| 1206 | + |
---|
| 1207 | + tmp = req->best_parent_rate * num; |
---|
| 1208 | + do_div(tmp, den); |
---|
| 1209 | + req->rate = tmp; |
---|
| 1210 | + |
---|
| 1211 | + return 0; |
---|
| 1212 | +} |
---|
| 1213 | + |
---|
| 1214 | +const struct clk_ops clk_dp_ops = { |
---|
| 1215 | + .is_enabled = clk_rcg2_is_enabled, |
---|
| 1216 | + .get_parent = clk_rcg2_get_parent, |
---|
| 1217 | + .set_parent = clk_rcg2_set_parent, |
---|
| 1218 | + .recalc_rate = clk_rcg2_recalc_rate, |
---|
| 1219 | + .set_rate = clk_rcg2_dp_set_rate, |
---|
| 1220 | + .set_rate_and_parent = clk_rcg2_dp_set_rate_and_parent, |
---|
| 1221 | + .determine_rate = clk_rcg2_dp_determine_rate, |
---|
| 1222 | +}; |
---|
| 1223 | +EXPORT_SYMBOL_GPL(clk_dp_ops); |
---|