gxbb-aoclk-32k.c 5.2 KB

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