clk-tegra-super-cclk.c 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  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. static struct tegra_clk_super_mux *cclk_super;
  24. static bool cclk_on_pllx;
  25. static u8 cclk_super_get_parent(struct clk_hw *hw)
  26. {
  27. return tegra_clk_super_ops.get_parent(hw);
  28. }
  29. static int cclk_super_set_parent(struct clk_hw *hw, u8 index)
  30. {
  31. return tegra_clk_super_ops.set_parent(hw, index);
  32. }
  33. static int cclk_super_set_rate(struct clk_hw *hw, unsigned long rate,
  34. unsigned long parent_rate)
  35. {
  36. return tegra_clk_super_ops.set_rate(hw, rate, parent_rate);
  37. }
  38. static unsigned long cclk_super_recalc_rate(struct clk_hw *hw,
  39. unsigned long parent_rate)
  40. {
  41. if (cclk_super_get_parent(hw) == PLLX_INDEX)
  42. return parent_rate;
  43. return tegra_clk_super_ops.recalc_rate(hw, parent_rate);
  44. }
  45. static int cclk_super_determine_rate(struct clk_hw *hw,
  46. struct clk_rate_request *req)
  47. {
  48. struct clk_hw *pllp_hw = clk_hw_get_parent_by_index(hw, PLLP_INDEX);
  49. struct clk_hw *pllx_hw = clk_hw_get_parent_by_index(hw, PLLX_INDEX);
  50. struct tegra_clk_super_mux *super = to_clk_super_mux(hw);
  51. unsigned long pllp_rate;
  52. long rate = req->rate;
  53. if (WARN_ON_ONCE(!pllp_hw || !pllx_hw))
  54. return -EINVAL;
  55. /*
  56. * Switch parent to PLLP for all CCLK rates that are suitable for PLLP.
  57. * PLLX will be disabled in this case, saving some power.
  58. */
  59. pllp_rate = clk_hw_get_rate(pllp_hw);
  60. if (rate <= pllp_rate) {
  61. if (super->flags & TEGRA20_SUPER_CLK)
  62. rate = pllp_rate;
  63. else
  64. rate = tegra_clk_super_ops.round_rate(hw, rate,
  65. &pllp_rate);
  66. req->best_parent_rate = pllp_rate;
  67. req->best_parent_hw = pllp_hw;
  68. req->rate = rate;
  69. } else {
  70. rate = clk_hw_round_rate(pllx_hw, rate);
  71. req->best_parent_rate = rate;
  72. req->best_parent_hw = pllx_hw;
  73. req->rate = rate;
  74. }
  75. if (WARN_ON_ONCE(rate <= 0))
  76. return -EINVAL;
  77. return 0;
  78. }
  79. static const struct clk_ops tegra_cclk_super_ops = {
  80. .get_parent = cclk_super_get_parent,
  81. .set_parent = cclk_super_set_parent,
  82. .set_rate = cclk_super_set_rate,
  83. .recalc_rate = cclk_super_recalc_rate,
  84. .determine_rate = cclk_super_determine_rate,
  85. };
  86. static const struct clk_ops tegra_cclk_super_mux_ops = {
  87. .get_parent = cclk_super_get_parent,
  88. .set_parent = cclk_super_set_parent,
  89. .determine_rate = cclk_super_determine_rate,
  90. };
  91. struct clk *tegra_clk_register_super_cclk(const char *name,
  92. const char * const *parent_names, u8 num_parents,
  93. unsigned long flags, void __iomem *reg, u8 clk_super_flags,
  94. spinlock_t *lock)
  95. {
  96. struct tegra_clk_super_mux *super;
  97. struct clk *clk;
  98. struct clk_init_data init;
  99. u32 val;
  100. if (WARN_ON(cclk_super))
  101. return ERR_PTR(-EBUSY);
  102. super = kzalloc(sizeof(*super), GFP_KERNEL);
  103. if (!super)
  104. return ERR_PTR(-ENOMEM);
  105. init.name = name;
  106. init.flags = flags;
  107. init.parent_names = parent_names;
  108. init.num_parents = num_parents;
  109. super->reg = reg;
  110. super->lock = lock;
  111. super->width = 4;
  112. super->flags = clk_super_flags;
  113. super->hw.init = &init;
  114. if (super->flags & TEGRA20_SUPER_CLK) {
  115. init.ops = &tegra_cclk_super_mux_ops;
  116. } else {
  117. init.ops = &tegra_cclk_super_ops;
  118. super->frac_div.reg = reg + 4;
  119. super->frac_div.shift = 16;
  120. super->frac_div.width = 8;
  121. super->frac_div.frac_width = 1;
  122. super->frac_div.lock = lock;
  123. super->div_ops = &tegra_clk_frac_div_ops;
  124. }
  125. /*
  126. * Tegra30+ has the following CPUG clock topology:
  127. *
  128. * +---+ +-------+ +-+ +-+ +-+
  129. * PLLP+->+ +->+DIVIDER+->+0| +-------->+0| ------------->+0|
  130. * | | +-------+ | | | +---+ | | | | |
  131. * PLLC+->+MUX| | +->+ | S | | +->+ | +->+CPU
  132. * ... | | | | | | K | | | | +-------+ | |
  133. * PLLX+->+-->+------------>+1| +->+ I +->+1| +->+ DIV2 +->+1|
  134. * +---+ +++ | P | +++ |SKIPPER| +++
  135. * ^ | P | ^ +-------+ ^
  136. * | | E | | |
  137. * PLLX_SEL+--+ | R | | OVERHEAT+--+
  138. * +---+ |
  139. * |
  140. * SUPER_CDIV_ENB+--+
  141. *
  142. * Tegra20 is similar, but simpler. It doesn't have the divider and
  143. * thermal DIV2 skipper.
  144. *
  145. * At least for now we're not going to use clock-skipper, hence let's
  146. * ensure that it is disabled.
  147. */
  148. val = readl_relaxed(reg + 4);
  149. val &= ~SUPER_CDIV_ENB;
  150. writel_relaxed(val, reg + 4);
  151. clk = clk_register(NULL, &super->hw);
  152. if (IS_ERR(clk))
  153. kfree(super);
  154. else
  155. cclk_super = super;
  156. return clk;
  157. }
  158. int tegra_cclk_pre_pllx_rate_change(void)
  159. {
  160. if (IS_ERR_OR_NULL(cclk_super))
  161. return -EINVAL;
  162. if (cclk_super_get_parent(&cclk_super->hw) == PLLX_INDEX)
  163. cclk_on_pllx = true;
  164. else
  165. cclk_on_pllx = false;
  166. /*
  167. * CPU needs to be temporarily re-parented away from PLLX if PLLX
  168. * changes its rate. PLLP is a safe parent for CPU on all Tegra SoCs.
  169. */
  170. if (cclk_on_pllx)
  171. cclk_super_set_parent(&cclk_super->hw, PLLP_INDEX);
  172. return 0;
  173. }
  174. void tegra_cclk_post_pllx_rate_change(void)
  175. {
  176. if (cclk_on_pllx)
  177. cclk_super_set_parent(&cclk_super->hw, PLLX_INDEX);
  178. }