clk-regmap-mux-div.c 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Copyright (c) 2017, Linaro Limited
  4. * Author: Georgi Djakov <[email protected]>
  5. */
  6. #include <linux/bitops.h>
  7. #include <linux/delay.h>
  8. #include <linux/kernel.h>
  9. #include <linux/regmap.h>
  10. #include "clk-regmap-mux-div.h"
  11. #define CMD_RCGR 0x0
  12. #define CMD_RCGR_UPDATE BIT(0)
  13. #define CMD_RCGR_DIRTY_CFG BIT(4)
  14. #define CMD_RCGR_ROOT_OFF BIT(31)
  15. #define CFG_RCGR 0x4
  16. #define to_clk_regmap_mux_div(_hw) \
  17. container_of(to_clk_regmap(_hw), struct clk_regmap_mux_div, clkr)
  18. int mux_div_set_src_div(struct clk_regmap_mux_div *md, u32 src, u32 div)
  19. {
  20. int ret, count;
  21. u32 val, mask;
  22. const char *name = clk_hw_get_name(&md->clkr.hw);
  23. val = (div << md->hid_shift) | (src << md->src_shift);
  24. mask = ((BIT(md->hid_width) - 1) << md->hid_shift) |
  25. ((BIT(md->src_width) - 1) << md->src_shift);
  26. ret = regmap_update_bits(md->clkr.regmap, CFG_RCGR + md->reg_offset,
  27. mask, val);
  28. if (ret)
  29. return ret;
  30. ret = regmap_update_bits(md->clkr.regmap, CMD_RCGR + md->reg_offset,
  31. CMD_RCGR_UPDATE, CMD_RCGR_UPDATE);
  32. if (ret)
  33. return ret;
  34. /* Wait for update to take effect */
  35. for (count = 500; count > 0; count--) {
  36. ret = regmap_read(md->clkr.regmap, CMD_RCGR + md->reg_offset,
  37. &val);
  38. if (ret)
  39. return ret;
  40. if (!(val & CMD_RCGR_UPDATE))
  41. return 0;
  42. udelay(1);
  43. }
  44. pr_err("%s: RCG did not update its configuration", name);
  45. return -EBUSY;
  46. }
  47. EXPORT_SYMBOL_GPL(mux_div_set_src_div);
  48. int mux_div_get_src_div(struct clk_regmap_mux_div *md, u32 *src,
  49. u32 *div)
  50. {
  51. int ret = 0;
  52. u32 val, d, s;
  53. const char *name = clk_hw_get_name(&md->clkr.hw);
  54. ret = regmap_read(md->clkr.regmap, CMD_RCGR + md->reg_offset, &val);
  55. if (ret)
  56. return ret;
  57. if (val & CMD_RCGR_DIRTY_CFG) {
  58. pr_err("%s: RCG configuration is pending\n", name);
  59. return -EBUSY;
  60. }
  61. ret = regmap_read(md->clkr.regmap, CFG_RCGR + md->reg_offset, &val);
  62. if (ret)
  63. return ret;
  64. s = (val >> md->src_shift);
  65. s &= BIT(md->src_width) - 1;
  66. *src = s;
  67. d = (val >> md->hid_shift);
  68. d &= BIT(md->hid_width) - 1;
  69. *div = d;
  70. return ret;
  71. }
  72. EXPORT_SYMBOL_GPL(mux_div_get_src_div);
  73. static inline bool is_better_rate(unsigned long req, unsigned long best,
  74. unsigned long new)
  75. {
  76. return (req <= new && new < best) || (best < req && best < new);
  77. }
  78. static int mux_div_determine_rate(struct clk_hw *hw,
  79. struct clk_rate_request *req)
  80. {
  81. struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
  82. unsigned int i, div, max_div;
  83. unsigned long actual_rate, best_rate = 0;
  84. unsigned long req_rate = req->rate;
  85. for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
  86. struct clk_hw *parent = clk_hw_get_parent_by_index(hw, i);
  87. unsigned long parent_rate = clk_hw_get_rate(parent);
  88. max_div = BIT(md->hid_width) - 1;
  89. for (div = 1; div < max_div; div++) {
  90. parent_rate = mult_frac(req_rate, div, 2);
  91. parent_rate = clk_hw_round_rate(parent, parent_rate);
  92. actual_rate = mult_frac(parent_rate, 2, div);
  93. if (is_better_rate(req_rate, best_rate, actual_rate)) {
  94. best_rate = actual_rate;
  95. req->rate = best_rate;
  96. req->best_parent_rate = parent_rate;
  97. req->best_parent_hw = parent;
  98. }
  99. if (actual_rate < req_rate || best_rate <= req_rate)
  100. break;
  101. }
  102. }
  103. if (!best_rate)
  104. return -EINVAL;
  105. return 0;
  106. }
  107. static int __mux_div_set_rate_and_parent(struct clk_hw *hw, unsigned long rate,
  108. unsigned long prate, u32 src)
  109. {
  110. struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
  111. int ret;
  112. u32 div, max_div, best_src = 0, best_div = 0;
  113. unsigned int i;
  114. unsigned long actual_rate, best_rate = 0;
  115. for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
  116. struct clk_hw *parent = clk_hw_get_parent_by_index(hw, i);
  117. unsigned long parent_rate = clk_hw_get_rate(parent);
  118. max_div = BIT(md->hid_width) - 1;
  119. for (div = 1; div < max_div; div++) {
  120. parent_rate = mult_frac(rate, div, 2);
  121. parent_rate = clk_hw_round_rate(parent, parent_rate);
  122. actual_rate = mult_frac(parent_rate, 2, div);
  123. if (is_better_rate(rate, best_rate, actual_rate)) {
  124. best_rate = actual_rate;
  125. best_src = md->parent_map[i].cfg;
  126. best_div = div - 1;
  127. }
  128. if (actual_rate < rate || best_rate <= rate)
  129. break;
  130. }
  131. }
  132. ret = mux_div_set_src_div(md, best_src, best_div);
  133. if (!ret) {
  134. md->div = best_div;
  135. md->src = best_src;
  136. }
  137. return ret;
  138. }
  139. static u8 mux_div_get_parent(struct clk_hw *hw)
  140. {
  141. struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
  142. const char *name = clk_hw_get_name(hw);
  143. u32 i, div, src = 0;
  144. mux_div_get_src_div(md, &src, &div);
  145. for (i = 0; i < clk_hw_get_num_parents(hw); i++)
  146. if (src == md->parent_map[i].cfg)
  147. return i;
  148. pr_err("%s: Can't find parent with src %d\n", name, src);
  149. return 0;
  150. }
  151. static int mux_div_set_parent(struct clk_hw *hw, u8 index)
  152. {
  153. struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
  154. return mux_div_set_src_div(md, md->parent_map[index].cfg, md->div);
  155. }
  156. static int mux_div_set_rate(struct clk_hw *hw,
  157. unsigned long rate, unsigned long prate)
  158. {
  159. struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
  160. return __mux_div_set_rate_and_parent(hw, rate, prate, md->src);
  161. }
  162. static int mux_div_set_rate_and_parent(struct clk_hw *hw, unsigned long rate,
  163. unsigned long prate, u8 index)
  164. {
  165. struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
  166. return __mux_div_set_rate_and_parent(hw, rate, prate,
  167. md->parent_map[index].cfg);
  168. }
  169. static unsigned long mux_div_recalc_rate(struct clk_hw *hw, unsigned long prate)
  170. {
  171. struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
  172. u32 div, src;
  173. int i, num_parents = clk_hw_get_num_parents(hw);
  174. const char *name = clk_hw_get_name(hw);
  175. mux_div_get_src_div(md, &src, &div);
  176. for (i = 0; i < num_parents; i++)
  177. if (src == md->parent_map[i].cfg) {
  178. struct clk_hw *p = clk_hw_get_parent_by_index(hw, i);
  179. unsigned long parent_rate = clk_hw_get_rate(p);
  180. return mult_frac(parent_rate, 2, div + 1);
  181. }
  182. pr_err("%s: Can't find parent %d\n", name, src);
  183. return 0;
  184. }
  185. static int mux_div_enable(struct clk_hw *hw)
  186. {
  187. struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
  188. return mux_div_set_src_div(md, md->src, md->div);
  189. }
  190. static void mux_div_disable(struct clk_hw *hw)
  191. {
  192. struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
  193. mux_div_set_src_div(md, md->safe_src, md->safe_div);
  194. }
  195. const struct clk_ops clk_regmap_mux_div_ops = {
  196. .enable = mux_div_enable,
  197. .disable = mux_div_disable,
  198. .get_parent = mux_div_get_parent,
  199. .set_parent = mux_div_set_parent,
  200. .set_rate = mux_div_set_rate,
  201. .set_rate_and_parent = mux_div_set_rate_and_parent,
  202. .determine_rate = mux_div_determine_rate,
  203. .recalc_rate = mux_div_recalc_rate,
  204. };
  205. EXPORT_SYMBOL_GPL(clk_regmap_mux_div_ops);