armada-37xx-periph.c 14 KB

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