diff options
author | Stephen Boyd <sboyd@codeaurora.org> | 2014-04-28 18:59:16 -0400 |
---|---|---|
committer | Stephen Boyd <sboyd@codeaurora.org> | 2014-09-22 18:16:54 -0400 |
commit | 229fd4a505553c3a475b90e9aa8e452f5d78eb3b (patch) | |
tree | 5f4b5b22574558f9d41e93f3d9f01f7290695cb2 /drivers/clk/qcom/clk-rcg.c | |
parent | ae3669ac5c09fa8dfc8d8a294ccb5f265b8929be (diff) |
clk: qcom: Add support for banked MD RCGs
The banked MD RCGs in global clock control have a different
register layout than the ones implemented in multimedia clock
control. Add support for these types of clocks so we can change
the rates of the UBI32 clocks.
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
Diffstat (limited to 'drivers/clk/qcom/clk-rcg.c')
-rw-r--r-- | drivers/clk/qcom/clk-rcg.c | 99 |
1 files changed, 52 insertions, 47 deletions
diff --git a/drivers/clk/qcom/clk-rcg.c b/drivers/clk/qcom/clk-rcg.c index 3db106b74934..b6e6959e89aa 100644 --- a/drivers/clk/qcom/clk-rcg.c +++ b/drivers/clk/qcom/clk-rcg.c | |||
@@ -68,16 +68,16 @@ static u8 clk_dyn_rcg_get_parent(struct clk_hw *hw) | |||
68 | { | 68 | { |
69 | struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw); | 69 | struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw); |
70 | int num_parents = __clk_get_num_parents(hw->clk); | 70 | int num_parents = __clk_get_num_parents(hw->clk); |
71 | u32 ns, ctl; | 71 | u32 ns, reg; |
72 | int bank; | 72 | int bank; |
73 | int i; | 73 | int i; |
74 | struct src_sel *s; | 74 | struct src_sel *s; |
75 | 75 | ||
76 | regmap_read(rcg->clkr.regmap, rcg->clkr.enable_reg, &ctl); | 76 | regmap_read(rcg->clkr.regmap, rcg->bank_reg, ®); |
77 | bank = reg_to_bank(rcg, ctl); | 77 | bank = reg_to_bank(rcg, reg); |
78 | s = &rcg->s[bank]; | 78 | s = &rcg->s[bank]; |
79 | 79 | ||
80 | regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns); | 80 | regmap_read(rcg->clkr.regmap, rcg->ns_reg[bank], &ns); |
81 | ns = ns_to_src(s, ns); | 81 | ns = ns_to_src(s, ns); |
82 | 82 | ||
83 | for (i = 0; i < num_parents; i++) | 83 | for (i = 0; i < num_parents; i++) |
@@ -193,90 +193,93 @@ static u32 mn_to_reg(struct mn *mn, u32 m, u32 n, u32 val) | |||
193 | 193 | ||
194 | static void configure_bank(struct clk_dyn_rcg *rcg, const struct freq_tbl *f) | 194 | static void configure_bank(struct clk_dyn_rcg *rcg, const struct freq_tbl *f) |
195 | { | 195 | { |
196 | u32 ns, md, ctl, *regp; | 196 | u32 ns, md, reg; |
197 | int bank, new_bank; | 197 | int bank, new_bank; |
198 | struct mn *mn; | 198 | struct mn *mn; |
199 | struct pre_div *p; | 199 | struct pre_div *p; |
200 | struct src_sel *s; | 200 | struct src_sel *s; |
201 | bool enabled; | 201 | bool enabled; |
202 | u32 md_reg; | 202 | u32 md_reg, ns_reg; |
203 | u32 bank_reg; | ||
204 | bool banked_mn = !!rcg->mn[1].width; | 203 | bool banked_mn = !!rcg->mn[1].width; |
204 | bool banked_p = !!rcg->p[1].pre_div_width; | ||
205 | struct clk_hw *hw = &rcg->clkr.hw; | 205 | struct clk_hw *hw = &rcg->clkr.hw; |
206 | 206 | ||
207 | enabled = __clk_is_enabled(hw->clk); | 207 | enabled = __clk_is_enabled(hw->clk); |
208 | 208 | ||
209 | regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns); | 209 | regmap_read(rcg->clkr.regmap, rcg->bank_reg, ®); |
210 | regmap_read(rcg->clkr.regmap, rcg->clkr.enable_reg, &ctl); | 210 | bank = reg_to_bank(rcg, reg); |
211 | |||
212 | if (banked_mn) { | ||
213 | regp = &ctl; | ||
214 | bank_reg = rcg->clkr.enable_reg; | ||
215 | } else { | ||
216 | regp = &ns; | ||
217 | bank_reg = rcg->ns_reg; | ||
218 | } | ||
219 | |||
220 | bank = reg_to_bank(rcg, *regp); | ||
221 | new_bank = enabled ? !bank : bank; | 211 | new_bank = enabled ? !bank : bank; |
222 | 212 | ||
213 | ns_reg = rcg->ns_reg[new_bank]; | ||
214 | regmap_read(rcg->clkr.regmap, ns_reg, &ns); | ||
215 | |||
223 | if (banked_mn) { | 216 | if (banked_mn) { |
224 | mn = &rcg->mn[new_bank]; | 217 | mn = &rcg->mn[new_bank]; |
225 | md_reg = rcg->md_reg[new_bank]; | 218 | md_reg = rcg->md_reg[new_bank]; |
226 | 219 | ||
227 | ns |= BIT(mn->mnctr_reset_bit); | 220 | ns |= BIT(mn->mnctr_reset_bit); |
228 | regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns); | 221 | regmap_write(rcg->clkr.regmap, ns_reg, ns); |
229 | 222 | ||
230 | regmap_read(rcg->clkr.regmap, md_reg, &md); | 223 | regmap_read(rcg->clkr.regmap, md_reg, &md); |
231 | md = mn_to_md(mn, f->m, f->n, md); | 224 | md = mn_to_md(mn, f->m, f->n, md); |
232 | regmap_write(rcg->clkr.regmap, md_reg, md); | 225 | regmap_write(rcg->clkr.regmap, md_reg, md); |
233 | 226 | ||
234 | ns = mn_to_ns(mn, f->m, f->n, ns); | 227 | ns = mn_to_ns(mn, f->m, f->n, ns); |
235 | regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns); | 228 | regmap_write(rcg->clkr.regmap, ns_reg, ns); |
236 | 229 | ||
237 | ctl = mn_to_reg(mn, f->m, f->n, ctl); | 230 | /* Two NS registers means mode control is in NS register */ |
238 | regmap_write(rcg->clkr.regmap, rcg->clkr.enable_reg, ctl); | 231 | if (rcg->ns_reg[0] != rcg->ns_reg[1]) { |
232 | ns = mn_to_reg(mn, f->m, f->n, ns); | ||
233 | regmap_write(rcg->clkr.regmap, ns_reg, ns); | ||
234 | } else { | ||
235 | reg = mn_to_reg(mn, f->m, f->n, reg); | ||
236 | regmap_write(rcg->clkr.regmap, rcg->bank_reg, reg); | ||
237 | } | ||
239 | 238 | ||
240 | ns &= ~BIT(mn->mnctr_reset_bit); | 239 | ns &= ~BIT(mn->mnctr_reset_bit); |
241 | regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns); | 240 | regmap_write(rcg->clkr.regmap, ns_reg, ns); |
242 | } else { | 241 | } |
242 | |||
243 | if (banked_p) { | ||
243 | p = &rcg->p[new_bank]; | 244 | p = &rcg->p[new_bank]; |
244 | ns = pre_div_to_ns(p, f->pre_div - 1, ns); | 245 | ns = pre_div_to_ns(p, f->pre_div - 1, ns); |
245 | } | 246 | } |
246 | 247 | ||
247 | s = &rcg->s[new_bank]; | 248 | s = &rcg->s[new_bank]; |
248 | ns = src_to_ns(s, s->parent_map[f->src], ns); | 249 | ns = src_to_ns(s, s->parent_map[f->src], ns); |
249 | regmap_write(rcg->clkr.regmap, rcg->ns_reg, ns); | 250 | regmap_write(rcg->clkr.regmap, ns_reg, ns); |
250 | 251 | ||
251 | if (enabled) { | 252 | if (enabled) { |
252 | *regp ^= BIT(rcg->mux_sel_bit); | 253 | regmap_read(rcg->clkr.regmap, rcg->bank_reg, ®); |
253 | regmap_write(rcg->clkr.regmap, bank_reg, *regp); | 254 | reg ^= BIT(rcg->mux_sel_bit); |
255 | regmap_write(rcg->clkr.regmap, rcg->bank_reg, reg); | ||
254 | } | 256 | } |
255 | } | 257 | } |
256 | 258 | ||
257 | static int clk_dyn_rcg_set_parent(struct clk_hw *hw, u8 index) | 259 | static int clk_dyn_rcg_set_parent(struct clk_hw *hw, u8 index) |
258 | { | 260 | { |
259 | struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw); | 261 | struct clk_dyn_rcg *rcg = to_clk_dyn_rcg(hw); |
260 | u32 ns, ctl, md, reg; | 262 | u32 ns, md, reg; |
261 | int bank; | 263 | int bank; |
262 | struct freq_tbl f = { 0 }; | 264 | struct freq_tbl f = { 0 }; |
263 | bool banked_mn = !!rcg->mn[1].width; | 265 | bool banked_mn = !!rcg->mn[1].width; |
266 | bool banked_p = !!rcg->p[1].pre_div_width; | ||
264 | 267 | ||
265 | regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns); | 268 | regmap_read(rcg->clkr.regmap, rcg->bank_reg, ®); |
266 | regmap_read(rcg->clkr.regmap, rcg->clkr.enable_reg, &ctl); | ||
267 | reg = banked_mn ? ctl : ns; | ||
268 | |||
269 | bank = reg_to_bank(rcg, reg); | 269 | bank = reg_to_bank(rcg, reg); |
270 | 270 | ||
271 | regmap_read(rcg->clkr.regmap, rcg->ns_reg[bank], &ns); | ||
272 | |||
271 | if (banked_mn) { | 273 | if (banked_mn) { |
272 | regmap_read(rcg->clkr.regmap, rcg->md_reg[bank], &md); | 274 | regmap_read(rcg->clkr.regmap, rcg->md_reg[bank], &md); |
273 | f.m = md_to_m(&rcg->mn[bank], md); | 275 | f.m = md_to_m(&rcg->mn[bank], md); |
274 | f.n = ns_m_to_n(&rcg->mn[bank], ns, f.m); | 276 | f.n = ns_m_to_n(&rcg->mn[bank], ns, f.m); |
275 | } else { | ||
276 | f.pre_div = ns_to_pre_div(&rcg->p[bank], ns) + 1; | ||
277 | } | 277 | } |
278 | f.src = index; | ||
279 | 278 | ||
279 | if (banked_p) | ||
280 | f.pre_div = ns_to_pre_div(&rcg->p[bank], ns) + 1; | ||
281 | |||
282 | f.src = index; | ||
280 | configure_bank(rcg, &f); | 283 | configure_bank(rcg, &f); |
281 | 284 | ||
282 | return 0; | 285 | return 0; |
@@ -337,28 +340,30 @@ clk_dyn_rcg_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) | |||
337 | u32 m, n, pre_div, ns, md, mode, reg; | 340 | u32 m, n, pre_div, ns, md, mode, reg; |
338 | int bank; | 341 | int bank; |
339 | struct mn *mn; | 342 | struct mn *mn; |
343 | bool banked_p = !!rcg->p[1].pre_div_width; | ||
340 | bool banked_mn = !!rcg->mn[1].width; | 344 | bool banked_mn = !!rcg->mn[1].width; |
341 | 345 | ||
342 | regmap_read(rcg->clkr.regmap, rcg->ns_reg, &ns); | 346 | regmap_read(rcg->clkr.regmap, rcg->bank_reg, ®); |
343 | |||
344 | if (banked_mn) | ||
345 | regmap_read(rcg->clkr.regmap, rcg->clkr.enable_reg, ®); | ||
346 | else | ||
347 | reg = ns; | ||
348 | |||
349 | bank = reg_to_bank(rcg, reg); | 347 | bank = reg_to_bank(rcg, reg); |
350 | 348 | ||
349 | regmap_read(rcg->clkr.regmap, rcg->ns_reg[bank], &ns); | ||
350 | m = n = pre_div = mode = 0; | ||
351 | |||
351 | if (banked_mn) { | 352 | if (banked_mn) { |
352 | mn = &rcg->mn[bank]; | 353 | mn = &rcg->mn[bank]; |
353 | regmap_read(rcg->clkr.regmap, rcg->md_reg[bank], &md); | 354 | regmap_read(rcg->clkr.regmap, rcg->md_reg[bank], &md); |
354 | m = md_to_m(mn, md); | 355 | m = md_to_m(mn, md); |
355 | n = ns_m_to_n(mn, ns, m); | 356 | n = ns_m_to_n(mn, ns, m); |
357 | /* Two NS registers means mode control is in NS register */ | ||
358 | if (rcg->ns_reg[0] != rcg->ns_reg[1]) | ||
359 | reg = ns; | ||
356 | mode = reg_to_mnctr_mode(mn, reg); | 360 | mode = reg_to_mnctr_mode(mn, reg); |
357 | return calc_rate(parent_rate, m, n, mode, 0); | ||
358 | } else { | ||
359 | pre_div = ns_to_pre_div(&rcg->p[bank], ns); | ||
360 | return calc_rate(parent_rate, 0, 0, 0, pre_div); | ||
361 | } | 361 | } |
362 | |||
363 | if (banked_p) | ||
364 | pre_div = ns_to_pre_div(&rcg->p[bank], ns); | ||
365 | |||
366 | return calc_rate(parent_rate, m, n, mode, pre_div); | ||
362 | } | 367 | } |
363 | 368 | ||
364 | static long _freq_tbl_determine_rate(struct clk_hw *hw, | 369 | static long _freq_tbl_determine_rate(struct clk_hw *hw, |