clk-composite-7ulp.c 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * Copyright (C) 2016 Freescale Semiconductor, Inc.
  4. * Copyright 2017~2018 NXP
  5. *
  6. */
  7. #include <linux/bits.h>
  8. #include <linux/clk-provider.h>
  9. #include <linux/delay.h>
  10. #include <linux/err.h>
  11. #include <linux/io.h>
  12. #include <linux/slab.h>
  13. #include "../clk-fractional-divider.h"
  14. #include "clk.h"
  15. #define PCG_PR_MASK BIT(31)
  16. #define PCG_PCS_SHIFT 24
  17. #define PCG_PCS_MASK 0x7
  18. #define PCG_CGC_SHIFT 30
  19. #define PCG_FRAC_SHIFT 3
  20. #define PCG_FRAC_WIDTH 1
  21. #define PCG_PCD_SHIFT 0
  22. #define PCG_PCD_WIDTH 3
  23. #define SW_RST BIT(28)
  24. static int pcc_gate_enable(struct clk_hw *hw)
  25. {
  26. struct clk_gate *gate = to_clk_gate(hw);
  27. unsigned long flags;
  28. u32 val;
  29. int ret;
  30. ret = clk_gate_ops.enable(hw);
  31. if (ret)
  32. return ret;
  33. /* Make sure the IP's clock is ready before release reset */
  34. udelay(1);
  35. spin_lock_irqsave(gate->lock, flags);
  36. /*
  37. * release the sw reset for peripherals associated with
  38. * with this pcc clock.
  39. */
  40. val = readl(gate->reg);
  41. val |= SW_RST;
  42. writel(val, gate->reg);
  43. spin_unlock_irqrestore(gate->lock, flags);
  44. /*
  45. * Read back the register to make sure the previous write has been
  46. * done in the target HW register. For IP like GPU, after deassert
  47. * the reset, need to wait for a while to make sure the sync reset
  48. * is done
  49. */
  50. readl(gate->reg);
  51. udelay(1);
  52. return 0;
  53. }
  54. static void pcc_gate_disable(struct clk_hw *hw)
  55. {
  56. clk_gate_ops.disable(hw);
  57. }
  58. static int pcc_gate_is_enabled(struct clk_hw *hw)
  59. {
  60. return clk_gate_ops.is_enabled(hw);
  61. }
  62. static const struct clk_ops pcc_gate_ops = {
  63. .enable = pcc_gate_enable,
  64. .disable = pcc_gate_disable,
  65. .is_enabled = pcc_gate_is_enabled,
  66. };
  67. static struct clk_hw *imx_ulp_clk_hw_composite(const char *name,
  68. const char * const *parent_names,
  69. int num_parents, bool mux_present,
  70. bool rate_present, bool gate_present,
  71. void __iomem *reg, bool has_swrst)
  72. {
  73. struct clk_hw *mux_hw = NULL, *fd_hw = NULL, *gate_hw = NULL;
  74. struct clk_fractional_divider *fd = NULL;
  75. struct clk_gate *gate = NULL;
  76. struct clk_mux *mux = NULL;
  77. struct clk_hw *hw;
  78. u32 val;
  79. val = readl(reg);
  80. if (!(val & PCG_PR_MASK)) {
  81. pr_info("PCC PR is 0 for clk:%s, bypass\n", name);
  82. return NULL;
  83. }
  84. if (mux_present) {
  85. mux = kzalloc_obj(*mux);
  86. if (!mux)
  87. return ERR_PTR(-ENOMEM);
  88. mux_hw = &mux->hw;
  89. mux->reg = reg;
  90. mux->shift = PCG_PCS_SHIFT;
  91. mux->mask = PCG_PCS_MASK;
  92. if (has_swrst)
  93. mux->lock = &imx_ccm_lock;
  94. }
  95. if (rate_present) {
  96. fd = kzalloc_obj(*fd);
  97. if (!fd) {
  98. kfree(mux);
  99. return ERR_PTR(-ENOMEM);
  100. }
  101. fd_hw = &fd->hw;
  102. fd->reg = reg;
  103. fd->mshift = PCG_FRAC_SHIFT;
  104. fd->mwidth = PCG_FRAC_WIDTH;
  105. fd->nshift = PCG_PCD_SHIFT;
  106. fd->nwidth = PCG_PCD_WIDTH;
  107. fd->flags = CLK_FRAC_DIVIDER_ZERO_BASED;
  108. if (has_swrst)
  109. fd->lock = &imx_ccm_lock;
  110. }
  111. if (gate_present) {
  112. gate = kzalloc_obj(*gate);
  113. if (!gate) {
  114. kfree(mux);
  115. kfree(fd);
  116. return ERR_PTR(-ENOMEM);
  117. }
  118. gate_hw = &gate->hw;
  119. gate->reg = reg;
  120. gate->bit_idx = PCG_CGC_SHIFT;
  121. if (has_swrst)
  122. gate->lock = &imx_ccm_lock;
  123. /*
  124. * make sure clock is gated during clock tree initialization,
  125. * the HW ONLY allow clock parent/rate changed with clock gated,
  126. * during clock tree initialization, clocks could be enabled
  127. * by bootloader, so the HW status will mismatch with clock tree
  128. * prepare count, then clock core driver will allow parent/rate
  129. * change since the prepare count is zero, but HW actually
  130. * prevent the parent/rate change due to the clock is enabled.
  131. */
  132. val = readl_relaxed(reg);
  133. val &= ~(1 << PCG_CGC_SHIFT);
  134. writel_relaxed(val, reg);
  135. }
  136. hw = clk_hw_register_composite(NULL, name, parent_names, num_parents,
  137. mux_hw, &clk_mux_ops, fd_hw,
  138. &clk_fractional_divider_ops, gate_hw,
  139. has_swrst ? &pcc_gate_ops : &clk_gate_ops, CLK_SET_RATE_GATE |
  140. CLK_SET_PARENT_GATE | CLK_SET_RATE_NO_REPARENT);
  141. if (IS_ERR(hw)) {
  142. kfree(mux);
  143. kfree(fd);
  144. kfree(gate);
  145. }
  146. return hw;
  147. }
  148. struct clk_hw *imx7ulp_clk_hw_composite(const char *name, const char * const *parent_names,
  149. int num_parents, bool mux_present, bool rate_present,
  150. bool gate_present, void __iomem *reg)
  151. {
  152. return imx_ulp_clk_hw_composite(name, parent_names, num_parents, mux_present, rate_present,
  153. gate_present, reg, false);
  154. }
  155. struct clk_hw *imx8ulp_clk_hw_composite(const char *name, const char * const *parent_names,
  156. int num_parents, bool mux_present, bool rate_present,
  157. bool gate_present, void __iomem *reg, bool has_swrst)
  158. {
  159. return imx_ulp_clk_hw_composite(name, parent_names, num_parents, mux_present, rate_present,
  160. gate_present, reg, has_swrst);
  161. }
  162. EXPORT_SYMBOL_GPL(imx8ulp_clk_hw_composite);