gxbb-aoclk-32k.c 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * Copyright (c) 2017 BayLibre, SAS.
  4. * Author: Neil Armstrong <narmstrong@baylibre.com>
  5. */
  6. #include <linux/clk-provider.h>
  7. #include <linux/bitfield.h>
  8. #include <linux/regmap.h>
  9. #include "gxbb-aoclk.h"
  10. /*
  11. * The AO Domain embeds a dual/divider to generate a more precise
  12. * 32,768KHz clock for low-power suspend mode and CEC.
  13. * ______ ______
  14. * | | | |
  15. * ______ | Div1 |-| Cnt1 | ______
  16. * | | /|______| |______|\ | |
  17. * Xtal-->| Gate |---| ______ ______ X-X--| Gate |-->
  18. * |______| | \| | | |/ | |______|
  19. * | | Div2 |-| Cnt2 | |
  20. * | |______| |______| |
  21. * |_______________________|
  22. *
  23. * The dividing can be switched to single or dual, with a counter
  24. * for each divider to set when the switching is done.
  25. * The entire dividing mechanism can be also bypassed.
  26. */
  27. #define CLK_CNTL0_N1_MASK GENMASK(11, 0)
  28. #define CLK_CNTL0_N2_MASK GENMASK(23, 12)
  29. #define CLK_CNTL0_DUALDIV_EN BIT(28)
  30. #define CLK_CNTL0_OUT_GATE_EN BIT(30)
  31. #define CLK_CNTL0_IN_GATE_EN BIT(31)
  32. #define CLK_CNTL1_M1_MASK GENMASK(11, 0)
  33. #define CLK_CNTL1_M2_MASK GENMASK(23, 12)
  34. #define CLK_CNTL1_BYPASS_EN BIT(24)
  35. #define CLK_CNTL1_SELECT_OSC BIT(27)
  36. #define PWR_CNTL_ALT_32K_SEL GENMASK(13, 10)
  37. struct cec_32k_freq_table {
  38. unsigned long parent_rate;
  39. unsigned long target_rate;
  40. bool dualdiv;
  41. unsigned int n1;
  42. unsigned int n2;
  43. unsigned int m1;
  44. unsigned int m2;
  45. };
  46. static const struct cec_32k_freq_table aoclk_cec_32k_table[] = {
  47. [0] = {
  48. .parent_rate = 24000000,
  49. .target_rate = 32768,
  50. .dualdiv = true,
  51. .n1 = 733,
  52. .n2 = 732,
  53. .m1 = 8,
  54. .m2 = 11,
  55. },
  56. };
  57. /*
  58. * If CLK_CNTL0_DUALDIV_EN == 0
  59. * - will use N1 divider only
  60. * If CLK_CNTL0_DUALDIV_EN == 1
  61. * - hold M1 cycles of N1 divider then changes to N2
  62. * - hold M2 cycles of N2 divider then changes to N1
  63. * Then we can get more accurate division.
  64. */
  65. static unsigned long aoclk_cec_32k_recalc_rate(struct clk_hw *hw,
  66. unsigned long parent_rate)
  67. {
  68. struct aoclk_cec_32k *cec_32k = to_aoclk_cec_32k(hw);
  69. unsigned long n1;
  70. u32 reg0, reg1;
  71. regmap_read(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0, &reg0);
  72. regmap_read(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL1, &reg1);
  73. if (reg1 & CLK_CNTL1_BYPASS_EN)
  74. return parent_rate;
  75. if (reg0 & CLK_CNTL0_DUALDIV_EN) {
  76. unsigned long n2, m1, m2, f1, f2, p1, p2;
  77. n1 = FIELD_GET(CLK_CNTL0_N1_MASK, reg0) + 1;
  78. n2 = FIELD_GET(CLK_CNTL0_N2_MASK, reg0) + 1;
  79. m1 = FIELD_GET(CLK_CNTL1_M1_MASK, reg1) + 1;
  80. m2 = FIELD_GET(CLK_CNTL1_M2_MASK, reg1) + 1;
  81. f1 = DIV_ROUND_CLOSEST(parent_rate, n1);
  82. f2 = DIV_ROUND_CLOSEST(parent_rate, n2);
  83. p1 = DIV_ROUND_CLOSEST(100000000 * m1, f1 * (m1 + m2));
  84. p2 = DIV_ROUND_CLOSEST(100000000 * m2, f2 * (m1 + m2));
  85. return DIV_ROUND_UP(100000000, p1 + p2);
  86. }
  87. n1 = FIELD_GET(CLK_CNTL0_N1_MASK, reg0) + 1;
  88. return DIV_ROUND_CLOSEST(parent_rate, n1);
  89. }
  90. static const struct cec_32k_freq_table *find_cec_32k_freq(unsigned long rate,
  91. unsigned long prate)
  92. {
  93. int i;
  94. for (i = 0 ; i < ARRAY_SIZE(aoclk_cec_32k_table) ; ++i)
  95. if (aoclk_cec_32k_table[i].parent_rate == prate &&
  96. aoclk_cec_32k_table[i].target_rate == rate)
  97. return &aoclk_cec_32k_table[i];
  98. return NULL;
  99. }
  100. static long aoclk_cec_32k_round_rate(struct clk_hw *hw, unsigned long rate,
  101. unsigned long *prate)
  102. {
  103. const struct cec_32k_freq_table *freq = find_cec_32k_freq(rate,
  104. *prate);
  105. /* If invalid return first one */
  106. if (!freq)
  107. return aoclk_cec_32k_table[0].target_rate;
  108. return freq->target_rate;
  109. }
  110. /*
  111. * From the Amlogic init procedure, the IN and OUT gates needs to be handled
  112. * in the init procedure to avoid any glitches.
  113. */
  114. static int aoclk_cec_32k_set_rate(struct clk_hw *hw, unsigned long rate,
  115. unsigned long parent_rate)
  116. {
  117. const struct cec_32k_freq_table *freq = find_cec_32k_freq(rate,
  118. parent_rate);
  119. struct aoclk_cec_32k *cec_32k = to_aoclk_cec_32k(hw);
  120. u32 reg = 0;
  121. if (!freq)
  122. return -EINVAL;
  123. /* Disable clock */
  124. regmap_update_bits(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0,
  125. CLK_CNTL0_IN_GATE_EN | CLK_CNTL0_OUT_GATE_EN, 0);
  126. reg = FIELD_PREP(CLK_CNTL0_N1_MASK, freq->n1 - 1);
  127. if (freq->dualdiv)
  128. reg |= CLK_CNTL0_DUALDIV_EN |
  129. FIELD_PREP(CLK_CNTL0_N2_MASK, freq->n2 - 1);
  130. regmap_write(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0, reg);
  131. reg = FIELD_PREP(CLK_CNTL1_M1_MASK, freq->m1 - 1);
  132. if (freq->dualdiv)
  133. reg |= FIELD_PREP(CLK_CNTL1_M2_MASK, freq->m2 - 1);
  134. regmap_write(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL1, reg);
  135. /* Enable clock */
  136. regmap_update_bits(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0,
  137. CLK_CNTL0_IN_GATE_EN, CLK_CNTL0_IN_GATE_EN);
  138. udelay(200);
  139. regmap_update_bits(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0,
  140. CLK_CNTL0_OUT_GATE_EN, CLK_CNTL0_OUT_GATE_EN);
  141. regmap_update_bits(cec_32k->regmap, AO_CRT_CLK_CNTL1,
  142. CLK_CNTL1_SELECT_OSC, CLK_CNTL1_SELECT_OSC);
  143. /* Select 32k from XTAL */
  144. regmap_update_bits(cec_32k->regmap,
  145. AO_RTI_PWR_CNTL_REG0,
  146. PWR_CNTL_ALT_32K_SEL,
  147. FIELD_PREP(PWR_CNTL_ALT_32K_SEL, 4));
  148. return 0;
  149. }
  150. const struct clk_ops meson_aoclk_cec_32k_ops = {
  151. .recalc_rate = aoclk_cec_32k_recalc_rate,
  152. .round_rate = aoclk_cec_32k_round_rate,
  153. .set_rate = aoclk_cec_32k_set_rate,
  154. };