clk-programmable.c 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * Programmable clock support for AT91 architectures.
  4. *
  5. * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
  6. *
  7. * Author: Claudiu Beznea <claudiu.beznea@microchip.com>
  8. *
  9. * Based on drivers/clk/at91/clk-programmable.c from Linux.
  10. */
  11. #include <common.h>
  12. #include <clk-uclass.h>
  13. #include <dm.h>
  14. #include <linux/clk-provider.h>
  15. #include <linux/clk/at91_pmc.h>
  16. #include "pmc.h"
  17. #define UBOOT_DM_CLK_AT91_PROG "at91-prog-clk"
  18. #define PROG_ID_MAX 7
  19. #define PROG_STATUS_MASK(id) (1 << ((id) + 8))
  20. #define PROG_PRES(_l, _p) (((_p) >> (_l)->pres_shift) & (_l)->pres_mask)
  21. #define PROG_MAX_RM9200_CSS 3
  22. struct clk_programmable {
  23. void __iomem *base;
  24. const u32 *clk_mux_table;
  25. const u32 *mux_table;
  26. const struct clk_programmable_layout *layout;
  27. u32 num_parents;
  28. struct clk clk;
  29. u8 id;
  30. };
  31. #define to_clk_programmable(_c) container_of(_c, struct clk_programmable, clk)
  32. static ulong clk_programmable_get_rate(struct clk *clk)
  33. {
  34. struct clk_programmable *prog = to_clk_programmable(clk);
  35. const struct clk_programmable_layout *layout = prog->layout;
  36. ulong rate, parent_rate = clk_get_parent_rate(clk);
  37. unsigned int pckr;
  38. pmc_read(prog->base, AT91_PMC_PCKR(prog->id), &pckr);
  39. if (layout->is_pres_direct)
  40. rate = parent_rate / (PROG_PRES(layout, pckr) + 1);
  41. else
  42. rate = parent_rate >> PROG_PRES(layout, pckr);
  43. return rate;
  44. }
  45. static int clk_programmable_set_parent(struct clk *clk, struct clk *parent)
  46. {
  47. struct clk_programmable *prog = to_clk_programmable(clk);
  48. const struct clk_programmable_layout *layout = prog->layout;
  49. unsigned int mask = layout->css_mask;
  50. int index;
  51. index = at91_clk_mux_val_to_index(prog->clk_mux_table,
  52. prog->num_parents, parent->id);
  53. if (index < 0)
  54. return index;
  55. index = at91_clk_mux_index_to_val(prog->mux_table, prog->num_parents,
  56. index);
  57. if (index < 0)
  58. return index;
  59. if (layout->have_slck_mck)
  60. mask |= AT91_PMC_CSSMCK_MCK;
  61. if (index > layout->css_mask) {
  62. if (index > PROG_MAX_RM9200_CSS && !layout->have_slck_mck)
  63. return -EINVAL;
  64. index |= AT91_PMC_CSSMCK_MCK;
  65. }
  66. pmc_update_bits(prog->base, AT91_PMC_PCKR(prog->id), mask, index);
  67. return 0;
  68. }
  69. static ulong clk_programmable_set_rate(struct clk *clk, ulong rate)
  70. {
  71. struct clk_programmable *prog = to_clk_programmable(clk);
  72. const struct clk_programmable_layout *layout = prog->layout;
  73. ulong parent_rate = clk_get_parent_rate(clk);
  74. ulong div = parent_rate / rate;
  75. int shift = 0;
  76. if (!parent_rate || !div)
  77. return -EINVAL;
  78. if (layout->is_pres_direct) {
  79. shift = div - 1;
  80. if (shift > layout->pres_mask)
  81. return -EINVAL;
  82. } else {
  83. shift = fls(div) - 1;
  84. if (div != (1 << shift))
  85. return -EINVAL;
  86. if (shift >= layout->pres_mask)
  87. return -EINVAL;
  88. }
  89. pmc_update_bits(prog->base, AT91_PMC_PCKR(prog->id),
  90. layout->pres_mask << layout->pres_shift,
  91. shift << layout->pres_shift);
  92. if (layout->is_pres_direct)
  93. return (parent_rate / shift + 1);
  94. return parent_rate >> shift;
  95. }
  96. static const struct clk_ops programmable_ops = {
  97. .get_rate = clk_programmable_get_rate,
  98. .set_parent = clk_programmable_set_parent,
  99. .set_rate = clk_programmable_set_rate,
  100. };
  101. struct clk *at91_clk_register_programmable(void __iomem *base, const char *name,
  102. const char *const *parent_names, u8 num_parents, u8 id,
  103. const struct clk_programmable_layout *layout,
  104. const u32 *clk_mux_table, const u32 *mux_table)
  105. {
  106. struct clk_programmable *prog;
  107. struct clk *clk;
  108. u32 val, tmp;
  109. int ret;
  110. if (!base || !name || !parent_names || !num_parents ||
  111. !layout || !clk_mux_table || !mux_table || id > PROG_ID_MAX)
  112. return ERR_PTR(-EINVAL);
  113. prog = kzalloc(sizeof(*prog), GFP_KERNEL);
  114. if (!prog)
  115. return ERR_PTR(-ENOMEM);
  116. prog->id = id;
  117. prog->layout = layout;
  118. prog->base = base;
  119. prog->clk_mux_table = clk_mux_table;
  120. prog->mux_table = mux_table;
  121. prog->num_parents = num_parents;
  122. pmc_read(prog->base, AT91_PMC_PCKR(prog->id), &tmp);
  123. val = tmp & prog->layout->css_mask;
  124. if (layout->have_slck_mck && (tmp & AT91_PMC_CSSMCK_MCK) && !val)
  125. ret = PROG_MAX_RM9200_CSS + 1;
  126. else
  127. ret = at91_clk_mux_val_to_index(prog->mux_table,
  128. prog->num_parents, val);
  129. if (ret < 0) {
  130. kfree(prog);
  131. return ERR_PTR(ret);
  132. }
  133. clk = &prog->clk;
  134. clk->flags = CLK_GET_RATE_NOCACHE;
  135. ret = clk_register(clk, UBOOT_DM_CLK_AT91_PROG, name,
  136. parent_names[ret]);
  137. if (ret) {
  138. kfree(prog);
  139. clk = ERR_PTR(ret);
  140. }
  141. return clk;
  142. }
  143. U_BOOT_DRIVER(at91_prog_clk) = {
  144. .name = UBOOT_DM_CLK_AT91_PROG,
  145. .id = UCLASS_CLK,
  146. .ops = &programmable_ops,
  147. .flags = DM_FLAG_PRE_RELOC,
  148. };
  149. const struct clk_programmable_layout at91rm9200_programmable_layout = {
  150. .pres_mask = 0x7,
  151. .pres_shift = 2,
  152. .css_mask = 0x3,
  153. .have_slck_mck = 0,
  154. .is_pres_direct = 0,
  155. };
  156. const struct clk_programmable_layout at91sam9g45_programmable_layout = {
  157. .pres_mask = 0x7,
  158. .pres_shift = 2,
  159. .css_mask = 0x3,
  160. .have_slck_mck = 1,
  161. .is_pres_direct = 0,
  162. };
  163. const struct clk_programmable_layout at91sam9x5_programmable_layout = {
  164. .pres_mask = 0x7,
  165. .pres_shift = 4,
  166. .css_mask = 0x7,
  167. .have_slck_mck = 0,
  168. .is_pres_direct = 0,
  169. };