armada-37xx-periph.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * Marvell Armada 37xx SoC Peripheral clocks
  4. *
  5. * Marek Behun <marek.behun@nic.cz>
  6. *
  7. * Based on Linux driver by:
  8. * Gregory CLEMENT <gregory.clement@free-electrons.com>
  9. */
  10. #include <common.h>
  11. #include <malloc.h>
  12. #include <clk-uclass.h>
  13. #include <clk.h>
  14. #include <dm.h>
  15. #include <asm/io.h>
  16. #include <asm/arch/cpu.h>
  17. #define TBG_SEL 0x0
  18. #define DIV_SEL0 0x4
  19. #define DIV_SEL1 0x8
  20. #define DIV_SEL2 0xC
  21. #define CLK_SEL 0x10
  22. #define CLK_DIS 0x14
  23. enum a37xx_periph_parent {
  24. TBG_A_P = 0,
  25. TBG_B_P = 1,
  26. TBG_A_S = 2,
  27. TBG_B_S = 3,
  28. MAX_TBG_PARENTS = 4,
  29. XTAL = 4,
  30. MAX_PARENTS = 5,
  31. };
  32. static const struct {
  33. const char *name;
  34. enum a37xx_periph_parent parent;
  35. } a37xx_periph_parent_names[] = {
  36. { "TBG-A-P", TBG_A_P },
  37. { "TBG-B-P", TBG_B_P },
  38. { "TBG-A-S", TBG_A_S },
  39. { "TBG-B-S", TBG_B_S },
  40. { "xtal", XTAL },
  41. };
  42. struct clk_periph;
  43. struct a37xx_periphclk {
  44. void __iomem *reg;
  45. ulong parents[MAX_PARENTS];
  46. const struct clk_periph *clks;
  47. bool clk_has_periph_parent[16];
  48. int clk_parent[16];
  49. int count;
  50. };
  51. struct clk_div_table {
  52. u32 div;
  53. u32 val;
  54. };
  55. struct clk_periph {
  56. const char *name;
  57. const char *parent_name;
  58. u32 disable_bit;
  59. int mux_shift;
  60. const struct clk_div_table *div_table[2];
  61. s32 div_reg_off[2];
  62. u32 div_mask[2];
  63. int div_shift[2];
  64. unsigned can_gate : 1;
  65. unsigned can_mux : 1;
  66. unsigned dividers : 2;
  67. };
  68. static const struct clk_div_table div_table1[] = {
  69. { 1, 1 },
  70. { 2, 2 },
  71. { 0, 0 },
  72. };
  73. static const struct clk_div_table div_table2[] = {
  74. { 2, 1 },
  75. { 4, 2 },
  76. { 0, 0 },
  77. };
  78. static const struct clk_div_table div_table6[] = {
  79. { 1, 1 },
  80. { 2, 2 },
  81. { 3, 3 },
  82. { 4, 4 },
  83. { 5, 5 },
  84. { 6, 6 },
  85. { 0, 0 },
  86. };
  87. #define CLK_FULL_DD(_n, _d, _mux, _r0, _r1, _s0, _s1) \
  88. { \
  89. .name = #_n, \
  90. .disable_bit = BIT(_d), \
  91. .mux_shift = _mux, \
  92. .div_table[0] = div_table6, \
  93. .div_table[1] = div_table6, \
  94. .div_reg_off[0] = _r0, \
  95. .div_reg_off[1] = _r1, \
  96. .div_shift[0] = _s0, \
  97. .div_shift[1] = _s1, \
  98. .div_mask[0] = 7, \
  99. .div_mask[1] = 7, \
  100. .can_gate = 1, \
  101. .can_mux = 1, \
  102. .dividers = 2, \
  103. }
  104. #define CLK_FULL(_n, _d, _mux, _r, _s, _m, _t) \
  105. { \
  106. .name = #_n, \
  107. .disable_bit = BIT(_d), \
  108. .mux_shift = _mux, \
  109. .div_table[0] = _t, \
  110. .div_reg_off[0] = _r, \
  111. .div_shift[0] = _s, \
  112. .div_mask[0] = _m, \
  113. .can_gate = 1, \
  114. .can_mux = 1, \
  115. .dividers = 1, \
  116. }
  117. #define CLK_GATE_DIV(_n, _d, _r, _s, _m, _t, _p) \
  118. { \
  119. .name = #_n, \
  120. .parent_name = _p, \
  121. .disable_bit = BIT(_d), \
  122. .div_table[0] = _t, \
  123. .div_reg_off[0] = _r, \
  124. .div_shift[0] = _s, \
  125. .div_mask[0] = _m, \
  126. .can_gate = 1, \
  127. .dividers = 1, \
  128. }
  129. #define CLK_GATE(_n, _d, _p) \
  130. { \
  131. .name = #_n, \
  132. .parent_name = _p, \
  133. .disable_bit = BIT(_d), \
  134. .can_gate = 1, \
  135. }
  136. #define CLK_MUX_DIV(_n, _mux, _r, _s, _m, _t) \
  137. { \
  138. .name = #_n, \
  139. .mux_shift = _mux, \
  140. .div_table[0] = _t, \
  141. .div_reg_off[0] = _r, \
  142. .div_shift[0] = _s, \
  143. .div_mask[0] = _m, \
  144. .can_mux = 1, \
  145. .dividers = 1, \
  146. }
  147. #define CLK_MUX_DD(_n, _mux, _r0, _r1, _s0, _s1) \
  148. { \
  149. .name = #_n, \
  150. .mux_shift = _mux, \
  151. .div_table[0] = div_table6, \
  152. .div_table[1] = div_table6, \
  153. .div_reg_off[0] = _r0, \
  154. .div_reg_off[1] = _r1, \
  155. .div_shift[0] = _s0, \
  156. .div_shift[1] = _s1, \
  157. .div_mask[0] = 7, \
  158. .div_mask[1] = 7, \
  159. .can_mux = 1, \
  160. .dividers = 2, \
  161. }
  162. /* NB periph clocks */
  163. static const struct clk_periph clks_nb[] = {
  164. CLK_FULL_DD(mmc, 2, 0, DIV_SEL2, DIV_SEL2, 16, 13),
  165. CLK_FULL_DD(sata_host, 3, 2, DIV_SEL2, DIV_SEL2, 10, 7),
  166. CLK_FULL_DD(sec_at, 6, 4, DIV_SEL1, DIV_SEL1, 3, 0),
  167. CLK_FULL_DD(sec_dap, 7, 6, DIV_SEL1, DIV_SEL1, 9, 6),
  168. CLK_FULL_DD(tscem, 8, 8, DIV_SEL1, DIV_SEL1, 15, 12),
  169. CLK_FULL(tscem_tmx, 10, 10, DIV_SEL1, 18, 7, div_table6),
  170. CLK_GATE(avs, 11, "xtal"),
  171. CLK_FULL_DD(sqf, 12, 12, DIV_SEL1, DIV_SEL1, 27, 24),
  172. CLK_FULL_DD(pwm, 13, 14, DIV_SEL0, DIV_SEL0, 3, 0),
  173. CLK_GATE(i2c_2, 16, "xtal"),
  174. CLK_GATE(i2c_1, 17, "xtal"),
  175. CLK_GATE_DIV(ddr_phy, 19, DIV_SEL0, 18, 1, div_table2, "TBG-A-S"),
  176. CLK_FULL_DD(ddr_fclk, 21, 16, DIV_SEL0, DIV_SEL0, 15, 12),
  177. CLK_FULL(trace, 22, 18, DIV_SEL0, 20, 7, div_table6),
  178. CLK_FULL(counter, 23, 20, DIV_SEL0, 23, 7, div_table6),
  179. CLK_FULL_DD(eip97, 24, 24, DIV_SEL2, DIV_SEL2, 22, 19),
  180. CLK_MUX_DIV(cpu, 22, DIV_SEL0, 28, 7, div_table6),
  181. { },
  182. };
  183. /* SB periph clocks */
  184. static const struct clk_periph clks_sb[] = {
  185. CLK_MUX_DD(gbe_50, 6, DIV_SEL2, DIV_SEL2, 6, 9),
  186. CLK_MUX_DD(gbe_core, 8, DIV_SEL1, DIV_SEL1, 18, 21),
  187. CLK_MUX_DD(gbe_125, 10, DIV_SEL1, DIV_SEL1, 6, 9),
  188. CLK_GATE(gbe1_50, 0, "gbe_50"),
  189. CLK_GATE(gbe0_50, 1, "gbe_50"),
  190. CLK_GATE(gbe1_125, 2, "gbe_125"),
  191. CLK_GATE(gbe0_125, 3, "gbe_125"),
  192. CLK_GATE_DIV(gbe1_core, 4, DIV_SEL1, 13, 1, div_table1, "gbe_core"),
  193. CLK_GATE_DIV(gbe0_core, 5, DIV_SEL1, 14, 1, div_table1, "gbe_core"),
  194. CLK_GATE_DIV(gbe_bm, 12, DIV_SEL1, 0, 1, div_table1, "gbe_core"),
  195. CLK_FULL_DD(sdio, 11, 14, DIV_SEL0, DIV_SEL0, 3, 6),
  196. CLK_FULL_DD(usb32_usb2_sys, 16, 16, DIV_SEL0, DIV_SEL0, 9, 12),
  197. CLK_FULL_DD(usb32_ss_sys, 17, 18, DIV_SEL0, DIV_SEL0, 15, 18),
  198. { },
  199. };
  200. static inline int get_mux(struct a37xx_periphclk *priv, int shift)
  201. {
  202. return (readl(priv->reg + TBG_SEL) >> shift) & 3;
  203. }
  204. static ulong periph_clk_get_rate(struct a37xx_periphclk *priv, int id);
  205. static ulong get_parent_rate(struct a37xx_periphclk *priv, int id)
  206. {
  207. const struct clk_periph *clk = &priv->clks[id];
  208. ulong res;
  209. if (clk->can_mux) {
  210. /* parent is one of TBG clocks */
  211. int tbg = get_mux(priv, clk->mux_shift);
  212. res = priv->parents[tbg];
  213. } else if (priv->clk_has_periph_parent[id]) {
  214. /* parent is one of other periph clocks */
  215. if (priv->clk_parent[id] >= priv->count)
  216. return -EINVAL;
  217. res = periph_clk_get_rate(priv, priv->clk_parent[id]);
  218. } else {
  219. /* otherwise parent is one of TBGs or XTAL */
  220. if (priv->clk_parent[id] >= MAX_PARENTS)
  221. return -EINVAL;
  222. res = priv->parents[priv->clk_parent[id]];
  223. }
  224. return res;
  225. }
  226. static ulong get_div(struct a37xx_periphclk *priv,
  227. const struct clk_periph *clk, int idx)
  228. {
  229. const struct clk_div_table *i;
  230. u32 reg;
  231. reg = readl(priv->reg + clk->div_reg_off[idx]);
  232. reg = (reg >> clk->div_shift[idx]) & clk->div_mask[idx];
  233. /* find divisor for register value val */
  234. for (i = clk->div_table[idx]; i && i->div != 0; ++i)
  235. if (i->val == reg)
  236. return i->div;
  237. return 0;
  238. }
  239. static ulong periph_clk_get_rate(struct a37xx_periphclk *priv, int id)
  240. {
  241. const struct clk_periph *clk = &priv->clks[id];
  242. ulong rate, div;
  243. int i;
  244. rate = get_parent_rate(priv, id);
  245. if (rate == -EINVAL)
  246. return -EINVAL;
  247. /* divide the parent rate by dividers */
  248. div = 1;
  249. for (i = 0; i < clk->dividers; ++i)
  250. div *= get_div(priv, clk, i);
  251. if (!div)
  252. return 0;
  253. return DIV_ROUND_UP(rate, div);
  254. }
  255. static ulong armada_37xx_periph_clk_get_rate(struct clk *clk)
  256. {
  257. struct a37xx_periphclk *priv = dev_get_priv(clk->dev);
  258. if (clk->id >= priv->count)
  259. return -EINVAL;
  260. return periph_clk_get_rate(priv, clk->id);
  261. }
  262. static int periph_clk_enable(struct clk *clk, int enable)
  263. {
  264. struct a37xx_periphclk *priv = dev_get_priv(clk->dev);
  265. const struct clk_periph *periph_clk = &priv->clks[clk->id];
  266. if (clk->id >= priv->count)
  267. return -EINVAL;
  268. if (!periph_clk->can_gate)
  269. return -ENOTSUPP;
  270. if (enable)
  271. clrbits_le32(priv->reg + CLK_DIS, periph_clk->disable_bit);
  272. else
  273. setbits_le32(priv->reg + CLK_DIS, periph_clk->disable_bit);
  274. return 0;
  275. }
  276. static int armada_37xx_periph_clk_enable(struct clk *clk)
  277. {
  278. return periph_clk_enable(clk, 1);
  279. }
  280. static int armada_37xx_periph_clk_disable(struct clk *clk)
  281. {
  282. return periph_clk_enable(clk, 0);
  283. }
  284. #if defined(CONFIG_CMD_CLK) && defined(CONFIG_CLK_ARMADA_3720)
  285. static int armada_37xx_periph_clk_dump(struct udevice *dev)
  286. {
  287. struct a37xx_periphclk *priv = dev_get_priv(dev);
  288. const struct clk_periph *clks;
  289. int i;
  290. if (!priv)
  291. return -ENODEV;
  292. clks = priv->clks;
  293. for (i = 0; i < priv->count; ++i)
  294. printf(" %s at %lu Hz\n", clks[i].name,
  295. periph_clk_get_rate(priv, i));
  296. printf("\n");
  297. return 0;
  298. }
  299. static int clk_dump(const char *name, int (*func)(struct udevice *))
  300. {
  301. struct udevice *dev;
  302. if (uclass_get_device_by_name(UCLASS_CLK, name, &dev)) {
  303. printf("Cannot find device %s\n", name);
  304. return -ENODEV;
  305. }
  306. return func(dev);
  307. }
  308. int armada_37xx_tbg_clk_dump(struct udevice *);
  309. int soc_clk_dump(void)
  310. {
  311. printf(" xtal at %u000000 Hz\n\n", get_ref_clk());
  312. if (clk_dump("tbg@13200", armada_37xx_tbg_clk_dump))
  313. return 1;
  314. if (clk_dump("nb-periph-clk@13000",
  315. armada_37xx_periph_clk_dump))
  316. return 1;
  317. if (clk_dump("sb-periph-clk@18000",
  318. armada_37xx_periph_clk_dump))
  319. return 1;
  320. return 0;
  321. }
  322. #endif
  323. static int armada_37xx_periph_clk_probe(struct udevice *dev)
  324. {
  325. struct a37xx_periphclk *priv = dev_get_priv(dev);
  326. const struct clk_periph *clks;
  327. int ret, i;
  328. clks = (const struct clk_periph *)dev_get_driver_data(dev);
  329. if (!clks)
  330. return -ENODEV;
  331. priv->reg = dev_read_addr_ptr(dev);
  332. if (!priv->reg) {
  333. dev_err(dev, "no io address\n");
  334. return -ENODEV;
  335. }
  336. /* count clk_periph nodes */
  337. priv->count = 0;
  338. while (clks[priv->count].name)
  339. priv->count++;
  340. priv->clks = clks;
  341. /* assign parent IDs to nodes which have non-NULL parent_name */
  342. for (i = 0; i < priv->count; ++i) {
  343. int j;
  344. if (!clks[i].parent_name)
  345. continue;
  346. /* first try if parent_name is one of TBGs or XTAL */
  347. for (j = 0; j < MAX_PARENTS; ++j)
  348. if (!strcmp(clks[i].parent_name,
  349. a37xx_periph_parent_names[j].name))
  350. break;
  351. if (j < MAX_PARENTS) {
  352. priv->clk_has_periph_parent[i] = false;
  353. priv->clk_parent[i] =
  354. a37xx_periph_parent_names[j].parent;
  355. continue;
  356. }
  357. /* else parent_name should be one of other periph clocks */
  358. for (j = 0; j < priv->count; ++j) {
  359. if (!strcmp(clks[i].parent_name, clks[j].name))
  360. break;
  361. }
  362. if (j < priv->count) {
  363. priv->clk_has_periph_parent[i] = true;
  364. priv->clk_parent[i] = j;
  365. continue;
  366. }
  367. dev_err(dev, "undefined parent %s\n", clks[i].parent_name);
  368. return -EINVAL;
  369. }
  370. for (i = 0; i < MAX_PARENTS; ++i) {
  371. struct clk clk;
  372. if (i == XTAL) {
  373. priv->parents[i] = get_ref_clk() * 1000000;
  374. continue;
  375. }
  376. ret = clk_get_by_index(dev, i, &clk);
  377. if (ret) {
  378. dev_err(dev, "one of parent clocks (%i) missing: %i\n",
  379. i, ret);
  380. return -ENODEV;
  381. }
  382. priv->parents[i] = clk_get_rate(&clk);
  383. clk_free(&clk);
  384. }
  385. return 0;
  386. }
  387. static const struct clk_ops armada_37xx_periph_clk_ops = {
  388. .get_rate = armada_37xx_periph_clk_get_rate,
  389. .enable = armada_37xx_periph_clk_enable,
  390. .disable = armada_37xx_periph_clk_disable,
  391. };
  392. static const struct udevice_id armada_37xx_periph_clk_ids[] = {
  393. {
  394. .compatible = "marvell,armada-3700-periph-clock-nb",
  395. .data = (ulong)clks_nb,
  396. },
  397. {
  398. .compatible = "marvell,armada-3700-periph-clock-sb",
  399. .data = (ulong)clks_sb,
  400. },
  401. {}
  402. };
  403. U_BOOT_DRIVER(armada_37xx_periph_clk) = {
  404. .name = "armada_37xx_periph_clk",
  405. .id = UCLASS_CLK,
  406. .of_match = armada_37xx_periph_clk_ids,
  407. .ops = &armada_37xx_periph_clk_ops,
  408. .priv_auto_alloc_size = sizeof(struct a37xx_periphclk),
  409. .probe = armada_37xx_periph_clk_probe,
  410. };