clk-composite-7ulp.c 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  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/err.h>
  10. #include <linux/io.h>
  11. #include <linux/slab.h>
  12. #include "../clk-fractional-divider.h"
  13. #include "clk.h"
  14. #define PCG_PCS_SHIFT 24
  15. #define PCG_PCS_MASK 0x7
  16. #define PCG_CGC_SHIFT 30
  17. #define PCG_FRAC_SHIFT 3
  18. #define PCG_FRAC_WIDTH 1
  19. #define PCG_FRAC_MASK BIT(3)
  20. #define PCG_PCD_SHIFT 0
  21. #define PCG_PCD_WIDTH 3
  22. #define PCG_PCD_MASK 0x7
  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. spin_lock_irqsave(gate->lock, flags);
  34. /*
  35. * release the sw reset for peripherals associated with
  36. * with this pcc clock.
  37. */
  38. val = readl(gate->reg);
  39. val |= SW_RST;
  40. writel(val, gate->reg);
  41. spin_unlock_irqrestore(gate->lock, flags);
  42. return 0;
  43. }
  44. static void pcc_gate_disable(struct clk_hw *hw)
  45. {
  46. clk_gate_ops.disable(hw);
  47. }
  48. static int pcc_gate_is_enabled(struct clk_hw *hw)
  49. {
  50. return clk_gate_ops.is_enabled(hw);
  51. }
  52. static const struct clk_ops pcc_gate_ops = {
  53. .enable = pcc_gate_enable,
  54. .disable = pcc_gate_disable,
  55. .is_enabled = pcc_gate_is_enabled,
  56. };
  57. static struct clk_hw *imx_ulp_clk_hw_composite(const char *name,
  58. const char * const *parent_names,
  59. int num_parents, bool mux_present,
  60. bool rate_present, bool gate_present,
  61. void __iomem *reg, bool has_swrst)
  62. {
  63. struct clk_hw *mux_hw = NULL, *fd_hw = NULL, *gate_hw = NULL;
  64. struct clk_fractional_divider *fd = NULL;
  65. struct clk_gate *gate = NULL;
  66. struct clk_mux *mux = NULL;
  67. struct clk_hw *hw;
  68. u32 val;
  69. if (mux_present) {
  70. mux = kzalloc(sizeof(*mux), GFP_KERNEL);
  71. if (!mux)
  72. return ERR_PTR(-ENOMEM);
  73. mux_hw = &mux->hw;
  74. mux->reg = reg;
  75. mux->shift = PCG_PCS_SHIFT;
  76. mux->mask = PCG_PCS_MASK;
  77. if (has_swrst)
  78. mux->lock = &imx_ccm_lock;
  79. }
  80. if (rate_present) {
  81. fd = kzalloc(sizeof(*fd), GFP_KERNEL);
  82. if (!fd) {
  83. kfree(mux);
  84. return ERR_PTR(-ENOMEM);
  85. }
  86. fd_hw = &fd->hw;
  87. fd->reg = reg;
  88. fd->mshift = PCG_FRAC_SHIFT;
  89. fd->mwidth = PCG_FRAC_WIDTH;
  90. fd->mmask = PCG_FRAC_MASK;
  91. fd->nshift = PCG_PCD_SHIFT;
  92. fd->nwidth = PCG_PCD_WIDTH;
  93. fd->nmask = PCG_PCD_MASK;
  94. fd->flags = CLK_FRAC_DIVIDER_ZERO_BASED;
  95. if (has_swrst)
  96. fd->lock = &imx_ccm_lock;
  97. }
  98. if (gate_present) {
  99. gate = kzalloc(sizeof(*gate), GFP_KERNEL);
  100. if (!gate) {
  101. kfree(mux);
  102. kfree(fd);
  103. return ERR_PTR(-ENOMEM);
  104. }
  105. gate_hw = &gate->hw;
  106. gate->reg = reg;
  107. gate->bit_idx = PCG_CGC_SHIFT;
  108. if (has_swrst)
  109. gate->lock = &imx_ccm_lock;
  110. /*
  111. * make sure clock is gated during clock tree initialization,
  112. * the HW ONLY allow clock parent/rate changed with clock gated,
  113. * during clock tree initialization, clocks could be enabled
  114. * by bootloader, so the HW status will mismatch with clock tree
  115. * prepare count, then clock core driver will allow parent/rate
  116. * change since the prepare count is zero, but HW actually
  117. * prevent the parent/rate change due to the clock is enabled.
  118. */
  119. val = readl_relaxed(reg);
  120. val &= ~(1 << PCG_CGC_SHIFT);
  121. writel_relaxed(val, reg);
  122. }
  123. hw = clk_hw_register_composite(NULL, name, parent_names, num_parents,
  124. mux_hw, &clk_mux_ops, fd_hw,
  125. &clk_fractional_divider_ops, gate_hw,
  126. has_swrst ? &pcc_gate_ops : &clk_gate_ops, CLK_SET_RATE_GATE |
  127. CLK_SET_PARENT_GATE | CLK_SET_RATE_NO_REPARENT);
  128. if (IS_ERR(hw)) {
  129. kfree(mux);
  130. kfree(fd);
  131. kfree(gate);
  132. }
  133. return hw;
  134. }
  135. struct clk_hw *imx7ulp_clk_hw_composite(const char *name, const char * const *parent_names,
  136. int num_parents, bool mux_present, bool rate_present,
  137. bool gate_present, void __iomem *reg)
  138. {
  139. return imx_ulp_clk_hw_composite(name, parent_names, num_parents, mux_present, rate_present,
  140. gate_present, reg, false);
  141. }
  142. struct clk_hw *imx8ulp_clk_hw_composite(const char *name, const char * const *parent_names,
  143. int num_parents, bool mux_present, bool rate_present,
  144. bool gate_present, void __iomem *reg, bool has_swrst)
  145. {
  146. return imx_ulp_clk_hw_composite(name, parent_names, num_parents, mux_present, rate_present,
  147. gate_present, reg, has_swrst);
  148. }
  149. EXPORT_SYMBOL_GPL(imx8ulp_clk_hw_composite);