0004-u54-prci-driver-for-core-U54-clocks.patch 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. From 883228d88d9ad6edc7ed9a4540926e80a22c560f Mon Sep 17 00:00:00 2001
  2. From: "Wesley W. Terpstra" <wesley@sifive.com>
  3. Date: Tue, 13 Feb 2018 19:39:41 -0800
  4. Subject: [PATCH 04/10] u54-prci: driver for core U54 clocks
  5. ---
  6. drivers/clk/sifive/u54-prci.c | 314 ++++++++++++++++++++++++++++++++++
  7. 1 file changed, 314 insertions(+)
  8. create mode 100644 drivers/clk/sifive/u54-prci.c
  9. diff --git a/drivers/clk/sifive/u54-prci.c b/drivers/clk/sifive/u54-prci.c
  10. new file mode 100644
  11. index 000000000000..edc4b7818e71
  12. --- /dev/null
  13. +++ b/drivers/clk/sifive/u54-prci.c
  14. @@ -0,0 +1,314 @@
  15. +/*
  16. + * This program is free software; you can redistribute it and/or modify
  17. + * it under the terms of the GNU General Public License version 2 as
  18. + * published by the Free Software Foundation.
  19. + *
  20. + * This program is distributed in the hope that it will be useful,
  21. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  22. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  23. + * GNU General Public License for more details.
  24. + *
  25. + * Copyright (C) 2018 SiFive, Inc.
  26. + */
  27. +
  28. +#include <linux/clkdev.h>
  29. +#include <linux/clk-provider.h>
  30. +#include <linux/clk.h>
  31. +#include <linux/err.h>
  32. +#include <linux/of.h>
  33. +#include <linux/platform_device.h>
  34. +#include <linux/slab.h>
  35. +#include <linux/log2.h>
  36. +
  37. +#define CORE_CLOCK 0
  38. +#define GEMTX_CLOCK 1
  39. +#define PRCI_CLOCKS 2
  40. +
  41. +#define MIN_REF 7000000UL
  42. +#define MAX_REF 200000000UL
  43. +#define MAX_PARENT 600000000UL
  44. +#define MAX_VCO 4800000000UL
  45. +#define MAX_DIV 64
  46. +#define MAX_R 64UL
  47. +
  48. +#define PLL_LOCK 0x80000000U
  49. +#define NAME_LEN 40
  50. +
  51. +struct sifive_u54_prci_driver;
  52. +
  53. +struct sifive_u54_prci_pll {
  54. + struct clk_hw hw;
  55. + struct sifive_u54_prci_driver *driver;
  56. + char name[NAME_LEN];
  57. + u32 freq;
  58. + u32 glcm;
  59. +};
  60. +
  61. +struct sifive_u54_prci_driver {
  62. + struct clk_onecell_data table;
  63. + struct clk *clks[PRCI_CLOCKS];
  64. + struct sifive_u54_prci_pll plls[PRCI_CLOCKS];
  65. + void __iomem *reg;
  66. +};
  67. +
  68. +#define to_sifive_u54_prci_pll(hw) container_of(hw, struct sifive_u54_prci_pll, hw)
  69. +
  70. +struct sifive_u54_pll_cfg {
  71. + unsigned long r, f, q, a;
  72. +};
  73. +
  74. +static struct sifive_u54_pll_cfg sifive_u54_pll_cfg(u32 reg)
  75. +{
  76. + struct sifive_u54_pll_cfg cfg;
  77. + cfg.r = (reg >> 0) & 0x3f;
  78. + cfg.f = (reg >> 6) & 0x1ff;
  79. + cfg.q = (reg >> 15) & 0x7;
  80. + cfg.a = (reg >> 18) & 0x7;
  81. + return cfg;
  82. +}
  83. +
  84. +static u32 sifive_u54_pll_reg(struct sifive_u54_pll_cfg cfg)
  85. +{
  86. + u32 reg = 0;
  87. + reg |= (cfg.r & 0x3f) << 0;
  88. + reg |= (cfg.f & 0x1ff) << 6;
  89. + reg |= (cfg.q & 0x7) << 15;
  90. + reg |= (cfg.a & 0x7) << 18;
  91. + reg |= 1<<25; // internal feedback
  92. + return reg;
  93. +}
  94. +
  95. +static unsigned long sifive_u54_pll_rate(struct sifive_u54_pll_cfg cfg, unsigned long parent)
  96. +{
  97. + return (parent*2*(cfg.f+1) / (cfg.r+1)) >> cfg.q;
  98. +}
  99. +
  100. +static struct sifive_u54_pll_cfg sifive_u54_pll_configure(unsigned long target, unsigned long parent)
  101. +{
  102. + struct sifive_u54_pll_cfg cfg;
  103. + unsigned long scale, ratio, best_delta, filter;
  104. + u32 max_r, best_r, best_f, r;
  105. +
  106. + /* Confirm input frequency is within bounds */
  107. + if (WARN_ON(parent > MAX_PARENT)) { parent = MAX_PARENT; }
  108. + if (WARN_ON(parent < MIN_REF)) { parent = MIN_REF; }
  109. +
  110. + /* Calculate the Q shift and target VCO */
  111. + scale = MAX_VCO / target;
  112. + if (scale <= 1) {
  113. + cfg.q = 1;
  114. + target = MAX_VCO;
  115. + } else if (scale > MAX_DIV) {
  116. + cfg.q = ilog2(MAX_DIV);
  117. + target = MAX_VCO/2;
  118. + } else {
  119. + cfg.q = ilog2(scale);
  120. + target = target << cfg.q;
  121. + }
  122. +
  123. + /* Precalcualte the target ratio */
  124. + ratio = (target << 20) / parent;
  125. +
  126. + /* Placeholder values */
  127. + best_r = 0;
  128. + best_f = 0;
  129. + best_delta = MAX_VCO;
  130. +
  131. + /* Consider all values for R which land within [MIN_REF, MAX_REF]; prefer smaller R */
  132. + max_r = min(MAX_R, parent / MIN_REF);
  133. + for (r = DIV_ROUND_UP(parent, MAX_REF); r <= max_r; ++r) {
  134. + /* What is the best F we can pick in this case? */
  135. + u32 f = (ratio*r + (1<<20)) >> 21;
  136. + unsigned long ref = parent / r;
  137. + unsigned long vco = ref * f * 2;
  138. + unsigned long delta;
  139. +
  140. + /* Ensure rounding didn't take us out of range */
  141. + if (vco > target) --f;
  142. + if (vco < MAX_VCO/2) ++f;
  143. + vco = ref * f * 2;
  144. +
  145. + delta = abs(target - vco);
  146. + if (delta < best_delta) {
  147. + best_delta = delta;
  148. + best_r = r;
  149. + best_f = f;
  150. + }
  151. + }
  152. +
  153. + cfg.r = best_r - 1;
  154. + cfg.f = best_f - 1;
  155. +
  156. + /* Pick the best PLL jitter filter */
  157. + filter = parent / best_r;
  158. + BUG_ON(filter < 7000000);
  159. + if (filter < 11000000) {
  160. + cfg.a = 1;
  161. + } else if (filter < 18000000) {
  162. + cfg.a = 2;
  163. + } else if (filter < 30000000) {
  164. + cfg.a = 3;
  165. + } else if (filter < 50000000) {
  166. + cfg.a = 4;
  167. + } else if (filter < 80000000) {
  168. + cfg.a = 5;
  169. + } else if (filter < 130000000) {
  170. + cfg.a = 6;
  171. + } else {
  172. + BUG_ON (filter > 200000000);
  173. + cfg.a = 7;
  174. + }
  175. +
  176. + return cfg;
  177. +}
  178. +
  179. +static unsigned long sifive_u54_prci_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
  180. +{
  181. + struct sifive_u54_prci_pll *pll = to_sifive_u54_prci_pll(hw);
  182. + struct sifive_u54_prci_driver *driver = pll->driver;
  183. +
  184. + u32 reg = ioread32(driver->reg + pll->freq);
  185. + struct sifive_u54_pll_cfg cfg = sifive_u54_pll_cfg(reg);
  186. +
  187. + return sifive_u54_pll_rate(cfg, parent_rate);
  188. +}
  189. +
  190. +static long sifive_u54_prci_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate)
  191. +{
  192. + struct sifive_u54_pll_cfg cfg = sifive_u54_pll_configure(rate, *parent_rate);
  193. + return sifive_u54_pll_rate(cfg, *parent_rate);
  194. +}
  195. +
  196. +static int sifive_u54_prci_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate)
  197. +{
  198. + struct sifive_u54_prci_pll *pll = to_sifive_u54_prci_pll(hw);
  199. + struct sifive_u54_prci_driver *driver = pll->driver;
  200. +
  201. + struct sifive_u54_pll_cfg cfg = sifive_u54_pll_configure(rate, parent_rate);
  202. + u32 reg = sifive_u54_pll_reg(cfg);
  203. +
  204. + /* Switch to reg clock and reconfigure PLL */
  205. + iowrite32(1, driver->reg + pll->glcm);
  206. + iowrite32(reg, driver->reg + pll->freq);
  207. +
  208. + /* Wait for lock and switch back to PLL */
  209. + while (!(ioread32(driver->reg + pll->freq) & PLL_LOCK));
  210. + iowrite32(0, driver->reg + pll->glcm);
  211. +
  212. + return 0;
  213. +}
  214. +
  215. +static const struct clk_ops sifive_u54_prci_ops_rw = {
  216. + .recalc_rate = sifive_u54_prci_recalc_rate,
  217. + .round_rate = sifive_u54_prci_round_rate,
  218. + .set_rate = sifive_u54_prci_set_rate,
  219. +};
  220. +
  221. +static const struct clk_ops sifive_u54_prci_ops_ro = {
  222. + .recalc_rate = sifive_u54_prci_recalc_rate,
  223. +};
  224. +
  225. +static ssize_t sifive_u54_pll_show(struct device *dev, struct device_attribute *attr, char *buf)
  226. +{
  227. + struct sifive_u54_prci_driver *driver = dev_get_drvdata(dev);
  228. + return sprintf(buf, "%ld", clk_get_rate(driver->clks[0]));
  229. +}
  230. +
  231. +static ssize_t sifive_u54_pll_rate_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
  232. +{
  233. + struct sifive_u54_prci_driver *driver = dev_get_drvdata(dev);
  234. + unsigned long rate;
  235. + char *endp;
  236. +
  237. + rate = simple_strtoul(buf, &endp, 0);
  238. + if (*endp != 0 && *endp != '\n')
  239. + return -EINVAL;
  240. +
  241. + clk_set_rate(driver->clks[0], rate);
  242. + return count;
  243. +}
  244. +
  245. +static DEVICE_ATTR(rate, 0644, sifive_u54_pll_show, sifive_u54_pll_rate_store);
  246. +
  247. +static int sifive_u54_prci_probe(struct platform_device *pdev)
  248. +{
  249. + struct device *dev = &pdev->dev;
  250. + struct clk_init_data init;
  251. + struct sifive_u54_prci_driver *driver;
  252. + struct resource *res;
  253. + const char *parent;
  254. + int i;
  255. +
  256. + parent = of_clk_get_parent_name(dev->of_node, 0);
  257. + if (!parent) {
  258. + dev_err(dev, "No OF parent clocks found\n");
  259. + return -EINVAL;
  260. + }
  261. +
  262. + driver = devm_kzalloc(dev, sizeof(*driver), GFP_KERNEL);
  263. + if (!driver) {
  264. + dev_err(dev, "Out of memory\n");
  265. + return -ENOMEM;
  266. + }
  267. +
  268. + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  269. + driver->reg = devm_ioremap_resource(dev, res);
  270. + if (IS_ERR(driver->reg))
  271. + return PTR_ERR(driver->reg);
  272. +
  273. + /* Link the data structure */
  274. + driver->table.clk_num = PRCI_CLOCKS;
  275. + driver->table.clks = &driver->clks[0];
  276. + dev_set_drvdata(dev, driver);
  277. +
  278. + /* Describe the clocks */
  279. + snprintf(driver->plls[CORE_CLOCK].name, NAME_LEN, "%s.core", dev->of_node->name);
  280. + driver->plls[CORE_CLOCK].freq = 0x4;
  281. + driver->plls[CORE_CLOCK].glcm = 0x24;
  282. + snprintf(driver->plls[GEMTX_CLOCK].name, NAME_LEN, "%s.gemtx", dev->of_node->name);
  283. + driver->plls[GEMTX_CLOCK].freq = 0x1c;
  284. + driver->plls[GEMTX_CLOCK].glcm = 0; /* None; cannot be set_rate */
  285. +
  286. + /* Export the clocks */
  287. + for (i = 0; i < PRCI_CLOCKS; ++i) {
  288. + init.name = &driver->plls[i].name[0];
  289. + init.ops = driver->plls[i].glcm ? &sifive_u54_prci_ops_rw : &sifive_u54_prci_ops_ro;
  290. + init.num_parents = 1;
  291. + init.parent_names = &parent;
  292. + init.flags = 0;
  293. +
  294. + driver->plls[i].driver = driver;
  295. + driver->plls[i].hw.init = &init;
  296. +
  297. + driver->clks[i] = devm_clk_register(dev, &driver->plls[i].hw);
  298. + if (IS_ERR(driver->clks[i])) {
  299. + dev_err(dev, "Failed to register clock %d, %ld\n", i, PTR_ERR(driver->clks[i]));
  300. + return PTR_ERR(driver->clks[i]);
  301. + }
  302. + }
  303. +
  304. + of_clk_add_provider(dev->of_node, of_clk_src_onecell_get, &driver->table);
  305. + device_create_file(dev, &dev_attr_rate);
  306. + dev_info(dev, "Registered U54 core clocks\n");
  307. +
  308. + return 0;
  309. +}
  310. +
  311. +static const struct of_device_id sifive_u54_prci_of_match[] = {
  312. + { .compatible = "sifive,aloeprci0", },
  313. + {}
  314. +};
  315. +
  316. +static struct platform_driver sifive_u54_prci_driver = {
  317. + .driver = {
  318. + .name = "sifive-u54-prci",
  319. + .of_match_table = sifive_u54_prci_of_match,
  320. + },
  321. + .probe = sifive_u54_prci_probe,
  322. +};
  323. +
  324. +static int __init sifive_u54_prci_init(void)
  325. +{
  326. + return platform_driver_register(&sifive_u54_prci_driver);
  327. +}
  328. +core_initcall(sifive_u54_prci_init);
  329. --
  330. 2.21.0