clk-phase.c 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. // SPDX-License-Identifier: (GPL-2.0 OR MIT)
  2. /*
  3. * Copyright (c) 2018 BayLibre, SAS.
  4. * Author: Jerome Brunet <[email protected]>
  5. */
  6. #include <linux/clk-provider.h>
  7. #include <linux/module.h>
  8. #include "clk-regmap.h"
  9. #include "clk-phase.h"
  10. #define phase_step(_width) (360 / (1 << (_width)))
  11. static inline struct meson_clk_phase_data *
  12. meson_clk_phase_data(struct clk_regmap *clk)
  13. {
  14. return (struct meson_clk_phase_data *)clk->data;
  15. }
  16. static int meson_clk_degrees_from_val(unsigned int val, unsigned int width)
  17. {
  18. return phase_step(width) * val;
  19. }
  20. static unsigned int meson_clk_degrees_to_val(int degrees, unsigned int width)
  21. {
  22. unsigned int val = DIV_ROUND_CLOSEST(degrees, phase_step(width));
  23. /*
  24. * This last calculation is here for cases when degrees is rounded
  25. * to 360, in which case val == (1 << width).
  26. */
  27. return val % (1 << width);
  28. }
  29. static int meson_clk_phase_get_phase(struct clk_hw *hw)
  30. {
  31. struct clk_regmap *clk = to_clk_regmap(hw);
  32. struct meson_clk_phase_data *phase = meson_clk_phase_data(clk);
  33. unsigned int val;
  34. val = meson_parm_read(clk->map, &phase->ph);
  35. return meson_clk_degrees_from_val(val, phase->ph.width);
  36. }
  37. static int meson_clk_phase_set_phase(struct clk_hw *hw, int degrees)
  38. {
  39. struct clk_regmap *clk = to_clk_regmap(hw);
  40. struct meson_clk_phase_data *phase = meson_clk_phase_data(clk);
  41. unsigned int val;
  42. val = meson_clk_degrees_to_val(degrees, phase->ph.width);
  43. meson_parm_write(clk->map, &phase->ph, val);
  44. return 0;
  45. }
  46. const struct clk_ops meson_clk_phase_ops = {
  47. .get_phase = meson_clk_phase_get_phase,
  48. .set_phase = meson_clk_phase_set_phase,
  49. };
  50. EXPORT_SYMBOL_GPL(meson_clk_phase_ops);
  51. /*
  52. * This is a special clock for the audio controller.
  53. * The phase of mst_sclk clock output can be controlled independently
  54. * for the outside world (ph0), the tdmout (ph1) and tdmin (ph2).
  55. * Controlling these 3 phases as just one makes things simpler and
  56. * give the same clock view to all the element on the i2s bus.
  57. * If necessary, we can still control the phase in the tdm block
  58. * which makes these independent control redundant.
  59. */
  60. static inline struct meson_clk_triphase_data *
  61. meson_clk_triphase_data(struct clk_regmap *clk)
  62. {
  63. return (struct meson_clk_triphase_data *)clk->data;
  64. }
  65. static int meson_clk_triphase_sync(struct clk_hw *hw)
  66. {
  67. struct clk_regmap *clk = to_clk_regmap(hw);
  68. struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);
  69. unsigned int val;
  70. /* Get phase 0 and sync it to phase 1 and 2 */
  71. val = meson_parm_read(clk->map, &tph->ph0);
  72. meson_parm_write(clk->map, &tph->ph1, val);
  73. meson_parm_write(clk->map, &tph->ph2, val);
  74. return 0;
  75. }
  76. static int meson_clk_triphase_get_phase(struct clk_hw *hw)
  77. {
  78. struct clk_regmap *clk = to_clk_regmap(hw);
  79. struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);
  80. unsigned int val;
  81. /* Phase are in sync, reading phase 0 is enough */
  82. val = meson_parm_read(clk->map, &tph->ph0);
  83. return meson_clk_degrees_from_val(val, tph->ph0.width);
  84. }
  85. static int meson_clk_triphase_set_phase(struct clk_hw *hw, int degrees)
  86. {
  87. struct clk_regmap *clk = to_clk_regmap(hw);
  88. struct meson_clk_triphase_data *tph = meson_clk_triphase_data(clk);
  89. unsigned int val;
  90. val = meson_clk_degrees_to_val(degrees, tph->ph0.width);
  91. meson_parm_write(clk->map, &tph->ph0, val);
  92. meson_parm_write(clk->map, &tph->ph1, val);
  93. meson_parm_write(clk->map, &tph->ph2, val);
  94. return 0;
  95. }
  96. const struct clk_ops meson_clk_triphase_ops = {
  97. .init = meson_clk_triphase_sync,
  98. .get_phase = meson_clk_triphase_get_phase,
  99. .set_phase = meson_clk_triphase_set_phase,
  100. };
  101. EXPORT_SYMBOL_GPL(meson_clk_triphase_ops);
  102. /*
  103. * This is a special clock for the audio controller.
  104. * This drive a bit clock inverter for which the
  105. * opposite value of the inverter bit needs to be manually
  106. * set into another bit
  107. */
  108. static inline struct meson_sclk_ws_inv_data *
  109. meson_sclk_ws_inv_data(struct clk_regmap *clk)
  110. {
  111. return (struct meson_sclk_ws_inv_data *)clk->data;
  112. }
  113. static int meson_sclk_ws_inv_sync(struct clk_hw *hw)
  114. {
  115. struct clk_regmap *clk = to_clk_regmap(hw);
  116. struct meson_sclk_ws_inv_data *tph = meson_sclk_ws_inv_data(clk);
  117. unsigned int val;
  118. /* Get phase and sync the inverted value to ws */
  119. val = meson_parm_read(clk->map, &tph->ph);
  120. meson_parm_write(clk->map, &tph->ws, val ? 0 : 1);
  121. return 0;
  122. }
  123. static int meson_sclk_ws_inv_get_phase(struct clk_hw *hw)
  124. {
  125. struct clk_regmap *clk = to_clk_regmap(hw);
  126. struct meson_sclk_ws_inv_data *tph = meson_sclk_ws_inv_data(clk);
  127. unsigned int val;
  128. val = meson_parm_read(clk->map, &tph->ph);
  129. return meson_clk_degrees_from_val(val, tph->ph.width);
  130. }
  131. static int meson_sclk_ws_inv_set_phase(struct clk_hw *hw, int degrees)
  132. {
  133. struct clk_regmap *clk = to_clk_regmap(hw);
  134. struct meson_sclk_ws_inv_data *tph = meson_sclk_ws_inv_data(clk);
  135. unsigned int val;
  136. val = meson_clk_degrees_to_val(degrees, tph->ph.width);
  137. meson_parm_write(clk->map, &tph->ph, val);
  138. meson_parm_write(clk->map, &tph->ws, val ? 0 : 1);
  139. return 0;
  140. }
  141. const struct clk_ops meson_sclk_ws_inv_ops = {
  142. .init = meson_sclk_ws_inv_sync,
  143. .get_phase = meson_sclk_ws_inv_get_phase,
  144. .set_phase = meson_sclk_ws_inv_set_phase,
  145. };
  146. EXPORT_SYMBOL_GPL(meson_sclk_ws_inv_ops);
  147. MODULE_DESCRIPTION("Amlogic phase driver");
  148. MODULE_AUTHOR("Jerome Brunet <[email protected]>");
  149. MODULE_LICENSE("GPL v2");