clkdivider-hi6220.c 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Hisilicon hi6220 SoC divider clock driver
  4. *
  5. * Copyright (c) 2015 Hisilicon Limited.
  6. *
  7. * Author: Bintian Wang <[email protected]>
  8. */
  9. #include <linux/kernel.h>
  10. #include <linux/clk-provider.h>
  11. #include <linux/slab.h>
  12. #include <linux/io.h>
  13. #include <linux/err.h>
  14. #include <linux/spinlock.h>
  15. #include "clk.h"
  16. #define div_mask(width) ((1 << (width)) - 1)
  17. /**
  18. * struct hi6220_clk_divider - divider clock for hi6220
  19. *
  20. * @hw: handle between common and hardware-specific interfaces
  21. * @reg: register containing divider
  22. * @shift: shift to the divider bit field
  23. * @width: width of the divider bit field
  24. * @mask: mask for setting divider rate
  25. * @table: the div table that the divider supports
  26. * @lock: register lock
  27. */
  28. struct hi6220_clk_divider {
  29. struct clk_hw hw;
  30. void __iomem *reg;
  31. u8 shift;
  32. u8 width;
  33. u32 mask;
  34. const struct clk_div_table *table;
  35. spinlock_t *lock;
  36. };
  37. #define to_hi6220_clk_divider(_hw) \
  38. container_of(_hw, struct hi6220_clk_divider, hw)
  39. static unsigned long hi6220_clkdiv_recalc_rate(struct clk_hw *hw,
  40. unsigned long parent_rate)
  41. {
  42. unsigned int val;
  43. struct hi6220_clk_divider *dclk = to_hi6220_clk_divider(hw);
  44. val = readl_relaxed(dclk->reg) >> dclk->shift;
  45. val &= div_mask(dclk->width);
  46. return divider_recalc_rate(hw, parent_rate, val, dclk->table,
  47. CLK_DIVIDER_ROUND_CLOSEST, dclk->width);
  48. }
  49. static long hi6220_clkdiv_round_rate(struct clk_hw *hw, unsigned long rate,
  50. unsigned long *prate)
  51. {
  52. struct hi6220_clk_divider *dclk = to_hi6220_clk_divider(hw);
  53. return divider_round_rate(hw, rate, prate, dclk->table,
  54. dclk->width, CLK_DIVIDER_ROUND_CLOSEST);
  55. }
  56. static int hi6220_clkdiv_set_rate(struct clk_hw *hw, unsigned long rate,
  57. unsigned long parent_rate)
  58. {
  59. int value;
  60. unsigned long flags = 0;
  61. u32 data;
  62. struct hi6220_clk_divider *dclk = to_hi6220_clk_divider(hw);
  63. value = divider_get_val(rate, parent_rate, dclk->table,
  64. dclk->width, CLK_DIVIDER_ROUND_CLOSEST);
  65. if (dclk->lock)
  66. spin_lock_irqsave(dclk->lock, flags);
  67. data = readl_relaxed(dclk->reg);
  68. data &= ~(div_mask(dclk->width) << dclk->shift);
  69. data |= value << dclk->shift;
  70. data |= dclk->mask;
  71. writel_relaxed(data, dclk->reg);
  72. if (dclk->lock)
  73. spin_unlock_irqrestore(dclk->lock, flags);
  74. return 0;
  75. }
  76. static const struct clk_ops hi6220_clkdiv_ops = {
  77. .recalc_rate = hi6220_clkdiv_recalc_rate,
  78. .round_rate = hi6220_clkdiv_round_rate,
  79. .set_rate = hi6220_clkdiv_set_rate,
  80. };
  81. struct clk *hi6220_register_clkdiv(struct device *dev, const char *name,
  82. const char *parent_name, unsigned long flags, void __iomem *reg,
  83. u8 shift, u8 width, u32 mask_bit, spinlock_t *lock)
  84. {
  85. struct hi6220_clk_divider *div;
  86. struct clk *clk;
  87. struct clk_init_data init;
  88. struct clk_div_table *table;
  89. u32 max_div, min_div;
  90. int i;
  91. /* allocate the divider */
  92. div = kzalloc(sizeof(*div), GFP_KERNEL);
  93. if (!div)
  94. return ERR_PTR(-ENOMEM);
  95. /* Init the divider table */
  96. max_div = div_mask(width) + 1;
  97. min_div = 1;
  98. table = kcalloc(max_div + 1, sizeof(*table), GFP_KERNEL);
  99. if (!table) {
  100. kfree(div);
  101. return ERR_PTR(-ENOMEM);
  102. }
  103. for (i = 0; i < max_div; i++) {
  104. table[i].div = min_div + i;
  105. table[i].val = table[i].div - 1;
  106. }
  107. init.name = name;
  108. init.ops = &hi6220_clkdiv_ops;
  109. init.flags = flags;
  110. init.parent_names = parent_name ? &parent_name : NULL;
  111. init.num_parents = parent_name ? 1 : 0;
  112. /* struct hi6220_clk_divider assignments */
  113. div->reg = reg;
  114. div->shift = shift;
  115. div->width = width;
  116. div->mask = mask_bit ? BIT(mask_bit) : 0;
  117. div->lock = lock;
  118. div->hw.init = &init;
  119. div->table = table;
  120. /* register the clock */
  121. clk = clk_register(dev, &div->hw);
  122. if (IS_ERR(clk)) {
  123. kfree(table);
  124. kfree(div);
  125. }
  126. return clk;
  127. }