clk-rk-cpu.c 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. /*
  2. * Copyright (c) 2006-2022, RT-Thread Development Team
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. *
  6. * Change Logs:
  7. * Date Author Notes
  8. * 2022-3-08 GuEe-GUI the first version
  9. */
  10. #include "clk-rk-cpu.h"
  11. static rt_ubase_t rockchip_cpu_clk_recalc_rate(struct rt_clk_cell *cell, rt_ubase_t parent_rate)
  12. {
  13. void *base;
  14. rt_uint32_t clksel0;
  15. struct rockchip_cpu_clk_cell *cpu_clk_cell = cell_to_rockchip_cpu_cell(cell);
  16. const struct rockchip_cpu_clk_reg_data *reg_data = cpu_clk_cell->reg_data;
  17. base = cpu_clk_cell->rk_cell.provider->reg_base;
  18. clksel0 = HWREG32(base + reg_data->core_reg[0]);
  19. clksel0 >>= reg_data->div_core_shift[0];
  20. clksel0 &= reg_data->div_core_mask[0];
  21. return parent_rate / (clksel0 + 1);
  22. }
  23. const struct rt_clk_ops rockchip_cpu_clk_ops =
  24. {
  25. .recalc_rate = rockchip_cpu_clk_recalc_rate,
  26. };
  27. static const struct rockchip_cpu_clk_rate_table *rockchip_get_cpu_clk_settings(
  28. struct rockchip_cpu_clk_cell *cpu_clk_cell, rt_ubase_t rate)
  29. {
  30. const struct rockchip_cpu_clk_rate_table *rate_table = cpu_clk_cell->rate_table;
  31. for (int i = 0; i < cpu_clk_cell->rate_count; ++i)
  32. {
  33. if (rate == rate_table[i].prate)
  34. {
  35. return &rate_table[i];
  36. }
  37. }
  38. return RT_NULL;
  39. }
  40. static void rockchip_cpu_clk_set_dividers(struct rockchip_cpu_clk_cell *cpu_clk_cell,
  41. const struct rockchip_cpu_clk_rate_table *rate)
  42. {
  43. void *base = cpu_clk_cell->rk_cell.provider->reg_base;
  44. /* Alternate parent is active now. set the dividers */
  45. for (int i = 0; i < RT_ARRAY_SIZE(rate->divs); ++i)
  46. {
  47. const struct rockchip_cpu_clk_clksel *clksel = &rate->divs[i];
  48. if (!clksel->reg)
  49. {
  50. continue;
  51. }
  52. HWREG32(base + clksel->reg) = clksel->val;
  53. }
  54. }
  55. static void rockchip_cpu_clk_set_pre_muxs(struct rockchip_cpu_clk_cell *cpu_clk_cell,
  56. const struct rockchip_cpu_clk_rate_table *rate)
  57. {
  58. void *base = cpu_clk_cell->rk_cell.provider->reg_base;
  59. /* Alternate parent is active now. set the pre_muxs */
  60. for (int i = 0; i < RT_ARRAY_SIZE(rate->pre_muxs); ++i)
  61. {
  62. const struct rockchip_cpu_clk_clksel *clksel = &rate->pre_muxs[i];
  63. if (!clksel->reg)
  64. {
  65. break;
  66. }
  67. HWREG32(base + clksel->reg) = clksel->val;
  68. }
  69. }
  70. static void rockchip_cpu_clk_set_post_muxs(struct rockchip_cpu_clk_cell *cpu_clk_cell,
  71. const struct rockchip_cpu_clk_rate_table *rate)
  72. {
  73. void *base = cpu_clk_cell->rk_cell.provider->reg_base;
  74. /* Alternate parent is active now. set the muxs */
  75. for (int i = 0; i < RT_ARRAY_SIZE(rate->post_muxs); ++i)
  76. {
  77. const struct rockchip_cpu_clk_clksel *clksel = &rate->post_muxs[i];
  78. if (!clksel->reg)
  79. {
  80. break;
  81. }
  82. HWREG32(base + clksel->reg) = clksel->val;
  83. }
  84. }
  85. static int rockchip_cpu_clk_pre_rate_change(struct rockchip_cpu_clk_cell *cpu_clk_cell,
  86. rt_ubase_t old_rate, rt_ubase_t new_rate)
  87. {
  88. rt_ubase_t alt_prate, alt_div;
  89. void *base = cpu_clk_cell->rk_cell.provider->reg_base;
  90. const struct rockchip_cpu_clk_reg_data *reg_data = cpu_clk_cell->reg_data;
  91. const struct rockchip_cpu_clk_rate_table *rate;
  92. /* Check validity of the new rate */
  93. if (!(rate = rockchip_get_cpu_clk_settings(cpu_clk_cell, new_rate)))
  94. {
  95. return -RT_EINVAL;
  96. }
  97. alt_prate = rt_clk_cell_get_rate(&cpu_clk_cell->rk_cell_alt_parent->cell);
  98. /*
  99. * If the old parent clock speed is less than the clock speed
  100. * of the alternate parent, then it should be ensured that at no point
  101. * the armclk speed is more than the old_rate until the dividers are
  102. * set.
  103. */
  104. if (alt_prate > old_rate)
  105. {
  106. /* Calculate dividers */
  107. alt_div = RT_DIV_ROUND_UP(alt_prate, old_rate) - 1;
  108. if (alt_div > reg_data->div_core_mask[0])
  109. {
  110. alt_div = reg_data->div_core_mask[0];
  111. }
  112. /*
  113. * Change parents and add dividers in a single transaction.
  114. *
  115. * NOTE: we do this in a single transaction so we're never
  116. * dividing the primary parent by the extra dividers that were
  117. * needed for the alt.
  118. */
  119. for (int i = 0; i < reg_data->num_cores; ++i)
  120. {
  121. HWREG32(base + reg_data->core_reg[i]) = HIWORD_UPDATE(
  122. alt_div,
  123. reg_data->div_core_mask[i],
  124. reg_data->div_core_shift[i]);
  125. }
  126. }
  127. rockchip_cpu_clk_set_pre_muxs(cpu_clk_cell, rate);
  128. /* select alternate parent */
  129. if (reg_data->mux_core_reg)
  130. {
  131. HWREG32(base + reg_data->mux_core_reg) = HIWORD_UPDATE(
  132. reg_data->mux_core_alt,
  133. reg_data->mux_core_mask,
  134. reg_data->mux_core_shift);
  135. }
  136. else
  137. {
  138. HWREG32(base + reg_data->core_reg[0]) = HIWORD_UPDATE(
  139. reg_data->mux_core_alt,
  140. reg_data->mux_core_mask,
  141. reg_data->mux_core_shift);
  142. }
  143. return RT_EOK;
  144. }
  145. static rt_err_t rockchip_cpu_clk_post_rate_change(struct rockchip_cpu_clk_cell *cpu_clk_cell,
  146. rt_ubase_t old_rate, rt_ubase_t new_rate)
  147. {
  148. void *base = cpu_clk_cell->rk_cell.provider->reg_base;
  149. const struct rockchip_cpu_clk_rate_table *rate;
  150. const struct rockchip_cpu_clk_reg_data *reg_data = cpu_clk_cell->reg_data;
  151. if (!(rate = rockchip_get_cpu_clk_settings(cpu_clk_cell, new_rate)))
  152. {
  153. return -RT_EINVAL;
  154. }
  155. if (old_rate < new_rate)
  156. {
  157. rockchip_cpu_clk_set_dividers(cpu_clk_cell, rate);
  158. }
  159. /*
  160. * post-rate change event, re-mux to primary parent and remove dividers.
  161. *
  162. * NOTE: we do this in a single transaction so we're never dividing the
  163. * primary parent by the extra dividers that were needed for the alt.
  164. */
  165. if (reg_data->mux_core_reg)
  166. {
  167. HWREG32(base + reg_data->mux_core_reg) = HIWORD_UPDATE(
  168. reg_data->mux_core_main,
  169. reg_data->mux_core_mask,
  170. reg_data->mux_core_shift);
  171. }
  172. else
  173. {
  174. HWREG32(base + reg_data->core_reg[0]) = HIWORD_UPDATE(
  175. reg_data->mux_core_main,
  176. reg_data->mux_core_mask,
  177. reg_data->mux_core_shift);
  178. }
  179. rockchip_cpu_clk_set_post_muxs(cpu_clk_cell, rate);
  180. /* Remove dividers */
  181. for (int i = 0; i < reg_data->num_cores; ++i)
  182. {
  183. HWREG32(base + reg_data->core_reg[i]) = HIWORD_UPDATE(
  184. 0,
  185. reg_data->div_core_mask[i],
  186. reg_data->div_core_shift[i]);
  187. }
  188. if (old_rate > new_rate)
  189. {
  190. rockchip_cpu_clk_set_dividers(cpu_clk_cell, rate);
  191. }
  192. return RT_EOK;
  193. }
  194. static rt_err_t rockchip_cpu_clk_notify(struct rt_clk_notifier *notifier,
  195. rt_ubase_t msg, rt_ubase_t old_rate, rt_ubase_t new_rate)
  196. {
  197. rt_err_t err = RT_EOK;
  198. struct rockchip_cpu_clk_cell *cpu_cell;
  199. cpu_cell = rt_container_of(notifier, struct rockchip_cpu_clk_cell, notifier);
  200. if (msg == RT_CLK_MSG_PRE_RATE_CHANGE)
  201. {
  202. err = rockchip_cpu_clk_pre_rate_change(cpu_cell, old_rate, new_rate);
  203. }
  204. else if (msg == RT_CLK_MSG_POST_RATE_CHANGE)
  205. {
  206. err = rockchip_cpu_clk_post_rate_change(cpu_cell, old_rate, new_rate);
  207. }
  208. return err;
  209. }
  210. void rockchip_cpu_clk_cell_init(struct rockchip_clk_cell *rk_cell)
  211. {
  212. struct rockchip_cpu_clk_cell *cpu_clk_cell = cell_to_rockchip_cpu_cell(&rk_cell->cell);
  213. rk_cell->cell.parents_nr = 1;
  214. rk_cell->cell.parent_name = cpu_clk_cell->rk_cell_parent->cell.name;
  215. if (cpu_clk_cell->rate_count > 0)
  216. {
  217. rk_cell->cell.flags |= RT_CLK_F_SET_RATE_PARENT;
  218. }
  219. }
  220. void rockchip_cpu_clk_cell_setup(struct rockchip_clk_cell *rk_cell)
  221. {
  222. struct rockchip_cpu_clk_cell *cpu_clk_cell = cell_to_rockchip_cpu_cell(&rk_cell->cell);
  223. rt_clk_prepare_enable(rt_clk_cell_get_clk(&cpu_clk_cell->rk_cell_alt_parent->cell, RT_NULL));
  224. cpu_clk_cell->notifier.callback = rockchip_cpu_clk_notify;
  225. rt_clk_notifier_register(rt_clk_cell_get_clk(&cpu_clk_cell->rk_cell_parent->cell, RT_NULL),
  226. &cpu_clk_cell->notifier);
  227. }