clk-iproc-asiu.c 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. // Copyright (C) 2014 Broadcom Corporation
  3. #include <linux/kernel.h>
  4. #include <linux/err.h>
  5. #include <linux/clk-provider.h>
  6. #include <linux/io.h>
  7. #include <linux/of.h>
  8. #include <linux/clkdev.h>
  9. #include <linux/of_address.h>
  10. #include <linux/delay.h>
  11. #include "clk-iproc.h"
  12. struct iproc_asiu;
  13. struct iproc_asiu_clk {
  14. struct clk_hw hw;
  15. const char *name;
  16. struct iproc_asiu *asiu;
  17. unsigned long rate;
  18. struct iproc_asiu_div div;
  19. struct iproc_asiu_gate gate;
  20. };
  21. struct iproc_asiu {
  22. void __iomem *div_base;
  23. void __iomem *gate_base;
  24. struct clk_hw_onecell_data *clk_data;
  25. struct iproc_asiu_clk *clks;
  26. };
  27. #define to_asiu_clk(hw) container_of(hw, struct iproc_asiu_clk, hw)
  28. static int iproc_asiu_clk_enable(struct clk_hw *hw)
  29. {
  30. struct iproc_asiu_clk *clk = to_asiu_clk(hw);
  31. struct iproc_asiu *asiu = clk->asiu;
  32. u32 val;
  33. /* some clocks at the ASIU level are always enabled */
  34. if (clk->gate.offset == IPROC_CLK_INVALID_OFFSET)
  35. return 0;
  36. val = readl(asiu->gate_base + clk->gate.offset);
  37. val |= (1 << clk->gate.en_shift);
  38. writel(val, asiu->gate_base + clk->gate.offset);
  39. return 0;
  40. }
  41. static void iproc_asiu_clk_disable(struct clk_hw *hw)
  42. {
  43. struct iproc_asiu_clk *clk = to_asiu_clk(hw);
  44. struct iproc_asiu *asiu = clk->asiu;
  45. u32 val;
  46. /* some clocks at the ASIU level are always enabled */
  47. if (clk->gate.offset == IPROC_CLK_INVALID_OFFSET)
  48. return;
  49. val = readl(asiu->gate_base + clk->gate.offset);
  50. val &= ~(1 << clk->gate.en_shift);
  51. writel(val, asiu->gate_base + clk->gate.offset);
  52. }
  53. static unsigned long iproc_asiu_clk_recalc_rate(struct clk_hw *hw,
  54. unsigned long parent_rate)
  55. {
  56. struct iproc_asiu_clk *clk = to_asiu_clk(hw);
  57. struct iproc_asiu *asiu = clk->asiu;
  58. u32 val;
  59. unsigned int div_h, div_l;
  60. if (parent_rate == 0) {
  61. clk->rate = 0;
  62. return 0;
  63. }
  64. /* if clock divisor is not enabled, simply return parent rate */
  65. val = readl(asiu->div_base + clk->div.offset);
  66. if ((val & (1 << clk->div.en_shift)) == 0) {
  67. clk->rate = parent_rate;
  68. return parent_rate;
  69. }
  70. /* clock rate = parent rate / (high_div + 1) + (low_div + 1) */
  71. div_h = (val >> clk->div.high_shift) & bit_mask(clk->div.high_width);
  72. div_h++;
  73. div_l = (val >> clk->div.low_shift) & bit_mask(clk->div.low_width);
  74. div_l++;
  75. clk->rate = parent_rate / (div_h + div_l);
  76. pr_debug("%s: rate: %lu. parent rate: %lu div_h: %u div_l: %u\n",
  77. __func__, clk->rate, parent_rate, div_h, div_l);
  78. return clk->rate;
  79. }
  80. static long iproc_asiu_clk_round_rate(struct clk_hw *hw, unsigned long rate,
  81. unsigned long *parent_rate)
  82. {
  83. unsigned int div;
  84. if (rate == 0 || *parent_rate == 0)
  85. return -EINVAL;
  86. if (rate == *parent_rate)
  87. return *parent_rate;
  88. div = DIV_ROUND_CLOSEST(*parent_rate, rate);
  89. if (div < 2)
  90. return *parent_rate;
  91. return *parent_rate / div;
  92. }
  93. static int iproc_asiu_clk_set_rate(struct clk_hw *hw, unsigned long rate,
  94. unsigned long parent_rate)
  95. {
  96. struct iproc_asiu_clk *clk = to_asiu_clk(hw);
  97. struct iproc_asiu *asiu = clk->asiu;
  98. unsigned int div, div_h, div_l;
  99. u32 val;
  100. if (rate == 0 || parent_rate == 0)
  101. return -EINVAL;
  102. /* simply disable the divisor if one wants the same rate as parent */
  103. if (rate == parent_rate) {
  104. val = readl(asiu->div_base + clk->div.offset);
  105. val &= ~(1 << clk->div.en_shift);
  106. writel(val, asiu->div_base + clk->div.offset);
  107. return 0;
  108. }
  109. div = DIV_ROUND_CLOSEST(parent_rate, rate);
  110. if (div < 2)
  111. return -EINVAL;
  112. div_h = div_l = div >> 1;
  113. div_h--;
  114. div_l--;
  115. val = readl(asiu->div_base + clk->div.offset);
  116. val |= 1 << clk->div.en_shift;
  117. if (div_h) {
  118. val &= ~(bit_mask(clk->div.high_width)
  119. << clk->div.high_shift);
  120. val |= div_h << clk->div.high_shift;
  121. } else {
  122. val &= ~(bit_mask(clk->div.high_width)
  123. << clk->div.high_shift);
  124. }
  125. if (div_l) {
  126. val &= ~(bit_mask(clk->div.low_width) << clk->div.low_shift);
  127. val |= div_l << clk->div.low_shift;
  128. } else {
  129. val &= ~(bit_mask(clk->div.low_width) << clk->div.low_shift);
  130. }
  131. writel(val, asiu->div_base + clk->div.offset);
  132. return 0;
  133. }
  134. static const struct clk_ops iproc_asiu_ops = {
  135. .enable = iproc_asiu_clk_enable,
  136. .disable = iproc_asiu_clk_disable,
  137. .recalc_rate = iproc_asiu_clk_recalc_rate,
  138. .round_rate = iproc_asiu_clk_round_rate,
  139. .set_rate = iproc_asiu_clk_set_rate,
  140. };
  141. void __init iproc_asiu_setup(struct device_node *node,
  142. const struct iproc_asiu_div *div,
  143. const struct iproc_asiu_gate *gate,
  144. unsigned int num_clks)
  145. {
  146. int i, ret;
  147. struct iproc_asiu *asiu;
  148. if (WARN_ON(!gate || !div))
  149. return;
  150. asiu = kzalloc(sizeof(*asiu), GFP_KERNEL);
  151. if (WARN_ON(!asiu))
  152. return;
  153. asiu->clk_data = kzalloc(struct_size(asiu->clk_data, hws, num_clks),
  154. GFP_KERNEL);
  155. if (WARN_ON(!asiu->clk_data))
  156. goto err_clks;
  157. asiu->clk_data->num = num_clks;
  158. asiu->clks = kcalloc(num_clks, sizeof(*asiu->clks), GFP_KERNEL);
  159. if (WARN_ON(!asiu->clks))
  160. goto err_asiu_clks;
  161. asiu->div_base = of_iomap(node, 0);
  162. if (WARN_ON(!asiu->div_base))
  163. goto err_iomap_div;
  164. asiu->gate_base = of_iomap(node, 1);
  165. if (WARN_ON(!asiu->gate_base))
  166. goto err_iomap_gate;
  167. for (i = 0; i < num_clks; i++) {
  168. struct clk_init_data init;
  169. const char *parent_name;
  170. struct iproc_asiu_clk *asiu_clk;
  171. const char *clk_name;
  172. ret = of_property_read_string_index(node, "clock-output-names",
  173. i, &clk_name);
  174. if (WARN_ON(ret))
  175. goto err_clk_register;
  176. asiu_clk = &asiu->clks[i];
  177. asiu_clk->name = clk_name;
  178. asiu_clk->asiu = asiu;
  179. asiu_clk->div = div[i];
  180. asiu_clk->gate = gate[i];
  181. init.name = clk_name;
  182. init.ops = &iproc_asiu_ops;
  183. init.flags = 0;
  184. parent_name = of_clk_get_parent_name(node, 0);
  185. init.parent_names = (parent_name ? &parent_name : NULL);
  186. init.num_parents = (parent_name ? 1 : 0);
  187. asiu_clk->hw.init = &init;
  188. ret = clk_hw_register(NULL, &asiu_clk->hw);
  189. if (WARN_ON(ret))
  190. goto err_clk_register;
  191. asiu->clk_data->hws[i] = &asiu_clk->hw;
  192. }
  193. ret = of_clk_add_hw_provider(node, of_clk_hw_onecell_get,
  194. asiu->clk_data);
  195. if (WARN_ON(ret))
  196. goto err_clk_register;
  197. return;
  198. err_clk_register:
  199. while (--i >= 0)
  200. clk_hw_unregister(asiu->clk_data->hws[i]);
  201. iounmap(asiu->gate_base);
  202. err_iomap_gate:
  203. iounmap(asiu->div_base);
  204. err_iomap_div:
  205. kfree(asiu->clks);
  206. err_asiu_clks:
  207. kfree(asiu->clk_data);
  208. err_clks:
  209. kfree(asiu);
  210. }