armada-37xx-periph.c 14 KB

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