clk-audio-divider.c 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Copyright (c) 2017 AmLogic, Inc.
  4. * Author: Jerome Brunet <jbrunet@baylibre.com>
  5. */
  6. /*
  7. * i2s master clock divider: The algorithm of the generic clk-divider used with
  8. * a very precise clock parent such as the mpll tends to select a low divider
  9. * factor. This gives poor results with this particular divider, especially with
  10. * high frequencies (> 100 MHz)
  11. *
  12. * This driver try to select the maximum possible divider with the rate the
  13. * upstream clock can provide.
  14. */
  15. #include <linux/clk-provider.h>
  16. #include "clkc.h"
  17. static inline struct meson_clk_audio_div_data *
  18. meson_clk_audio_div_data(struct clk_regmap *clk)
  19. {
  20. return (struct meson_clk_audio_div_data *)clk->data;
  21. }
  22. static int _div_round(unsigned long parent_rate, unsigned long rate,
  23. unsigned long flags)
  24. {
  25. if (flags & CLK_DIVIDER_ROUND_CLOSEST)
  26. return DIV_ROUND_CLOSEST_ULL((u64)parent_rate, rate);
  27. return DIV_ROUND_UP_ULL((u64)parent_rate, rate);
  28. }
  29. static int _get_val(unsigned long parent_rate, unsigned long rate)
  30. {
  31. return DIV_ROUND_UP_ULL((u64)parent_rate, rate) - 1;
  32. }
  33. static int _valid_divider(unsigned int width, int divider)
  34. {
  35. int max_divider = 1 << width;
  36. return clamp(divider, 1, max_divider);
  37. }
  38. static unsigned long audio_divider_recalc_rate(struct clk_hw *hw,
  39. unsigned long parent_rate)
  40. {
  41. struct clk_regmap *clk = to_clk_regmap(hw);
  42. struct meson_clk_audio_div_data *adiv = meson_clk_audio_div_data(clk);
  43. unsigned long divider;
  44. divider = meson_parm_read(clk->map, &adiv->div);
  45. return DIV_ROUND_UP_ULL((u64)parent_rate, divider);
  46. }
  47. static long audio_divider_round_rate(struct clk_hw *hw,
  48. unsigned long rate,
  49. unsigned long *parent_rate)
  50. {
  51. struct clk_regmap *clk = to_clk_regmap(hw);
  52. struct meson_clk_audio_div_data *adiv = meson_clk_audio_div_data(clk);
  53. unsigned long max_prate;
  54. int divider;
  55. if (!(clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT)) {
  56. divider = _div_round(*parent_rate, rate, adiv->flags);
  57. divider = _valid_divider(adiv->div.width, divider);
  58. return DIV_ROUND_UP_ULL((u64)*parent_rate, divider);
  59. }
  60. /* Get the maximum parent rate */
  61. max_prate = clk_hw_round_rate(clk_hw_get_parent(hw), ULONG_MAX);
  62. /* Get the corresponding rounded down divider */
  63. divider = max_prate / rate;
  64. divider = _valid_divider(adiv->div.width, divider);
  65. /* Get actual rate of the parent */
  66. *parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw),
  67. divider * rate);
  68. return DIV_ROUND_UP_ULL((u64)*parent_rate, divider);
  69. }
  70. static int audio_divider_set_rate(struct clk_hw *hw,
  71. unsigned long rate,
  72. unsigned long parent_rate)
  73. {
  74. struct clk_regmap *clk = to_clk_regmap(hw);
  75. struct meson_clk_audio_div_data *adiv = meson_clk_audio_div_data(clk);
  76. int val = _get_val(parent_rate, rate);
  77. meson_parm_write(clk->map, &adiv->div, val);
  78. return 0;
  79. }
  80. const struct clk_ops meson_clk_audio_divider_ro_ops = {
  81. .recalc_rate = audio_divider_recalc_rate,
  82. .round_rate = audio_divider_round_rate,
  83. };
  84. const struct clk_ops meson_clk_audio_divider_ops = {
  85. .recalc_rate = audio_divider_recalc_rate,
  86. .round_rate = audio_divider_round_rate,
  87. .set_rate = audio_divider_set_rate,
  88. };