clk-periph-s10.c 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Copyright (C) 2017, Intel Corporation
  4. */
  5. #include <linux/slab.h>
  6. #include <linux/clk-provider.h>
  7. #include "stratix10-clk.h"
  8. #include "clk.h"
  9. #define CLK_MGR_FREE_SHIFT 16
  10. #define CLK_MGR_FREE_MASK 0x7
  11. #define SWCTRLBTCLKSEN_SHIFT 8
  12. #define to_periph_clk(p) container_of(p, struct socfpga_periph_clk, hw.hw)
  13. static unsigned long clk_peri_c_clk_recalc_rate(struct clk_hw *hwclk,
  14. unsigned long parent_rate)
  15. {
  16. struct socfpga_periph_clk *socfpgaclk = to_periph_clk(hwclk);
  17. unsigned long div = 1;
  18. u32 val;
  19. val = readl(socfpgaclk->hw.reg);
  20. val &= GENMASK(SWCTRLBTCLKSEN_SHIFT - 1, 0);
  21. parent_rate /= val;
  22. return parent_rate / div;
  23. }
  24. static unsigned long clk_peri_cnt_clk_recalc_rate(struct clk_hw *hwclk,
  25. unsigned long parent_rate)
  26. {
  27. struct socfpga_periph_clk *socfpgaclk = to_periph_clk(hwclk);
  28. unsigned long div = 1;
  29. if (socfpgaclk->fixed_div) {
  30. div = socfpgaclk->fixed_div;
  31. } else {
  32. if (socfpgaclk->hw.reg)
  33. div = ((readl(socfpgaclk->hw.reg) & 0x7ff) + 1);
  34. }
  35. return parent_rate / div;
  36. }
  37. static u8 clk_periclk_get_parent(struct clk_hw *hwclk)
  38. {
  39. struct socfpga_periph_clk *socfpgaclk = to_periph_clk(hwclk);
  40. u32 clk_src, mask;
  41. u8 parent;
  42. if (socfpgaclk->bypass_reg) {
  43. mask = (0x1 << socfpgaclk->bypass_shift);
  44. parent = ((readl(socfpgaclk->bypass_reg) & mask) >>
  45. socfpgaclk->bypass_shift);
  46. } else {
  47. clk_src = readl(socfpgaclk->hw.reg);
  48. parent = (clk_src >> CLK_MGR_FREE_SHIFT) &
  49. CLK_MGR_FREE_MASK;
  50. }
  51. return parent;
  52. }
  53. static const struct clk_ops peri_c_clk_ops = {
  54. .recalc_rate = clk_peri_c_clk_recalc_rate,
  55. .get_parent = clk_periclk_get_parent,
  56. };
  57. static const struct clk_ops peri_cnt_clk_ops = {
  58. .recalc_rate = clk_peri_cnt_clk_recalc_rate,
  59. .get_parent = clk_periclk_get_parent,
  60. };
  61. struct clk *s10_register_periph(const char *name, const char *parent_name,
  62. const char * const *parent_names,
  63. u8 num_parents, unsigned long flags,
  64. void __iomem *reg, unsigned long offset)
  65. {
  66. struct clk *clk;
  67. struct socfpga_periph_clk *periph_clk;
  68. struct clk_init_data init;
  69. periph_clk = kzalloc(sizeof(*periph_clk), GFP_KERNEL);
  70. if (WARN_ON(!periph_clk))
  71. return NULL;
  72. periph_clk->hw.reg = reg + offset;
  73. init.name = name;
  74. init.ops = &peri_c_clk_ops;
  75. init.flags = flags;
  76. init.num_parents = num_parents;
  77. init.parent_names = parent_names ? parent_names : &parent_name;
  78. periph_clk->hw.hw.init = &init;
  79. clk = clk_register(NULL, &periph_clk->hw.hw);
  80. if (WARN_ON(IS_ERR(clk))) {
  81. kfree(periph_clk);
  82. return NULL;
  83. }
  84. return clk;
  85. }
  86. struct clk *s10_register_cnt_periph(const char *name, const char *parent_name,
  87. const char * const *parent_names,
  88. u8 num_parents, unsigned long flags,
  89. void __iomem *regbase, unsigned long offset,
  90. u8 fixed_divider, unsigned long bypass_reg,
  91. unsigned long bypass_shift)
  92. {
  93. struct clk *clk;
  94. struct socfpga_periph_clk *periph_clk;
  95. struct clk_init_data init;
  96. periph_clk = kzalloc(sizeof(*periph_clk), GFP_KERNEL);
  97. if (WARN_ON(!periph_clk))
  98. return NULL;
  99. if (offset)
  100. periph_clk->hw.reg = regbase + offset;
  101. else
  102. periph_clk->hw.reg = NULL;
  103. if (bypass_reg)
  104. periph_clk->bypass_reg = regbase + bypass_reg;
  105. else
  106. periph_clk->bypass_reg = NULL;
  107. periph_clk->bypass_shift = bypass_shift;
  108. periph_clk->fixed_div = fixed_divider;
  109. init.name = name;
  110. init.ops = &peri_cnt_clk_ops;
  111. init.flags = flags;
  112. init.num_parents = num_parents;
  113. init.parent_names = parent_names ? parent_names : &parent_name;
  114. periph_clk->hw.hw.init = &init;
  115. clk = clk_register(NULL, &periph_clk->hw.hw);
  116. if (WARN_ON(IS_ERR(clk))) {
  117. kfree(periph_clk);
  118. return NULL;
  119. }
  120. return clk;
  121. }