clk-master.c 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * Master 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-master.c from Linux.
  10. */
  11. #include <asm/processor.h>
  12. #include <clk-uclass.h>
  13. #include <common.h>
  14. #include <dm.h>
  15. #include <linux/clk-provider.h>
  16. #include <linux/clk/at91_pmc.h>
  17. #include "pmc.h"
  18. #define UBOOT_DM_CLK_AT91_MASTER "at91-master-clk"
  19. #define UBOOT_DM_CLK_AT91_SAMA7G5_MASTER "at91-sama7g5-master-clk"
  20. #define MASTER_PRES_MASK 0x7
  21. #define MASTER_PRES_MAX MASTER_PRES_MASK
  22. #define MASTER_DIV_SHIFT 8
  23. #define MASTER_DIV_MASK 0x3
  24. #define PMC_MCR 0x30
  25. #define PMC_MCR_ID_MSK GENMASK(3, 0)
  26. #define PMC_MCR_CMD BIT(7)
  27. #define PMC_MCR_DIV GENMASK(10, 8)
  28. #define PMC_MCR_CSS GENMASK(20, 16)
  29. #define PMC_MCR_CSS_SHIFT (16)
  30. #define PMC_MCR_EN BIT(28)
  31. #define PMC_MCR_ID(x) ((x) & PMC_MCR_ID_MSK)
  32. #define MASTER_MAX_ID 4
  33. struct clk_master {
  34. void __iomem *base;
  35. const struct clk_master_layout *layout;
  36. const struct clk_master_characteristics *characteristics;
  37. const u32 *mux_table;
  38. const u32 *clk_mux_table;
  39. u32 num_parents;
  40. struct clk clk;
  41. u8 id;
  42. };
  43. #define to_clk_master(_clk) container_of(_clk, struct clk_master, clk)
  44. static inline bool clk_master_ready(struct clk_master *master)
  45. {
  46. unsigned int bit = master->id ? AT91_PMC_MCKXRDY : AT91_PMC_MCKRDY;
  47. unsigned int status;
  48. pmc_read(master->base, AT91_PMC_SR, &status);
  49. return !!(status & bit);
  50. }
  51. static int clk_master_enable(struct clk *clk)
  52. {
  53. struct clk_master *master = to_clk_master(clk);
  54. while (!clk_master_ready(master)) {
  55. debug("waiting for mck %d\n", master->id);
  56. cpu_relax();
  57. }
  58. return 0;
  59. }
  60. static ulong clk_master_get_rate(struct clk *clk)
  61. {
  62. struct clk_master *master = to_clk_master(clk);
  63. const struct clk_master_layout *layout = master->layout;
  64. const struct clk_master_characteristics *characteristics =
  65. master->characteristics;
  66. ulong rate = clk_get_parent_rate(clk);
  67. unsigned int mckr;
  68. u8 pres, div;
  69. if (!rate)
  70. return 0;
  71. pmc_read(master->base, master->layout->offset, &mckr);
  72. mckr &= layout->mask;
  73. pres = (mckr >> layout->pres_shift) & MASTER_PRES_MASK;
  74. div = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
  75. if (characteristics->have_div3_pres && pres == MASTER_PRES_MAX)
  76. rate /= 3;
  77. else
  78. rate >>= pres;
  79. rate /= characteristics->divisors[div];
  80. if (rate < characteristics->output.min)
  81. pr_warn("master clk is underclocked");
  82. else if (rate > characteristics->output.max)
  83. pr_warn("master clk is overclocked");
  84. return rate;
  85. }
  86. static const struct clk_ops master_ops = {
  87. .enable = clk_master_enable,
  88. .get_rate = clk_master_get_rate,
  89. };
  90. struct clk *at91_clk_register_master(void __iomem *base,
  91. const char *name, const char * const *parent_names,
  92. int num_parents, const struct clk_master_layout *layout,
  93. const struct clk_master_characteristics *characteristics,
  94. const u32 *mux_table)
  95. {
  96. struct clk_master *master;
  97. struct clk *clk;
  98. unsigned int val;
  99. int ret;
  100. if (!base || !name || !num_parents || !parent_names ||
  101. !layout || !characteristics || !mux_table)
  102. return ERR_PTR(-EINVAL);
  103. master = kzalloc(sizeof(*master), GFP_KERNEL);
  104. if (!master)
  105. return ERR_PTR(-ENOMEM);
  106. master->layout = layout;
  107. master->characteristics = characteristics;
  108. master->base = base;
  109. master->num_parents = num_parents;
  110. master->mux_table = mux_table;
  111. pmc_read(master->base, master->layout->offset, &val);
  112. clk = &master->clk;
  113. clk->flags = CLK_GET_RATE_NOCACHE | CLK_IS_CRITICAL;
  114. ret = clk_register(clk, UBOOT_DM_CLK_AT91_MASTER, name,
  115. parent_names[val & AT91_PMC_CSS]);
  116. if (ret) {
  117. kfree(master);
  118. clk = ERR_PTR(ret);
  119. }
  120. return clk;
  121. }
  122. U_BOOT_DRIVER(at91_master_clk) = {
  123. .name = UBOOT_DM_CLK_AT91_MASTER,
  124. .id = UCLASS_CLK,
  125. .ops = &master_ops,
  126. .flags = DM_FLAG_PRE_RELOC,
  127. };
  128. static int clk_sama7g5_master_set_parent(struct clk *clk, struct clk *parent)
  129. {
  130. struct clk_master *master = to_clk_master(clk);
  131. int index;
  132. index = at91_clk_mux_val_to_index(master->clk_mux_table,
  133. master->num_parents, parent->id);
  134. if (index < 0)
  135. return index;
  136. index = at91_clk_mux_index_to_val(master->mux_table,
  137. master->num_parents, index);
  138. if (index < 0)
  139. return index;
  140. pmc_write(master->base, PMC_MCR, PMC_MCR_ID(master->id));
  141. pmc_update_bits(master->base, PMC_MCR,
  142. PMC_MCR_CSS | PMC_MCR_CMD | PMC_MCR_ID_MSK,
  143. (index << PMC_MCR_CSS_SHIFT) | PMC_MCR_CMD |
  144. PMC_MCR_ID(master->id));
  145. return 0;
  146. }
  147. static int clk_sama7g5_master_enable(struct clk *clk)
  148. {
  149. struct clk_master *master = to_clk_master(clk);
  150. pmc_write(master->base, PMC_MCR, PMC_MCR_ID(master->id));
  151. pmc_update_bits(master->base, PMC_MCR,
  152. PMC_MCR_EN | PMC_MCR_CMD | PMC_MCR_ID_MSK,
  153. PMC_MCR_EN | PMC_MCR_CMD | PMC_MCR_ID(master->id));
  154. return 0;
  155. }
  156. static int clk_sama7g5_master_disable(struct clk *clk)
  157. {
  158. struct clk_master *master = to_clk_master(clk);
  159. pmc_write(master->base, PMC_MCR, master->id);
  160. pmc_update_bits(master->base, PMC_MCR,
  161. PMC_MCR_EN | PMC_MCR_CMD | PMC_MCR_ID_MSK,
  162. PMC_MCR_CMD | PMC_MCR_ID(master->id));
  163. return 0;
  164. }
  165. static ulong clk_sama7g5_master_set_rate(struct clk *clk, ulong rate)
  166. {
  167. struct clk_master *master = to_clk_master(clk);
  168. ulong parent_rate = clk_get_parent_rate(clk);
  169. ulong div, rrate;
  170. if (!parent_rate)
  171. return 0;
  172. div = DIV_ROUND_CLOSEST(parent_rate, rate);
  173. if ((div > (1 << (MASTER_PRES_MAX - 1))) || (div & (div - 1))) {
  174. return 0;
  175. } else if (div == 3) {
  176. rrate = DIV_ROUND_CLOSEST(parent_rate, MASTER_PRES_MAX);
  177. div = MASTER_PRES_MAX;
  178. } else {
  179. rrate = DIV_ROUND_CLOSEST(parent_rate, div);
  180. div = ffs(div) - 1;
  181. }
  182. pmc_write(master->base, PMC_MCR, master->id);
  183. pmc_update_bits(master->base, PMC_MCR,
  184. PMC_MCR_DIV | PMC_MCR_CMD | PMC_MCR_ID_MSK,
  185. (div << MASTER_DIV_SHIFT) | PMC_MCR_CMD |
  186. PMC_MCR_ID(master->id));
  187. return rrate;
  188. }
  189. static ulong clk_sama7g5_master_get_rate(struct clk *clk)
  190. {
  191. struct clk_master *master = to_clk_master(clk);
  192. ulong parent_rate = clk_get_parent_rate(clk);
  193. unsigned int val;
  194. ulong div;
  195. if (!parent_rate)
  196. return 0;
  197. pmc_write(master->base, PMC_MCR, master->id);
  198. pmc_read(master->base, PMC_MCR, &val);
  199. div = (val >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
  200. if (div == MASTER_PRES_MAX)
  201. div = 3;
  202. else
  203. div = 1 << div;
  204. return DIV_ROUND_CLOSEST(parent_rate, div);
  205. }
  206. static const struct clk_ops sama7g5_master_ops = {
  207. .enable = clk_sama7g5_master_enable,
  208. .disable = clk_sama7g5_master_disable,
  209. .set_rate = clk_sama7g5_master_set_rate,
  210. .get_rate = clk_sama7g5_master_get_rate,
  211. .set_parent = clk_sama7g5_master_set_parent,
  212. };
  213. struct clk *at91_clk_sama7g5_register_master(void __iomem *base,
  214. const char *name, const char * const *parent_names,
  215. int num_parents, const u32 *mux_table, const u32 *clk_mux_table,
  216. bool critical, u8 id)
  217. {
  218. struct clk_master *master;
  219. struct clk *clk;
  220. u32 val, index;
  221. int ret;
  222. if (!base || !name || !num_parents || !parent_names ||
  223. !mux_table || !clk_mux_table || id > MASTER_MAX_ID)
  224. return ERR_PTR(-EINVAL);
  225. master = kzalloc(sizeof(*master), GFP_KERNEL);
  226. if (!master)
  227. return ERR_PTR(-ENOMEM);
  228. master->base = base;
  229. master->id = id;
  230. master->mux_table = mux_table;
  231. master->clk_mux_table = clk_mux_table;
  232. master->num_parents = num_parents;
  233. pmc_write(master->base, PMC_MCR, master->id);
  234. pmc_read(master->base, PMC_MCR, &val);
  235. index = at91_clk_mux_val_to_index(master->mux_table,
  236. master->num_parents,
  237. (val & PMC_MCR_CSS) >> PMC_MCR_CSS_SHIFT);
  238. if (index < 0) {
  239. kfree(master);
  240. return ERR_PTR(index);
  241. }
  242. clk = &master->clk;
  243. clk->flags = CLK_GET_RATE_NOCACHE | (critical ? CLK_IS_CRITICAL : 0);
  244. ret = clk_register(clk, UBOOT_DM_CLK_AT91_SAMA7G5_MASTER, name,
  245. parent_names[index]);
  246. if (ret) {
  247. kfree(master);
  248. clk = ERR_PTR(ret);
  249. }
  250. return clk;
  251. }
  252. U_BOOT_DRIVER(at91_sama7g5_master_clk) = {
  253. .name = UBOOT_DM_CLK_AT91_SAMA7G5_MASTER,
  254. .id = UCLASS_CLK,
  255. .ops = &sama7g5_master_ops,
  256. .flags = DM_FLAG_PRE_RELOC,
  257. };
  258. const struct clk_master_layout at91rm9200_master_layout = {
  259. .mask = 0x31F,
  260. .pres_shift = 2,
  261. .offset = AT91_PMC_MCKR,
  262. };
  263. const struct clk_master_layout at91sam9x5_master_layout = {
  264. .mask = 0x373,
  265. .pres_shift = 4,
  266. .offset = AT91_PMC_MCKR,
  267. };