clk-tegra-super-cclk.c 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Based on clk-super.c
  4. * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
  5. *
  6. * Based on older tegra20-cpufreq driver by Colin Cross <ccross@google.com>
  7. * Copyright (C) 2010 Google, Inc.
  8. *
  9. * Author: Dmitry Osipenko <digetx@gmail.com>
  10. * Copyright (C) 2019 GRATE-DRIVER project
  11. */
  12. #include <linux/bits.h>
  13. #include <linux/clk-provider.h>
  14. #include <linux/err.h>
  15. #include <linux/io.h>
  16. #include <linux/kernel.h>
  17. #include <linux/slab.h>
  18. #include <linux/types.h>
  19. #include "clk.h"
  20. #define PLLP_INDEX 4
  21. #define PLLX_INDEX 8
  22. #define SUPER_CDIV_ENB BIT(31)
  23. #define TSENSOR_SLOWDOWN BIT(23)
  24. static struct tegra_clk_super_mux *cclk_super;
  25. static bool cclk_on_pllx;
  26. static u8 cclk_super_get_parent(struct clk_hw *hw)
  27. {
  28. return tegra_clk_super_ops.get_parent(hw);
  29. }
  30. static int cclk_super_set_parent(struct clk_hw *hw, u8 index)
  31. {
  32. return tegra_clk_super_ops.set_parent(hw, index);
  33. }
  34. static int cclk_super_set_rate(struct clk_hw *hw, unsigned long rate,
  35. unsigned long parent_rate)
  36. {
  37. return tegra_clk_super_ops.set_rate(hw, rate, parent_rate);
  38. }
  39. static unsigned long cclk_super_recalc_rate(struct clk_hw *hw,
  40. unsigned long parent_rate)
  41. {
  42. struct tegra_clk_super_mux *super = to_clk_super_mux(hw);
  43. u32 val = readl_relaxed(super->reg);
  44. unsigned int div2;
  45. /* check whether thermal throttling is active */
  46. if (val & TSENSOR_SLOWDOWN)
  47. div2 = 1;
  48. else
  49. div2 = 0;
  50. if (cclk_super_get_parent(hw) == PLLX_INDEX)
  51. return parent_rate >> div2;
  52. return tegra_clk_super_ops.recalc_rate(hw, parent_rate) >> div2;
  53. }
  54. static int cclk_super_determine_rate(struct clk_hw *hw,
  55. struct clk_rate_request *req)
  56. {
  57. struct clk_hw *pllp_hw = clk_hw_get_parent_by_index(hw, PLLP_INDEX);
  58. struct clk_hw *pllx_hw = clk_hw_get_parent_by_index(hw, PLLX_INDEX);
  59. struct tegra_clk_super_mux *super = to_clk_super_mux(hw);
  60. unsigned long pllp_rate;
  61. long rate = req->rate;
  62. if (WARN_ON_ONCE(!pllp_hw || !pllx_hw))
  63. return -EINVAL;
  64. /*
  65. * Switch parent to PLLP for all CCLK rates that are suitable for PLLP.
  66. * PLLX will be disabled in this case, saving some power.
  67. */
  68. pllp_rate = clk_hw_get_rate(pllp_hw);
  69. if (rate <= pllp_rate) {
  70. if (super->flags & TEGRA20_SUPER_CLK)
  71. rate = pllp_rate;
  72. else {
  73. struct clk_rate_request parent = {
  74. .rate = req->rate,
  75. .best_parent_rate = pllp_rate,
  76. };
  77. clk_hw_get_rate_range(hw, &parent.min_rate,
  78. &parent.max_rate);
  79. tegra_clk_super_ops.determine_rate(hw, &parent);
  80. pllp_rate = parent.best_parent_rate;
  81. rate = parent.rate;
  82. }
  83. req->best_parent_rate = pllp_rate;
  84. req->best_parent_hw = pllp_hw;
  85. req->rate = rate;
  86. } else {
  87. rate = clk_hw_round_rate(pllx_hw, rate);
  88. req->best_parent_rate = rate;
  89. req->best_parent_hw = pllx_hw;
  90. req->rate = rate;
  91. }
  92. if (WARN_ON_ONCE(rate <= 0))
  93. return -EINVAL;
  94. return 0;
  95. }
  96. static const struct clk_ops tegra_cclk_super_ops = {
  97. .get_parent = cclk_super_get_parent,
  98. .set_parent = cclk_super_set_parent,
  99. .set_rate = cclk_super_set_rate,
  100. .recalc_rate = cclk_super_recalc_rate,
  101. .determine_rate = cclk_super_determine_rate,
  102. };
  103. static const struct clk_ops tegra_cclk_super_mux_ops = {
  104. .get_parent = cclk_super_get_parent,
  105. .set_parent = cclk_super_set_parent,
  106. .determine_rate = cclk_super_determine_rate,
  107. };
  108. struct clk *tegra_clk_register_super_cclk(const char *name,
  109. const char * const *parent_names, u8 num_parents,
  110. unsigned long flags, void __iomem *reg, u8 clk_super_flags,
  111. spinlock_t *lock)
  112. {
  113. struct tegra_clk_super_mux *super;
  114. struct clk *clk;
  115. struct clk_init_data init;
  116. u32 val;
  117. if (WARN_ON(cclk_super))
  118. return ERR_PTR(-EBUSY);
  119. super = kzalloc(sizeof(*super), GFP_KERNEL);
  120. if (!super)
  121. return ERR_PTR(-ENOMEM);
  122. init.name = name;
  123. init.flags = flags;
  124. init.parent_names = parent_names;
  125. init.num_parents = num_parents;
  126. super->reg = reg;
  127. super->lock = lock;
  128. super->width = 4;
  129. super->flags = clk_super_flags;
  130. super->hw.init = &init;
  131. if (super->flags & TEGRA20_SUPER_CLK) {
  132. init.ops = &tegra_cclk_super_mux_ops;
  133. } else {
  134. init.ops = &tegra_cclk_super_ops;
  135. super->frac_div.reg = reg + 4;
  136. super->frac_div.shift = 16;
  137. super->frac_div.width = 8;
  138. super->frac_div.frac_width = 1;
  139. super->frac_div.lock = lock;
  140. super->div_ops = &tegra_clk_frac_div_ops;
  141. }
  142. /*
  143. * Tegra30+ has the following CPUG clock topology:
  144. *
  145. * +---+ +-------+ +-+ +-+ +-+
  146. * PLLP+->+ +->+DIVIDER+->+0| +-------->+0| ------------->+0|
  147. * | | +-------+ | | | +---+ | | | | |
  148. * PLLC+->+MUX| | +->+ | S | | +->+ | +->+CPU
  149. * ... | | | | | | K | | | | +-------+ | |
  150. * PLLX+->+-->+------------>+1| +->+ I +->+1| +->+ DIV2 +->+1|
  151. * +---+ +++ | P | +++ |SKIPPER| +++
  152. * ^ | P | ^ +-------+ ^
  153. * | | E | | |
  154. * PLLX_SEL+--+ | R | | OVERHEAT+--+
  155. * +---+ |
  156. * |
  157. * SUPER_CDIV_ENB+--+
  158. *
  159. * Tegra20 is similar, but simpler. It doesn't have the divider and
  160. * thermal DIV2 skipper.
  161. *
  162. * At least for now we're not going to use clock-skipper, hence let's
  163. * ensure that it is disabled.
  164. */
  165. val = readl_relaxed(reg + 4);
  166. val &= ~SUPER_CDIV_ENB;
  167. writel_relaxed(val, reg + 4);
  168. clk = clk_register(NULL, &super->hw);
  169. if (IS_ERR(clk))
  170. kfree(super);
  171. else
  172. cclk_super = super;
  173. return clk;
  174. }
  175. int tegra_cclk_pre_pllx_rate_change(void)
  176. {
  177. if (IS_ERR_OR_NULL(cclk_super))
  178. return -EINVAL;
  179. if (cclk_super_get_parent(&cclk_super->hw) == PLLX_INDEX)
  180. cclk_on_pllx = true;
  181. else
  182. cclk_on_pllx = false;
  183. /*
  184. * CPU needs to be temporarily re-parented away from PLLX if PLLX
  185. * changes its rate. PLLP is a safe parent for CPU on all Tegra SoCs.
  186. */
  187. if (cclk_on_pllx)
  188. cclk_super_set_parent(&cclk_super->hw, PLLP_INDEX);
  189. return 0;
  190. }
  191. void tegra_cclk_post_pllx_rate_change(void)
  192. {
  193. if (cclk_on_pllx)
  194. cclk_super_set_parent(&cclk_super->hw, PLLX_INDEX);
  195. }