clk-flexgen.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * clk-flexgen.c
  4. *
  5. * Copyright (C) ST-Microelectronics SA 2013
  6. * Author: Maxime Coquelin <maxime.coquelin@st.com> for ST-Microelectronics.
  7. */
  8. #include <linux/clk.h>
  9. #include <linux/clk-provider.h>
  10. #include <linux/module.h>
  11. #include <linux/slab.h>
  12. #include <linux/io.h>
  13. #include <linux/err.h>
  14. #include <linux/string.h>
  15. #include <linux/of.h>
  16. #include <linux/of_address.h>
  17. struct clkgen_clk_out {
  18. const char *name;
  19. unsigned long flags;
  20. };
  21. struct clkgen_data {
  22. unsigned long flags;
  23. bool mode;
  24. const struct clkgen_clk_out *outputs;
  25. const unsigned int outputs_nb;
  26. };
  27. struct flexgen {
  28. struct clk_hw hw;
  29. /* Crossbar */
  30. struct clk_mux mux;
  31. /* Pre-divisor's gate */
  32. struct clk_gate pgate;
  33. /* Pre-divisor */
  34. struct clk_divider pdiv;
  35. /* Final divisor's gate */
  36. struct clk_gate fgate;
  37. /* Final divisor */
  38. struct clk_divider fdiv;
  39. /* Asynchronous mode control */
  40. struct clk_gate sync;
  41. /* hw control flags */
  42. bool control_mode;
  43. };
  44. #define to_flexgen(_hw) container_of(_hw, struct flexgen, hw)
  45. #define to_clk_gate(_hw) container_of(_hw, struct clk_gate, hw)
  46. static int flexgen_enable(struct clk_hw *hw)
  47. {
  48. struct flexgen *flexgen = to_flexgen(hw);
  49. struct clk_hw *pgate_hw = &flexgen->pgate.hw;
  50. struct clk_hw *fgate_hw = &flexgen->fgate.hw;
  51. __clk_hw_set_clk(pgate_hw, hw);
  52. __clk_hw_set_clk(fgate_hw, hw);
  53. clk_gate_ops.enable(pgate_hw);
  54. clk_gate_ops.enable(fgate_hw);
  55. pr_debug("%s: flexgen output enabled\n", clk_hw_get_name(hw));
  56. return 0;
  57. }
  58. static void flexgen_disable(struct clk_hw *hw)
  59. {
  60. struct flexgen *flexgen = to_flexgen(hw);
  61. struct clk_hw *fgate_hw = &flexgen->fgate.hw;
  62. /* disable only the final gate */
  63. __clk_hw_set_clk(fgate_hw, hw);
  64. clk_gate_ops.disable(fgate_hw);
  65. pr_debug("%s: flexgen output disabled\n", clk_hw_get_name(hw));
  66. }
  67. static int flexgen_is_enabled(struct clk_hw *hw)
  68. {
  69. struct flexgen *flexgen = to_flexgen(hw);
  70. struct clk_hw *fgate_hw = &flexgen->fgate.hw;
  71. __clk_hw_set_clk(fgate_hw, hw);
  72. if (!clk_gate_ops.is_enabled(fgate_hw))
  73. return 0;
  74. return 1;
  75. }
  76. static u8 flexgen_get_parent(struct clk_hw *hw)
  77. {
  78. struct flexgen *flexgen = to_flexgen(hw);
  79. struct clk_hw *mux_hw = &flexgen->mux.hw;
  80. __clk_hw_set_clk(mux_hw, hw);
  81. return clk_mux_ops.get_parent(mux_hw);
  82. }
  83. static int flexgen_set_parent(struct clk_hw *hw, u8 index)
  84. {
  85. struct flexgen *flexgen = to_flexgen(hw);
  86. struct clk_hw *mux_hw = &flexgen->mux.hw;
  87. __clk_hw_set_clk(mux_hw, hw);
  88. return clk_mux_ops.set_parent(mux_hw, index);
  89. }
  90. static inline unsigned long
  91. clk_best_div(unsigned long parent_rate, unsigned long rate)
  92. {
  93. return parent_rate / rate + ((rate > (2*(parent_rate % rate))) ? 0 : 1);
  94. }
  95. static int flexgen_determine_rate(struct clk_hw *hw,
  96. struct clk_rate_request *req)
  97. {
  98. unsigned long div;
  99. /* Round div according to exact prate and wished rate */
  100. div = clk_best_div(req->best_parent_rate, req->rate);
  101. if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) {
  102. req->best_parent_rate = req->rate * div;
  103. return 0;
  104. }
  105. req->rate = req->best_parent_rate / div;
  106. return 0;
  107. }
  108. static unsigned long flexgen_recalc_rate(struct clk_hw *hw,
  109. unsigned long parent_rate)
  110. {
  111. struct flexgen *flexgen = to_flexgen(hw);
  112. struct clk_hw *pdiv_hw = &flexgen->pdiv.hw;
  113. struct clk_hw *fdiv_hw = &flexgen->fdiv.hw;
  114. unsigned long mid_rate;
  115. __clk_hw_set_clk(pdiv_hw, hw);
  116. __clk_hw_set_clk(fdiv_hw, hw);
  117. mid_rate = clk_divider_ops.recalc_rate(pdiv_hw, parent_rate);
  118. return clk_divider_ops.recalc_rate(fdiv_hw, mid_rate);
  119. }
  120. static int flexgen_set_rate(struct clk_hw *hw, unsigned long rate,
  121. unsigned long parent_rate)
  122. {
  123. struct flexgen *flexgen = to_flexgen(hw);
  124. struct clk_hw *pdiv_hw = &flexgen->pdiv.hw;
  125. struct clk_hw *fdiv_hw = &flexgen->fdiv.hw;
  126. struct clk_hw *sync_hw = &flexgen->sync.hw;
  127. struct clk_gate *config = to_clk_gate(sync_hw);
  128. unsigned long div = 0;
  129. int ret = 0;
  130. u32 reg;
  131. __clk_hw_set_clk(pdiv_hw, hw);
  132. __clk_hw_set_clk(fdiv_hw, hw);
  133. if (flexgen->control_mode) {
  134. reg = readl(config->reg);
  135. reg &= ~BIT(config->bit_idx);
  136. writel(reg, config->reg);
  137. }
  138. div = clk_best_div(parent_rate, rate);
  139. /*
  140. * pdiv is mainly targeted for low freq results, while fdiv
  141. * should be used for div <= 64. The other way round can
  142. * lead to 'duty cycle' issues.
  143. */
  144. if (div <= 64) {
  145. clk_divider_ops.set_rate(pdiv_hw, parent_rate, parent_rate);
  146. ret = clk_divider_ops.set_rate(fdiv_hw, rate, rate * div);
  147. } else {
  148. clk_divider_ops.set_rate(fdiv_hw, parent_rate, parent_rate);
  149. ret = clk_divider_ops.set_rate(pdiv_hw, rate, rate * div);
  150. }
  151. return ret;
  152. }
  153. static const struct clk_ops flexgen_ops = {
  154. .enable = flexgen_enable,
  155. .disable = flexgen_disable,
  156. .is_enabled = flexgen_is_enabled,
  157. .get_parent = flexgen_get_parent,
  158. .set_parent = flexgen_set_parent,
  159. .determine_rate = flexgen_determine_rate,
  160. .recalc_rate = flexgen_recalc_rate,
  161. .set_rate = flexgen_set_rate,
  162. };
  163. static struct clk *clk_register_flexgen(const char *name,
  164. const char **parent_names, u8 num_parents,
  165. void __iomem *reg, spinlock_t *lock, u32 idx,
  166. unsigned long flexgen_flags, bool mode) {
  167. struct flexgen *fgxbar;
  168. struct clk *clk;
  169. struct clk_init_data init;
  170. u32 xbar_shift;
  171. void __iomem *xbar_reg, *fdiv_reg;
  172. fgxbar = kzalloc_obj(struct flexgen);
  173. if (!fgxbar)
  174. return ERR_PTR(-ENOMEM);
  175. init.name = name;
  176. init.ops = &flexgen_ops;
  177. init.flags = CLK_GET_RATE_NOCACHE | flexgen_flags;
  178. init.parent_names = parent_names;
  179. init.num_parents = num_parents;
  180. xbar_reg = reg + 0x18 + (idx & ~0x3);
  181. xbar_shift = (idx % 4) * 0x8;
  182. fdiv_reg = reg + 0x164 + idx * 4;
  183. /* Crossbar element config */
  184. fgxbar->mux.lock = lock;
  185. fgxbar->mux.mask = BIT(6) - 1;
  186. fgxbar->mux.reg = xbar_reg;
  187. fgxbar->mux.shift = xbar_shift;
  188. fgxbar->mux.table = NULL;
  189. /* Pre-divider's gate config (in xbar register)*/
  190. fgxbar->pgate.lock = lock;
  191. fgxbar->pgate.reg = xbar_reg;
  192. fgxbar->pgate.bit_idx = xbar_shift + 6;
  193. /* Pre-divider config */
  194. fgxbar->pdiv.lock = lock;
  195. fgxbar->pdiv.reg = reg + 0x58 + idx * 4;
  196. fgxbar->pdiv.width = 10;
  197. /* Final divider's gate config */
  198. fgxbar->fgate.lock = lock;
  199. fgxbar->fgate.reg = fdiv_reg;
  200. fgxbar->fgate.bit_idx = 6;
  201. /* Final divider config */
  202. fgxbar->fdiv.lock = lock;
  203. fgxbar->fdiv.reg = fdiv_reg;
  204. fgxbar->fdiv.width = 6;
  205. /* Final divider sync config */
  206. fgxbar->sync.lock = lock;
  207. fgxbar->sync.reg = fdiv_reg;
  208. fgxbar->sync.bit_idx = 7;
  209. fgxbar->control_mode = mode;
  210. fgxbar->hw.init = &init;
  211. clk = clk_register(NULL, &fgxbar->hw);
  212. if (IS_ERR(clk))
  213. kfree(fgxbar);
  214. else
  215. pr_debug("%s: parent %s rate %u\n",
  216. __clk_get_name(clk),
  217. __clk_get_name(clk_get_parent(clk)),
  218. (unsigned int)clk_get_rate(clk));
  219. return clk;
  220. }
  221. static const char ** __init flexgen_get_parents(struct device_node *np,
  222. int *num_parents)
  223. {
  224. const char **parents;
  225. unsigned int nparents;
  226. nparents = of_clk_get_parent_count(np);
  227. if (WARN_ON(!nparents))
  228. return NULL;
  229. parents = kcalloc(nparents, sizeof(const char *), GFP_KERNEL);
  230. if (!parents)
  231. return NULL;
  232. *num_parents = of_clk_parent_fill(np, parents, nparents);
  233. return parents;
  234. }
  235. static const struct clkgen_data clkgen_audio = {
  236. .flags = CLK_SET_RATE_PARENT,
  237. };
  238. static const struct clkgen_data clkgen_video = {
  239. .flags = CLK_SET_RATE_PARENT,
  240. .mode = 1,
  241. };
  242. static const struct clkgen_clk_out clkgen_stih410_a0_clk_out[] = {
  243. /* Those clks need to be on so that memory interface is accessible */
  244. { .name = "clk-ic-lmi0", .flags = CLK_IS_CRITICAL },
  245. { .name = "clk-ic-lmi1", .flags = CLK_IS_CRITICAL },
  246. };
  247. static const struct clkgen_data clkgen_stih410_a0 = {
  248. .outputs = clkgen_stih410_a0_clk_out,
  249. .outputs_nb = ARRAY_SIZE(clkgen_stih410_a0_clk_out),
  250. };
  251. static const struct clkgen_clk_out clkgen_stih410_c0_clk_out[] = {
  252. { .name = "clk-icn-gpu", },
  253. { .name = "clk-fdma", },
  254. { .name = "clk-nand", },
  255. { .name = "clk-hva", },
  256. { .name = "clk-proc-stfe", },
  257. { .name = "clk-proc-tp", },
  258. { .name = "clk-rx-icn-dmu", },
  259. { .name = "clk-rx-icn-hva", },
  260. /* This clk needs to be on to keep bus interconnect alive */
  261. { .name = "clk-icn-cpu", .flags = CLK_IS_CRITICAL },
  262. /* This clk needs to be on to keep bus interconnect alive */
  263. { .name = "clk-tx-icn-dmu", .flags = CLK_IS_CRITICAL },
  264. { .name = "clk-mmc-0", },
  265. { .name = "clk-mmc-1", },
  266. { .name = "clk-jpegdec", },
  267. /* This clk needs to be on to keep A9 running */
  268. { .name = "clk-ext2fa9", .flags = CLK_IS_CRITICAL },
  269. { .name = "clk-ic-bdisp-0", },
  270. { .name = "clk-ic-bdisp-1", },
  271. { .name = "clk-pp-dmu", },
  272. { .name = "clk-vid-dmu", },
  273. { .name = "clk-dss-lpc", },
  274. { .name = "clk-st231-aud-0", },
  275. { .name = "clk-st231-gp-1", },
  276. { .name = "clk-st231-dmu", },
  277. /* This clk needs to be on to keep bus interconnect alive */
  278. { .name = "clk-icn-lmi", .flags = CLK_IS_CRITICAL },
  279. { .name = "clk-tx-icn-disp-1", },
  280. /* This clk needs to be on to keep bus interconnect alive */
  281. { .name = "clk-icn-sbc", .flags = CLK_IS_CRITICAL },
  282. { .name = "clk-stfe-frc2", },
  283. { .name = "clk-eth-phy", },
  284. { .name = "clk-eth-ref-phyclk", },
  285. { .name = "clk-flash-promip", },
  286. { .name = "clk-main-disp", },
  287. { .name = "clk-aux-disp", },
  288. { .name = "clk-compo-dvp", },
  289. { .name = "clk-tx-icn-hades", },
  290. { .name = "clk-rx-icn-hades", },
  291. /* This clk needs to be on to keep bus interconnect alive */
  292. { .name = "clk-icn-reg-16", .flags = CLK_IS_CRITICAL },
  293. { .name = "clk-pp-hades", },
  294. { .name = "clk-clust-hades", },
  295. { .name = "clk-hwpe-hades", },
  296. { .name = "clk-fc-hades", },
  297. };
  298. static const struct clkgen_data clkgen_stih410_c0 = {
  299. .outputs = clkgen_stih410_c0_clk_out,
  300. .outputs_nb = ARRAY_SIZE(clkgen_stih410_c0_clk_out),
  301. };
  302. static const struct clkgen_clk_out clkgen_stih418_c0_clk_out[] = {
  303. { .name = "clk-icn-gpu", },
  304. { .name = "clk-fdma", },
  305. { .name = "clk-nand", },
  306. { .name = "clk-hva", },
  307. { .name = "clk-proc-stfe", },
  308. { .name = "clk-tp", },
  309. /* This clk needs to be on to keep bus interconnect alive */
  310. { .name = "clk-rx-icn-dmu", .flags = CLK_IS_CRITICAL },
  311. /* This clk needs to be on to keep bus interconnect alive */
  312. { .name = "clk-rx-icn-hva", .flags = CLK_IS_CRITICAL },
  313. { .name = "clk-icn-cpu", .flags = CLK_IS_CRITICAL },
  314. /* This clk needs to be on to keep bus interconnect alive */
  315. { .name = "clk-tx-icn-dmu", .flags = CLK_IS_CRITICAL },
  316. { .name = "clk-mmc-0", },
  317. { .name = "clk-mmc-1", },
  318. { .name = "clk-jpegdec", },
  319. /* This clk needs to be on to keep bus interconnect alive */
  320. { .name = "clk-icn-reg", .flags = CLK_IS_CRITICAL },
  321. { .name = "clk-proc-bdisp-0", },
  322. { .name = "clk-proc-bdisp-1", },
  323. { .name = "clk-pp-dmu", },
  324. { .name = "clk-vid-dmu", },
  325. { .name = "clk-dss-lpc", },
  326. { .name = "clk-st231-aud-0", },
  327. { .name = "clk-st231-gp-1", },
  328. { .name = "clk-st231-dmu", },
  329. /* This clk needs to be on to keep bus interconnect alive */
  330. { .name = "clk-icn-lmi", .flags = CLK_IS_CRITICAL },
  331. /* This clk needs to be on to keep bus interconnect alive */
  332. { .name = "clk-tx-icn-1", .flags = CLK_IS_CRITICAL },
  333. /* This clk needs to be on to keep bus interconnect alive */
  334. { .name = "clk-icn-sbc", .flags = CLK_IS_CRITICAL },
  335. { .name = "clk-stfe-frc2", },
  336. { .name = "clk-eth-phyref", },
  337. { .name = "clk-eth-ref-phyclk", },
  338. { .name = "clk-flash-promip", },
  339. { .name = "clk-main-disp", },
  340. { .name = "clk-aux-disp", },
  341. { .name = "clk-compo-dvp", },
  342. /* This clk needs to be on to keep bus interconnect alive */
  343. { .name = "clk-tx-icn-hades", .flags = CLK_IS_CRITICAL },
  344. /* This clk needs to be on to keep bus interconnect alive */
  345. { .name = "clk-rx-icn-hades", .flags = CLK_IS_CRITICAL },
  346. /* This clk needs to be on to keep bus interconnect alive */
  347. { .name = "clk-icn-reg-16", .flags = CLK_IS_CRITICAL },
  348. { .name = "clk-pp-hevc", },
  349. { .name = "clk-clust-hevc", },
  350. { .name = "clk-hwpe-hevc", },
  351. { .name = "clk-fc-hevc", },
  352. { .name = "clk-proc-mixer", },
  353. { .name = "clk-proc-sc", },
  354. { .name = "clk-avsp-hevc", },
  355. };
  356. static const struct clkgen_data clkgen_stih418_c0 = {
  357. .outputs = clkgen_stih418_c0_clk_out,
  358. .outputs_nb = ARRAY_SIZE(clkgen_stih418_c0_clk_out),
  359. };
  360. static const struct clkgen_clk_out clkgen_stih410_d0_clk_out[] = {
  361. { .name = "clk-pcm-0", },
  362. { .name = "clk-pcm-1", },
  363. { .name = "clk-pcm-2", },
  364. { .name = "clk-spdiff", },
  365. { .name = "clk-pcmr10-master", },
  366. { .name = "clk-usb2-phy", },
  367. };
  368. static const struct clkgen_data clkgen_stih410_d0 = {
  369. .flags = CLK_SET_RATE_PARENT,
  370. .outputs = clkgen_stih410_d0_clk_out,
  371. .outputs_nb = ARRAY_SIZE(clkgen_stih410_d0_clk_out),
  372. };
  373. static const struct clkgen_clk_out clkgen_stih407_d2_clk_out[] = {
  374. { .name = "clk-pix-main-disp", },
  375. { .name = "clk-pix-pip", },
  376. { .name = "clk-pix-gdp1", },
  377. { .name = "clk-pix-gdp2", },
  378. { .name = "clk-pix-gdp3", },
  379. { .name = "clk-pix-gdp4", },
  380. { .name = "clk-pix-aux-disp", },
  381. { .name = "clk-denc", },
  382. { .name = "clk-pix-hddac", },
  383. { .name = "clk-hddac", },
  384. { .name = "clk-sddac", },
  385. { .name = "clk-pix-dvo", },
  386. { .name = "clk-dvo", },
  387. { .name = "clk-pix-hdmi", },
  388. { .name = "clk-tmds-hdmi", },
  389. { .name = "clk-ref-hdmiphy", },
  390. };
  391. static const struct clkgen_data clkgen_stih407_d2 = {
  392. .outputs = clkgen_stih407_d2_clk_out,
  393. .outputs_nb = ARRAY_SIZE(clkgen_stih407_d2_clk_out),
  394. .flags = CLK_SET_RATE_PARENT,
  395. .mode = 1,
  396. };
  397. static const struct clkgen_clk_out clkgen_stih418_d2_clk_out[] = {
  398. { .name = "clk-pix-main-disp", },
  399. { .name = "", },
  400. { .name = "", },
  401. { .name = "", },
  402. { .name = "", },
  403. { .name = "clk-tmds-hdmi-div2", },
  404. { .name = "clk-pix-aux-disp", },
  405. { .name = "clk-denc", },
  406. { .name = "clk-pix-hddac", },
  407. { .name = "clk-hddac", },
  408. { .name = "clk-sddac", },
  409. { .name = "clk-pix-dvo", },
  410. { .name = "clk-dvo", },
  411. { .name = "clk-pix-hdmi", },
  412. { .name = "clk-tmds-hdmi", },
  413. { .name = "clk-ref-hdmiphy", },
  414. { .name = "", }, { .name = "", }, { .name = "", }, { .name = "", },
  415. { .name = "", }, { .name = "", }, { .name = "", }, { .name = "", },
  416. { .name = "", }, { .name = "", }, { .name = "", }, { .name = "", },
  417. { .name = "", }, { .name = "", }, { .name = "", }, { .name = "", },
  418. { .name = "", }, { .name = "", }, { .name = "", }, { .name = "", },
  419. { .name = "", }, { .name = "", }, { .name = "", }, { .name = "", },
  420. { .name = "", }, { .name = "", }, { .name = "", }, { .name = "", },
  421. { .name = "", }, { .name = "", }, { .name = "", },
  422. { .name = "clk-vp9", },
  423. };
  424. static const struct clkgen_data clkgen_stih418_d2 = {
  425. .outputs = clkgen_stih418_d2_clk_out,
  426. .outputs_nb = ARRAY_SIZE(clkgen_stih418_d2_clk_out),
  427. .flags = CLK_SET_RATE_PARENT,
  428. .mode = 1,
  429. };
  430. static const struct clkgen_clk_out clkgen_stih407_d3_clk_out[] = {
  431. { .name = "clk-stfe-frc1", },
  432. { .name = "clk-tsout-0", },
  433. { .name = "clk-tsout-1", },
  434. { .name = "clk-mchi", },
  435. { .name = "clk-vsens-compo", },
  436. { .name = "clk-frc1-remote", },
  437. { .name = "clk-lpc-0", },
  438. { .name = "clk-lpc-1", },
  439. };
  440. static const struct clkgen_data clkgen_stih407_d3 = {
  441. .outputs = clkgen_stih407_d3_clk_out,
  442. .outputs_nb = ARRAY_SIZE(clkgen_stih407_d3_clk_out),
  443. };
  444. static const struct of_device_id flexgen_of_match[] = {
  445. {
  446. .compatible = "st,flexgen-audio",
  447. .data = &clkgen_audio,
  448. },
  449. {
  450. .compatible = "st,flexgen-video",
  451. .data = &clkgen_video,
  452. },
  453. {
  454. .compatible = "st,flexgen-stih410-a0",
  455. .data = &clkgen_stih410_a0,
  456. },
  457. {
  458. .compatible = "st,flexgen-stih410-c0",
  459. .data = &clkgen_stih410_c0,
  460. },
  461. {
  462. .compatible = "st,flexgen-stih418-c0",
  463. .data = &clkgen_stih418_c0,
  464. },
  465. {
  466. .compatible = "st,flexgen-stih410-d0",
  467. .data = &clkgen_stih410_d0,
  468. },
  469. {
  470. .compatible = "st,flexgen-stih407-d2",
  471. .data = &clkgen_stih407_d2,
  472. },
  473. {
  474. .compatible = "st,flexgen-stih418-d2",
  475. .data = &clkgen_stih418_d2,
  476. },
  477. {
  478. .compatible = "st,flexgen-stih407-d3",
  479. .data = &clkgen_stih407_d3,
  480. },
  481. {}
  482. };
  483. static void __init st_of_flexgen_setup(struct device_node *np)
  484. {
  485. struct device_node *pnode;
  486. void __iomem *reg;
  487. struct clk_onecell_data *clk_data;
  488. const char **parents;
  489. int num_parents, i;
  490. spinlock_t *rlock = NULL;
  491. const struct of_device_id *match;
  492. struct clkgen_data *data = NULL;
  493. unsigned long flex_flags = 0;
  494. int ret;
  495. bool clk_mode = 0;
  496. const char *clk_name;
  497. pnode = of_get_parent(np);
  498. if (!pnode)
  499. return;
  500. reg = of_iomap(pnode, 0);
  501. of_node_put(pnode);
  502. if (!reg)
  503. return;
  504. parents = flexgen_get_parents(np, &num_parents);
  505. if (!parents) {
  506. iounmap(reg);
  507. return;
  508. }
  509. match = of_match_node(flexgen_of_match, np);
  510. if (match) {
  511. data = (struct clkgen_data *)match->data;
  512. flex_flags = data->flags;
  513. clk_mode = data->mode;
  514. }
  515. clk_data = kzalloc_obj(*clk_data);
  516. if (!clk_data)
  517. goto err;
  518. /* First try to get output information from the compatible data */
  519. if (!data || !data->outputs_nb || !data->outputs) {
  520. ret = of_property_count_strings(np, "clock-output-names");
  521. if (ret <= 0) {
  522. pr_err("%s: Failed to get number of output clocks (%d)",
  523. __func__, clk_data->clk_num);
  524. goto err;
  525. }
  526. clk_data->clk_num = ret;
  527. } else
  528. clk_data->clk_num = data->outputs_nb;
  529. clk_data->clks = kzalloc_objs(struct clk *, clk_data->clk_num);
  530. if (!clk_data->clks)
  531. goto err;
  532. rlock = kzalloc_obj(spinlock_t);
  533. if (!rlock)
  534. goto err;
  535. spin_lock_init(rlock);
  536. for (i = 0; i < clk_data->clk_num; i++) {
  537. struct clk *clk;
  538. if (!data || !data->outputs_nb || !data->outputs) {
  539. if (of_property_read_string_index(np,
  540. "clock-output-names",
  541. i, &clk_name))
  542. break;
  543. flex_flags &= ~CLK_IS_CRITICAL;
  544. of_clk_detect_critical(np, i, &flex_flags);
  545. } else {
  546. clk_name = data->outputs[i].name;
  547. flex_flags = data->flags | data->outputs[i].flags;
  548. }
  549. /*
  550. * If we read an empty clock name then the output is unused
  551. */
  552. if (*clk_name == '\0')
  553. continue;
  554. clk = clk_register_flexgen(clk_name, parents, num_parents,
  555. reg, rlock, i, flex_flags, clk_mode);
  556. if (IS_ERR(clk))
  557. goto err;
  558. clk_data->clks[i] = clk;
  559. }
  560. kfree(parents);
  561. of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
  562. return;
  563. err:
  564. iounmap(reg);
  565. if (clk_data)
  566. kfree(clk_data->clks);
  567. kfree(clk_data);
  568. kfree(parents);
  569. kfree(rlock);
  570. }
  571. CLK_OF_DECLARE(flexgen, "st,flexgen", st_of_flexgen_setup);