clk-ref.c 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. /*
  2. * Copyright 2012 Freescale Semiconductor, Inc.
  3. *
  4. * The code contained herein is licensed under the GNU General Public
  5. * License. You may obtain a copy of the GNU General Public License
  6. * Version 2 or later at the following locations:
  7. *
  8. * http://www.opensource.org/licenses/gpl-license.html
  9. * http://www.gnu.org/copyleft/gpl.html
  10. */
  11. #include <linux/clk-provider.h>
  12. #include <linux/err.h>
  13. #include <linux/io.h>
  14. #include <linux/slab.h>
  15. #include "clk.h"
  16. /**
  17. * struct clk_ref - mxs reference clock
  18. * @hw: clk_hw for the reference clock
  19. * @reg: register address
  20. * @idx: the index of the reference clock within the same register
  21. *
  22. * The mxs reference clock sources from pll. Every 4 reference clocks share
  23. * one register space, and @idx is used to identify them. Each reference
  24. * clock has a gate control and a fractional * divider. The rate is calculated
  25. * as pll rate * (18 / FRAC), where FRAC = 18 ~ 35.
  26. */
  27. struct clk_ref {
  28. struct clk_hw hw;
  29. void __iomem *reg;
  30. u8 idx;
  31. };
  32. #define to_clk_ref(_hw) container_of(_hw, struct clk_ref, hw)
  33. static int clk_ref_enable(struct clk_hw *hw)
  34. {
  35. struct clk_ref *ref = to_clk_ref(hw);
  36. writel_relaxed(1 << ((ref->idx + 1) * 8 - 1), ref->reg + CLR);
  37. return 0;
  38. }
  39. static void clk_ref_disable(struct clk_hw *hw)
  40. {
  41. struct clk_ref *ref = to_clk_ref(hw);
  42. writel_relaxed(1 << ((ref->idx + 1) * 8 - 1), ref->reg + SET);
  43. }
  44. static unsigned long clk_ref_recalc_rate(struct clk_hw *hw,
  45. unsigned long parent_rate)
  46. {
  47. struct clk_ref *ref = to_clk_ref(hw);
  48. u64 tmp = parent_rate;
  49. u8 frac = (readl_relaxed(ref->reg) >> (ref->idx * 8)) & 0x3f;
  50. tmp *= 18;
  51. do_div(tmp, frac);
  52. return tmp;
  53. }
  54. static long clk_ref_round_rate(struct clk_hw *hw, unsigned long rate,
  55. unsigned long *prate)
  56. {
  57. unsigned long parent_rate = *prate;
  58. u64 tmp = parent_rate;
  59. u8 frac;
  60. tmp = tmp * 18 + rate / 2;
  61. do_div(tmp, rate);
  62. frac = tmp;
  63. if (frac < 18)
  64. frac = 18;
  65. else if (frac > 35)
  66. frac = 35;
  67. tmp = parent_rate;
  68. tmp *= 18;
  69. do_div(tmp, frac);
  70. return tmp;
  71. }
  72. static int clk_ref_set_rate(struct clk_hw *hw, unsigned long rate,
  73. unsigned long parent_rate)
  74. {
  75. struct clk_ref *ref = to_clk_ref(hw);
  76. unsigned long flags;
  77. u64 tmp = parent_rate;
  78. u32 val;
  79. u8 frac, shift = ref->idx * 8;
  80. tmp = tmp * 18 + rate / 2;
  81. do_div(tmp, rate);
  82. frac = tmp;
  83. if (frac < 18)
  84. frac = 18;
  85. else if (frac > 35)
  86. frac = 35;
  87. spin_lock_irqsave(&mxs_lock, flags);
  88. val = readl_relaxed(ref->reg);
  89. val &= ~(0x3f << shift);
  90. val |= frac << shift;
  91. writel_relaxed(val, ref->reg);
  92. spin_unlock_irqrestore(&mxs_lock, flags);
  93. return 0;
  94. }
  95. static const struct clk_ops clk_ref_ops = {
  96. .enable = clk_ref_enable,
  97. .disable = clk_ref_disable,
  98. .recalc_rate = clk_ref_recalc_rate,
  99. .round_rate = clk_ref_round_rate,
  100. .set_rate = clk_ref_set_rate,
  101. };
  102. struct clk *mxs_clk_ref(const char *name, const char *parent_name,
  103. void __iomem *reg, u8 idx)
  104. {
  105. struct clk_ref *ref;
  106. struct clk *clk;
  107. struct clk_init_data init;
  108. ref = kzalloc(sizeof(*ref), GFP_KERNEL);
  109. if (!ref)
  110. return ERR_PTR(-ENOMEM);
  111. init.name = name;
  112. init.ops = &clk_ref_ops;
  113. init.flags = 0;
  114. init.parent_names = (parent_name ? &parent_name: NULL);
  115. init.num_parents = (parent_name ? 1 : 0);
  116. ref->reg = reg;
  117. ref->idx = idx;
  118. ref->hw.init = &init;
  119. clk = clk_register(NULL, &ref->hw);
  120. if (IS_ERR(clk))
  121. kfree(ref);
  122. return clk;
  123. }