clk-busy.c 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * Copyright 2012 Freescale Semiconductor, Inc.
  4. * Copyright 2012 Linaro Ltd.
  5. */
  6. #include <linux/bits.h>
  7. #include <linux/clk.h>
  8. #include <linux/clk-provider.h>
  9. #include <linux/io.h>
  10. #include <linux/slab.h>
  11. #include <linux/jiffies.h>
  12. #include <linux/err.h>
  13. #include "clk.h"
  14. static int clk_busy_wait(void __iomem *reg, u8 shift)
  15. {
  16. unsigned long timeout = jiffies + msecs_to_jiffies(10);
  17. while (readl_relaxed(reg) & (1 << shift))
  18. if (time_after(jiffies, timeout))
  19. return -ETIMEDOUT;
  20. return 0;
  21. }
  22. struct clk_busy_divider {
  23. struct clk_divider div;
  24. const struct clk_ops *div_ops;
  25. void __iomem *reg;
  26. u8 shift;
  27. };
  28. static inline struct clk_busy_divider *to_clk_busy_divider(struct clk_hw *hw)
  29. {
  30. struct clk_divider *div = to_clk_divider(hw);
  31. return container_of(div, struct clk_busy_divider, div);
  32. }
  33. static unsigned long clk_busy_divider_recalc_rate(struct clk_hw *hw,
  34. unsigned long parent_rate)
  35. {
  36. struct clk_busy_divider *busy = to_clk_busy_divider(hw);
  37. return busy->div_ops->recalc_rate(&busy->div.hw, parent_rate);
  38. }
  39. static long clk_busy_divider_round_rate(struct clk_hw *hw, unsigned long rate,
  40. unsigned long *prate)
  41. {
  42. struct clk_busy_divider *busy = to_clk_busy_divider(hw);
  43. return busy->div_ops->round_rate(&busy->div.hw, rate, prate);
  44. }
  45. static int clk_busy_divider_set_rate(struct clk_hw *hw, unsigned long rate,
  46. unsigned long parent_rate)
  47. {
  48. struct clk_busy_divider *busy = to_clk_busy_divider(hw);
  49. int ret;
  50. ret = busy->div_ops->set_rate(&busy->div.hw, rate, parent_rate);
  51. if (!ret)
  52. ret = clk_busy_wait(busy->reg, busy->shift);
  53. return ret;
  54. }
  55. static const struct clk_ops clk_busy_divider_ops = {
  56. .recalc_rate = clk_busy_divider_recalc_rate,
  57. .round_rate = clk_busy_divider_round_rate,
  58. .set_rate = clk_busy_divider_set_rate,
  59. };
  60. struct clk_hw *imx_clk_hw_busy_divider(const char *name, const char *parent_name,
  61. void __iomem *reg, u8 shift, u8 width,
  62. void __iomem *busy_reg, u8 busy_shift)
  63. {
  64. struct clk_busy_divider *busy;
  65. struct clk_hw *hw;
  66. struct clk_init_data init;
  67. int ret;
  68. busy = kzalloc(sizeof(*busy), GFP_KERNEL);
  69. if (!busy)
  70. return ERR_PTR(-ENOMEM);
  71. busy->reg = busy_reg;
  72. busy->shift = busy_shift;
  73. busy->div.reg = reg;
  74. busy->div.shift = shift;
  75. busy->div.width = width;
  76. busy->div.lock = &imx_ccm_lock;
  77. busy->div_ops = &clk_divider_ops;
  78. init.name = name;
  79. init.ops = &clk_busy_divider_ops;
  80. init.flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL;
  81. init.parent_names = &parent_name;
  82. init.num_parents = 1;
  83. busy->div.hw.init = &init;
  84. hw = &busy->div.hw;
  85. ret = clk_hw_register(NULL, hw);
  86. if (ret) {
  87. kfree(busy);
  88. return ERR_PTR(ret);
  89. }
  90. return hw;
  91. }
  92. struct clk_busy_mux {
  93. struct clk_mux mux;
  94. const struct clk_ops *mux_ops;
  95. void __iomem *reg;
  96. u8 shift;
  97. };
  98. static inline struct clk_busy_mux *to_clk_busy_mux(struct clk_hw *hw)
  99. {
  100. struct clk_mux *mux = to_clk_mux(hw);
  101. return container_of(mux, struct clk_busy_mux, mux);
  102. }
  103. static u8 clk_busy_mux_get_parent(struct clk_hw *hw)
  104. {
  105. struct clk_busy_mux *busy = to_clk_busy_mux(hw);
  106. return busy->mux_ops->get_parent(&busy->mux.hw);
  107. }
  108. static int clk_busy_mux_set_parent(struct clk_hw *hw, u8 index)
  109. {
  110. struct clk_busy_mux *busy = to_clk_busy_mux(hw);
  111. int ret;
  112. ret = busy->mux_ops->set_parent(&busy->mux.hw, index);
  113. if (!ret)
  114. ret = clk_busy_wait(busy->reg, busy->shift);
  115. return ret;
  116. }
  117. static const struct clk_ops clk_busy_mux_ops = {
  118. .get_parent = clk_busy_mux_get_parent,
  119. .set_parent = clk_busy_mux_set_parent,
  120. };
  121. struct clk_hw *imx_clk_hw_busy_mux(const char *name, void __iomem *reg, u8 shift,
  122. u8 width, void __iomem *busy_reg, u8 busy_shift,
  123. const char * const *parent_names, int num_parents)
  124. {
  125. struct clk_busy_mux *busy;
  126. struct clk_hw *hw;
  127. struct clk_init_data init;
  128. int ret;
  129. busy = kzalloc(sizeof(*busy), GFP_KERNEL);
  130. if (!busy)
  131. return ERR_PTR(-ENOMEM);
  132. busy->reg = busy_reg;
  133. busy->shift = busy_shift;
  134. busy->mux.reg = reg;
  135. busy->mux.shift = shift;
  136. busy->mux.mask = BIT(width) - 1;
  137. busy->mux.lock = &imx_ccm_lock;
  138. busy->mux_ops = &clk_mux_ops;
  139. init.name = name;
  140. init.ops = &clk_busy_mux_ops;
  141. init.flags = CLK_IS_CRITICAL;
  142. init.parent_names = parent_names;
  143. init.num_parents = num_parents;
  144. busy->mux.hw.init = &init;
  145. hw = &busy->mux.hw;
  146. ret = clk_hw_register(NULL, hw);
  147. if (ret) {
  148. kfree(busy);
  149. return ERR_PTR(ret);
  150. }
  151. return hw;
  152. }