clk-sys.c 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. /*
  2. * Copyright 2018-2019 Arkmicro, Inc.
  3. *
  4. * This program is free software; you can redistribute it and/or modify it
  5. * under the terms and conditions of the GNU General Public License,
  6. * version 2, as published by the Free Software Foundation.
  7. *
  8. * This program is distributed in the hope it will be useful, but WITHOUT
  9. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  10. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  11. * more details.
  12. *
  13. * You should have received a copy of the GNU General Public License along with
  14. * this program. If not, see <http://www.gnu.org/licenses/>.
  15. */
  16. #include <linux/kernel.h>
  17. #include <linux/slab.h>
  18. #include <linux/err.h>
  19. #include <linux/clk.h>
  20. #include <linux/clk-provider.h>
  21. #include <linux/io.h>
  22. #include <linux/of.h>
  23. #include <linux/of_address.h>
  24. #include <linux/log2.h>
  25. #include <dt-bindings/clock/ark-clk.h>
  26. #include "clk-ark.h"
  27. static inline int get_div(int div, int divmode)
  28. {
  29. switch(divmode) {
  30. case ARK_CLK_DIVMODE_NOZERO:
  31. div = div ? div : 1;
  32. break;
  33. case ARK_CLK_DIVMODE_PLUSONE:
  34. div = div + 1;
  35. break;
  36. case ARK_CLK_DIVMODE_DOUBLE:
  37. div *= 2;
  38. break;
  39. case ARK_CLK_DIVMODE_EXPONENT:
  40. div = 1 << div;
  41. break;
  42. case ARK_CLK_DIVMODE_PONEDOUBLE:
  43. div = (div + 1) * 2;
  44. break;
  45. }
  46. return div;
  47. }
  48. static inline int set_div(int div, int divmode)
  49. {
  50. switch(divmode) {
  51. case ARK_CLK_DIVMODE_PLUSONE:
  52. div = div - 1;
  53. break;
  54. case ARK_CLK_DIVMODE_DOUBLE:
  55. div /= 2;
  56. break;
  57. case ARK_CLK_DIVMODE_EXPONENT:
  58. div = ilog2(div);
  59. break;
  60. case ARK_CLK_DIVMODE_PONEDOUBLE:
  61. div = div / 2 - 1;
  62. break;
  63. }
  64. return div;
  65. }
  66. static unsigned long clk_sys_recalc_rate(struct clk_hw *hwclk,
  67. unsigned long parent_rate)
  68. {
  69. struct ark_clk *clk = to_ark_clk(hwclk);
  70. pr_info("clk %s rate %lu.\n", clk_hw_get_name(hwclk), parent_rate / clk->div);
  71. return parent_rate / clk->div;
  72. }
  73. static int clk_sys_enable(struct clk_hw *hwclk)
  74. {
  75. struct ark_clk *clk = to_ark_clk(hwclk);
  76. int i;
  77. for (i = 0; i < clk->enable_num; i++) {
  78. writel(readl(clk->enable_reg[i]) | (1 << clk->enable_offset[i]),
  79. clk->enable_reg[i]);
  80. }
  81. return 0;
  82. }
  83. static void clk_sys_disable(struct clk_hw *hwclk)
  84. {
  85. struct ark_clk *clk = to_ark_clk(hwclk);
  86. int i;
  87. for (i = 0; i < clk->enable_num; i++) {
  88. writel(readl(clk->enable_reg[i]) & ~(1 << clk->enable_offset[i]),
  89. clk->enable_reg[i]);
  90. }
  91. }
  92. static int clk_sys_is_enabled(struct clk_hw *hwclk)
  93. {
  94. struct ark_clk *clk = to_ark_clk(hwclk);
  95. int i, enable = 0;
  96. for (i = 0; i < clk->enable_num; i++) {
  97. enable |= !!(readl(clk->enable_reg[i]) & (1 << clk->enable_offset[i]));
  98. }
  99. return enable;
  100. }
  101. static int clk_sys_set_rate(struct clk_hw *hwclk, unsigned long rate,
  102. unsigned long parent_rate)
  103. {
  104. struct ark_clk *clk = to_ark_clk(hwclk);
  105. int div;
  106. u32 reg;
  107. if (clk->can_change) {
  108. div = DIV_ROUND_UP(parent_rate, rate);
  109. clk->div = div;
  110. div = set_div(div, clk->divmode);
  111. reg = readl(clk->reg);
  112. reg &= ~(clk->divmask << clk->divoffset);
  113. reg |= ((div & clk->divmask) << clk->divoffset);
  114. writel(reg, clk->reg);
  115. }
  116. return 0;
  117. }
  118. static long clk_sys_round_rate(struct clk_hw *hwclk, unsigned long rate,
  119. unsigned long *parent_rate)
  120. {
  121. struct ark_clk *clk = to_ark_clk(hwclk);
  122. int div;
  123. if (clk->can_change) {
  124. div = DIV_ROUND_UP(*parent_rate, rate);
  125. div = set_div(div, clk->divmode);
  126. div = get_div(div, clk->divmode);
  127. return DIV_ROUND_UP(*parent_rate, div);
  128. }
  129. return rate;
  130. }
  131. static const struct clk_ops clk_sys_ops = {
  132. .recalc_rate = clk_sys_recalc_rate,
  133. .enable = clk_sys_enable,
  134. .disable = clk_sys_disable,
  135. .is_enabled = clk_sys_is_enabled,
  136. .set_rate = clk_sys_set_rate,
  137. .round_rate = clk_sys_round_rate,
  138. };
  139. static __init struct clk *ark_sys_clk_init(struct device_node *node,
  140. const struct clk_ops *ops)
  141. {
  142. u32 reg, mask, offset, divmode = 0;
  143. void __iomem *reg_base;
  144. struct ark_clk *ark_clk;
  145. const char *clk_name = node->name;
  146. const char *parent_name;
  147. struct clk_init_data init;
  148. struct device_node *srnp;
  149. int rc, index = 0, div = 1;
  150. const __be32 *enable_list, *enable_offset_list;
  151. int enable_size, enable_offset_size;
  152. u32 val, inv_reg;
  153. int i;
  154. rc = of_property_read_u32(node, "reg", &reg);
  155. if (WARN_ON(rc))
  156. return NULL;
  157. ark_clk = kzalloc(sizeof(*ark_clk), GFP_KERNEL);
  158. if (WARN_ON(!ark_clk))
  159. return NULL;
  160. /* Map system registers */
  161. srnp = of_find_compatible_node(NULL, NULL, "arkmicro,ark-sregs");
  162. reg_base = of_iomap(srnp, 0);
  163. BUG_ON(!reg_base);
  164. ark_clk->reg = reg_base + reg;
  165. reg = readl(ark_clk->reg);
  166. of_property_read_string(node, "clock-output-names", &clk_name);
  167. init.name = clk_name;
  168. init.ops = ops;
  169. init.flags = 0;
  170. if (!of_property_read_u32(node, "index-mask", &mask) && !of_property_read_u32(node, "index-offset", &offset)) {
  171. if (!of_property_read_u32(node, "index-value", &val)) {
  172. index = val;
  173. if (val) val = 1 << (val - 1);
  174. reg &= ~(mask << offset);
  175. reg |= ((val & mask) << offset);
  176. writel(reg, ark_clk->reg);
  177. } else {
  178. index = (reg >> offset) & mask;
  179. if (index) index = ilog2(index) + 1;
  180. }
  181. }
  182. of_property_read_u32(node, "div-mode", &divmode);
  183. if (!of_property_read_u32(node, "div-mask", &mask) && !of_property_read_u32(node, "div-offset", &offset)) {
  184. ark_clk->divmode = divmode;
  185. ark_clk->divmask = mask;
  186. ark_clk->divoffset = offset;
  187. if (!of_property_read_u32(node, "div-value", &val)) {
  188. val = set_div(val, divmode);
  189. div = get_div(val, divmode);
  190. reg &= ~(mask << offset);
  191. reg |= ((val & mask) << offset);
  192. writel(reg, ark_clk->reg);
  193. } else {
  194. div = (reg >> offset) & mask;
  195. div = get_div(div, divmode);
  196. }
  197. }
  198. ark_clk->can_change = of_property_read_bool(node, "clk-can-change");
  199. enable_list = of_get_property(node, "enable-reg", &enable_size);
  200. enable_offset_list = of_get_property(node, "enable-offset", &enable_offset_size);
  201. if (enable_list && enable_offset_list && enable_size == enable_offset_size) {
  202. ark_clk->enable_num = min(ARK_CLK_MAX_ENABLE_NUM,
  203. (enable_size / sizeof(*enable_list)));
  204. for (i = 0; i < ark_clk->enable_num; i++) {
  205. ark_clk->enable_reg[i] = reg_base + be32_to_cpu(*enable_list++);
  206. ark_clk->enable_offset[i] = be32_to_cpu(*enable_offset_list++);
  207. }
  208. }
  209. if (!of_property_read_u32(node, "inv-reg", &inv_reg) &&
  210. !of_property_read_u32(node, "inv-offset", &offset) &&
  211. !of_property_read_u32(node, "inv-mask", &mask) &&
  212. !of_property_read_u32(node, "inv-value", &val)) {
  213. reg = readl(reg_base + inv_reg);
  214. reg &= ~(mask << offset);
  215. reg |= ((val & mask) << offset);
  216. writel(reg, reg_base + inv_reg);
  217. }
  218. parent_name = of_clk_get_parent_name(node, index);
  219. init.parent_names = &parent_name;
  220. init.num_parents = 1;
  221. ark_clk->div = div;
  222. ark_clk->hw.init = &init;
  223. rc = clk_hw_register(NULL, &ark_clk->hw);
  224. if (WARN_ON(rc)) {
  225. kfree(ark_clk);
  226. return NULL;
  227. }
  228. rc = of_clk_add_hw_provider(node, of_clk_hw_simple_get, &ark_clk->hw);
  229. return ark_clk->hw.clk;
  230. }
  231. static void __init of_ark_clk_sys_setup(struct device_node *np)
  232. {
  233. ark_sys_clk_init(np, &clk_sys_ops);
  234. }
  235. CLK_OF_DECLARE(ark_clk_sys, "arkmiro,ark-clk-sys", of_ark_clk_sys_setup);