clk-tegra-super-cclk.c 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  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 <[email protected]>
  7. * Copyright (C) 2010 Google, Inc.
  8. *
  9. * Author: Dmitry Osipenko <[email protected]>
  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. rate = tegra_clk_super_ops.round_rate(hw, rate,
  74. &pllp_rate);
  75. req->best_parent_rate = pllp_rate;
  76. req->best_parent_hw = pllp_hw;
  77. req->rate = rate;
  78. } else {
  79. rate = clk_hw_round_rate(pllx_hw, rate);
  80. req->best_parent_rate = rate;
  81. req->best_parent_hw = pllx_hw;
  82. req->rate = rate;
  83. }
  84. if (WARN_ON_ONCE(rate <= 0))
  85. return -EINVAL;
  86. return 0;
  87. }
  88. static const struct clk_ops tegra_cclk_super_ops = {
  89. .get_parent = cclk_super_get_parent,
  90. .set_parent = cclk_super_set_parent,
  91. .set_rate = cclk_super_set_rate,
  92. .recalc_rate = cclk_super_recalc_rate,
  93. .determine_rate = cclk_super_determine_rate,
  94. };
  95. static const struct clk_ops tegra_cclk_super_mux_ops = {
  96. .get_parent = cclk_super_get_parent,
  97. .set_parent = cclk_super_set_parent,
  98. .determine_rate = cclk_super_determine_rate,
  99. };
  100. struct clk *tegra_clk_register_super_cclk(const char *name,
  101. const char * const *parent_names, u8 num_parents,
  102. unsigned long flags, void __iomem *reg, u8 clk_super_flags,
  103. spinlock_t *lock)
  104. {
  105. struct tegra_clk_super_mux *super;
  106. struct clk *clk;
  107. struct clk_init_data init;
  108. u32 val;
  109. if (WARN_ON(cclk_super))
  110. return ERR_PTR(-EBUSY);
  111. super = kzalloc(sizeof(*super), GFP_KERNEL);
  112. if (!super)
  113. return ERR_PTR(-ENOMEM);
  114. init.name = name;
  115. init.flags = flags;
  116. init.parent_names = parent_names;
  117. init.num_parents = num_parents;
  118. super->reg = reg;
  119. super->lock = lock;
  120. super->width = 4;
  121. super->flags = clk_super_flags;
  122. super->hw.init = &init;
  123. if (super->flags & TEGRA20_SUPER_CLK) {
  124. init.ops = &tegra_cclk_super_mux_ops;
  125. } else {
  126. init.ops = &tegra_cclk_super_ops;
  127. super->frac_div.reg = reg + 4;
  128. super->frac_div.shift = 16;
  129. super->frac_div.width = 8;
  130. super->frac_div.frac_width = 1;
  131. super->frac_div.lock = lock;
  132. super->div_ops = &tegra_clk_frac_div_ops;
  133. }
  134. /*
  135. * Tegra30+ has the following CPUG clock topology:
  136. *
  137. * +---+ +-------+ +-+ +-+ +-+
  138. * PLLP+->+ +->+DIVIDER+->+0| +-------->+0| ------------->+0|
  139. * | | +-------+ | | | +---+ | | | | |
  140. * PLLC+->+MUX| | +->+ | S | | +->+ | +->+CPU
  141. * ... | | | | | | K | | | | +-------+ | |
  142. * PLLX+->+-->+------------>+1| +->+ I +->+1| +->+ DIV2 +->+1|
  143. * +---+ +++ | P | +++ |SKIPPER| +++
  144. * ^ | P | ^ +-------+ ^
  145. * | | E | | |
  146. * PLLX_SEL+--+ | R | | OVERHEAT+--+
  147. * +---+ |
  148. * |
  149. * SUPER_CDIV_ENB+--+
  150. *
  151. * Tegra20 is similar, but simpler. It doesn't have the divider and
  152. * thermal DIV2 skipper.
  153. *
  154. * At least for now we're not going to use clock-skipper, hence let's
  155. * ensure that it is disabled.
  156. */
  157. val = readl_relaxed(reg + 4);
  158. val &= ~SUPER_CDIV_ENB;
  159. writel_relaxed(val, reg + 4);
  160. clk = clk_register(NULL, &super->hw);
  161. if (IS_ERR(clk))
  162. kfree(super);
  163. else
  164. cclk_super = super;
  165. return clk;
  166. }
  167. int tegra_cclk_pre_pllx_rate_change(void)
  168. {
  169. if (IS_ERR_OR_NULL(cclk_super))
  170. return -EINVAL;
  171. if (cclk_super_get_parent(&cclk_super->hw) == PLLX_INDEX)
  172. cclk_on_pllx = true;
  173. else
  174. cclk_on_pllx = false;
  175. /*
  176. * CPU needs to be temporarily re-parented away from PLLX if PLLX
  177. * changes its rate. PLLP is a safe parent for CPU on all Tegra SoCs.
  178. */
  179. if (cclk_on_pllx)
  180. cclk_super_set_parent(&cclk_super->hw, PLLP_INDEX);
  181. return 0;
  182. }
  183. void tegra_cclk_post_pllx_rate_change(void)
  184. {
  185. if (cclk_on_pllx)
  186. cclk_super_set_parent(&cclk_super->hw, PLLX_INDEX);
  187. }