clk-iproc-armpll.c 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. // Copyright (C) 2014 Broadcom Corporation
  3. #include <linux/kernel.h>
  4. #include <linux/slab.h>
  5. #include <linux/err.h>
  6. #include <linux/clk-provider.h>
  7. #include <linux/io.h>
  8. #include <linux/of.h>
  9. #include <linux/clkdev.h>
  10. #include <linux/of_address.h>
  11. #include "clk-iproc.h"
  12. #define IPROC_CLK_MAX_FREQ_POLICY 0x3
  13. #define IPROC_CLK_POLICY_FREQ_OFFSET 0x008
  14. #define IPROC_CLK_POLICY_FREQ_POLICY_FREQ_SHIFT 8
  15. #define IPROC_CLK_POLICY_FREQ_POLICY_FREQ_MASK 0x7
  16. #define IPROC_CLK_PLLARMA_OFFSET 0xc00
  17. #define IPROC_CLK_PLLARMA_LOCK_SHIFT 28
  18. #define IPROC_CLK_PLLARMA_PDIV_SHIFT 24
  19. #define IPROC_CLK_PLLARMA_PDIV_MASK 0xf
  20. #define IPROC_CLK_PLLARMA_NDIV_INT_SHIFT 8
  21. #define IPROC_CLK_PLLARMA_NDIV_INT_MASK 0x3ff
  22. #define IPROC_CLK_PLLARMB_OFFSET 0xc04
  23. #define IPROC_CLK_PLLARMB_NDIV_FRAC_MASK 0xfffff
  24. #define IPROC_CLK_PLLARMC_OFFSET 0xc08
  25. #define IPROC_CLK_PLLARMC_BYPCLK_EN_SHIFT 8
  26. #define IPROC_CLK_PLLARMC_MDIV_MASK 0xff
  27. #define IPROC_CLK_PLLARMCTL5_OFFSET 0xc20
  28. #define IPROC_CLK_PLLARMCTL5_H_MDIV_MASK 0xff
  29. #define IPROC_CLK_PLLARM_OFFSET_OFFSET 0xc24
  30. #define IPROC_CLK_PLLARM_SW_CTL_SHIFT 29
  31. #define IPROC_CLK_PLLARM_NDIV_INT_OFFSET_SHIFT 20
  32. #define IPROC_CLK_PLLARM_NDIV_INT_OFFSET_MASK 0xff
  33. #define IPROC_CLK_PLLARM_NDIV_FRAC_OFFSET_MASK 0xfffff
  34. #define IPROC_CLK_ARM_DIV_OFFSET 0xe00
  35. #define IPROC_CLK_ARM_DIV_PLL_SELECT_OVERRIDE_SHIFT 4
  36. #define IPROC_CLK_ARM_DIV_ARM_PLL_SELECT_MASK 0xf
  37. #define IPROC_CLK_POLICY_DBG_OFFSET 0xec0
  38. #define IPROC_CLK_POLICY_DBG_ACT_FREQ_SHIFT 12
  39. #define IPROC_CLK_POLICY_DBG_ACT_FREQ_MASK 0x7
  40. enum iproc_arm_pll_fid {
  41. ARM_PLL_FID_CRYSTAL_CLK = 0,
  42. ARM_PLL_FID_SYS_CLK = 2,
  43. ARM_PLL_FID_CH0_SLOW_CLK = 6,
  44. ARM_PLL_FID_CH1_FAST_CLK = 7
  45. };
  46. struct iproc_arm_pll {
  47. struct clk_hw hw;
  48. void __iomem *base;
  49. unsigned long rate;
  50. };
  51. #define to_iproc_arm_pll(hw) container_of(hw, struct iproc_arm_pll, hw)
  52. static unsigned int __get_fid(struct iproc_arm_pll *pll)
  53. {
  54. u32 val;
  55. unsigned int policy, fid, active_fid;
  56. val = readl(pll->base + IPROC_CLK_ARM_DIV_OFFSET);
  57. if (val & (1 << IPROC_CLK_ARM_DIV_PLL_SELECT_OVERRIDE_SHIFT))
  58. policy = val & IPROC_CLK_ARM_DIV_ARM_PLL_SELECT_MASK;
  59. else
  60. policy = 0;
  61. /* something is seriously wrong */
  62. BUG_ON(policy > IPROC_CLK_MAX_FREQ_POLICY);
  63. val = readl(pll->base + IPROC_CLK_POLICY_FREQ_OFFSET);
  64. fid = (val >> (IPROC_CLK_POLICY_FREQ_POLICY_FREQ_SHIFT * policy)) &
  65. IPROC_CLK_POLICY_FREQ_POLICY_FREQ_MASK;
  66. val = readl(pll->base + IPROC_CLK_POLICY_DBG_OFFSET);
  67. active_fid = IPROC_CLK_POLICY_DBG_ACT_FREQ_MASK &
  68. (val >> IPROC_CLK_POLICY_DBG_ACT_FREQ_SHIFT);
  69. if (fid != active_fid) {
  70. pr_debug("%s: fid override %u->%u\n", __func__, fid,
  71. active_fid);
  72. fid = active_fid;
  73. }
  74. pr_debug("%s: active fid: %u\n", __func__, fid);
  75. return fid;
  76. }
  77. /*
  78. * Determine the mdiv (post divider) based on the frequency ID being used.
  79. * There are 4 sources that can be used to derive the output clock rate:
  80. * - 25 MHz Crystal
  81. * - System clock
  82. * - PLL channel 0 (slow clock)
  83. * - PLL channel 1 (fast clock)
  84. */
  85. static int __get_mdiv(struct iproc_arm_pll *pll)
  86. {
  87. unsigned int fid;
  88. int mdiv;
  89. u32 val;
  90. fid = __get_fid(pll);
  91. switch (fid) {
  92. case ARM_PLL_FID_CRYSTAL_CLK:
  93. case ARM_PLL_FID_SYS_CLK:
  94. mdiv = 1;
  95. break;
  96. case ARM_PLL_FID_CH0_SLOW_CLK:
  97. val = readl(pll->base + IPROC_CLK_PLLARMC_OFFSET);
  98. mdiv = val & IPROC_CLK_PLLARMC_MDIV_MASK;
  99. if (mdiv == 0)
  100. mdiv = 256;
  101. break;
  102. case ARM_PLL_FID_CH1_FAST_CLK:
  103. val = readl(pll->base + IPROC_CLK_PLLARMCTL5_OFFSET);
  104. mdiv = val & IPROC_CLK_PLLARMCTL5_H_MDIV_MASK;
  105. if (mdiv == 0)
  106. mdiv = 256;
  107. break;
  108. default:
  109. mdiv = -EFAULT;
  110. }
  111. return mdiv;
  112. }
  113. static unsigned int __get_ndiv(struct iproc_arm_pll *pll)
  114. {
  115. u32 val;
  116. unsigned int ndiv_int, ndiv_frac, ndiv;
  117. val = readl(pll->base + IPROC_CLK_PLLARM_OFFSET_OFFSET);
  118. if (val & (1 << IPROC_CLK_PLLARM_SW_CTL_SHIFT)) {
  119. /*
  120. * offset mode is active. Read the ndiv from the PLLARM OFFSET
  121. * register
  122. */
  123. ndiv_int = (val >> IPROC_CLK_PLLARM_NDIV_INT_OFFSET_SHIFT) &
  124. IPROC_CLK_PLLARM_NDIV_INT_OFFSET_MASK;
  125. if (ndiv_int == 0)
  126. ndiv_int = 256;
  127. ndiv_frac = val & IPROC_CLK_PLLARM_NDIV_FRAC_OFFSET_MASK;
  128. } else {
  129. /* offset mode not active */
  130. val = readl(pll->base + IPROC_CLK_PLLARMA_OFFSET);
  131. ndiv_int = (val >> IPROC_CLK_PLLARMA_NDIV_INT_SHIFT) &
  132. IPROC_CLK_PLLARMA_NDIV_INT_MASK;
  133. if (ndiv_int == 0)
  134. ndiv_int = 1024;
  135. val = readl(pll->base + IPROC_CLK_PLLARMB_OFFSET);
  136. ndiv_frac = val & IPROC_CLK_PLLARMB_NDIV_FRAC_MASK;
  137. }
  138. ndiv = (ndiv_int << 20) | ndiv_frac;
  139. return ndiv;
  140. }
  141. /*
  142. * The output frequency of the ARM PLL is calculated based on the ARM PLL
  143. * divider values:
  144. * pdiv = ARM PLL pre-divider
  145. * ndiv = ARM PLL multiplier
  146. * mdiv = ARM PLL post divider
  147. *
  148. * The frequency is calculated by:
  149. * ((ndiv * parent clock rate) / pdiv) / mdiv
  150. */
  151. static unsigned long iproc_arm_pll_recalc_rate(struct clk_hw *hw,
  152. unsigned long parent_rate)
  153. {
  154. struct iproc_arm_pll *pll = to_iproc_arm_pll(hw);
  155. u32 val;
  156. int mdiv;
  157. u64 ndiv;
  158. unsigned int pdiv;
  159. /* in bypass mode, use parent rate */
  160. val = readl(pll->base + IPROC_CLK_PLLARMC_OFFSET);
  161. if (val & (1 << IPROC_CLK_PLLARMC_BYPCLK_EN_SHIFT)) {
  162. pll->rate = parent_rate;
  163. return pll->rate;
  164. }
  165. /* PLL needs to be locked */
  166. val = readl(pll->base + IPROC_CLK_PLLARMA_OFFSET);
  167. if (!(val & (1 << IPROC_CLK_PLLARMA_LOCK_SHIFT))) {
  168. pll->rate = 0;
  169. return 0;
  170. }
  171. pdiv = (val >> IPROC_CLK_PLLARMA_PDIV_SHIFT) &
  172. IPROC_CLK_PLLARMA_PDIV_MASK;
  173. if (pdiv == 0)
  174. pdiv = 16;
  175. ndiv = __get_ndiv(pll);
  176. mdiv = __get_mdiv(pll);
  177. if (mdiv <= 0) {
  178. pll->rate = 0;
  179. return 0;
  180. }
  181. pll->rate = (ndiv * parent_rate) >> 20;
  182. pll->rate = (pll->rate / pdiv) / mdiv;
  183. pr_debug("%s: ARM PLL rate: %lu. parent rate: %lu\n", __func__,
  184. pll->rate, parent_rate);
  185. pr_debug("%s: ndiv_int: %u, pdiv: %u, mdiv: %d\n", __func__,
  186. (unsigned int)(ndiv >> 20), pdiv, mdiv);
  187. return pll->rate;
  188. }
  189. static const struct clk_ops iproc_arm_pll_ops = {
  190. .recalc_rate = iproc_arm_pll_recalc_rate,
  191. };
  192. void __init iproc_armpll_setup(struct device_node *node)
  193. {
  194. int ret;
  195. struct iproc_arm_pll *pll;
  196. struct clk_init_data init;
  197. const char *parent_name;
  198. pll = kzalloc(sizeof(*pll), GFP_KERNEL);
  199. if (WARN_ON(!pll))
  200. return;
  201. pll->base = of_iomap(node, 0);
  202. if (WARN_ON(!pll->base))
  203. goto err_free_pll;
  204. init.name = node->name;
  205. init.ops = &iproc_arm_pll_ops;
  206. init.flags = 0;
  207. parent_name = of_clk_get_parent_name(node, 0);
  208. init.parent_names = (parent_name ? &parent_name : NULL);
  209. init.num_parents = (parent_name ? 1 : 0);
  210. pll->hw.init = &init;
  211. ret = clk_hw_register(NULL, &pll->hw);
  212. if (WARN_ON(ret))
  213. goto err_iounmap;
  214. ret = of_clk_add_hw_provider(node, of_clk_hw_simple_get, &pll->hw);
  215. if (WARN_ON(ret))
  216. goto err_clk_unregister;
  217. return;
  218. err_clk_unregister:
  219. clk_hw_unregister(&pll->hw);
  220. err_iounmap:
  221. iounmap(pll->base);
  222. err_free_pll:
  223. kfree(pll);
  224. }