clk-scpi.c 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. /*
  2. * System Control and Power Interface (SCPI) Protocol based clock driver
  3. *
  4. * Copyright (C) 2015 ARM Ltd.
  5. *
  6. * This program is free software; you can redistribute it and/or modify it
  7. * under the terms and conditions of the GNU General Public License,
  8. * version 2, as published by the Free Software Foundation.
  9. *
  10. * This program is distributed in the hope it will be useful, but WITHOUT
  11. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  13. * more details.
  14. *
  15. * You should have received a copy of the GNU General Public License along with
  16. * this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include <linux/clk-provider.h>
  19. #include <linux/device.h>
  20. #include <linux/err.h>
  21. #include <linux/of.h>
  22. #include <linux/module.h>
  23. #include <linux/of_platform.h>
  24. #include <linux/platform_device.h>
  25. #include <linux/scpi_protocol.h>
  26. struct scpi_clk {
  27. u32 id;
  28. struct clk_hw hw;
  29. struct scpi_dvfs_info *info;
  30. struct scpi_ops *scpi_ops;
  31. };
  32. #define to_scpi_clk(clk) container_of(clk, struct scpi_clk, hw)
  33. static struct platform_device *cpufreq_dev;
  34. static unsigned long scpi_clk_recalc_rate(struct clk_hw *hw,
  35. unsigned long parent_rate)
  36. {
  37. struct scpi_clk *clk = to_scpi_clk(hw);
  38. return clk->scpi_ops->clk_get_val(clk->id);
  39. }
  40. static long scpi_clk_round_rate(struct clk_hw *hw, unsigned long rate,
  41. unsigned long *parent_rate)
  42. {
  43. /*
  44. * We can't figure out what rate it will be, so just return the
  45. * rate back to the caller. scpi_clk_recalc_rate() will be called
  46. * after the rate is set and we'll know what rate the clock is
  47. * running at then.
  48. */
  49. return rate;
  50. }
  51. static int scpi_clk_set_rate(struct clk_hw *hw, unsigned long rate,
  52. unsigned long parent_rate)
  53. {
  54. struct scpi_clk *clk = to_scpi_clk(hw);
  55. return clk->scpi_ops->clk_set_val(clk->id, rate);
  56. }
  57. static const struct clk_ops scpi_clk_ops = {
  58. .recalc_rate = scpi_clk_recalc_rate,
  59. .round_rate = scpi_clk_round_rate,
  60. .set_rate = scpi_clk_set_rate,
  61. };
  62. /* find closest match to given frequency in OPP table */
  63. static long __scpi_dvfs_round_rate(struct scpi_clk *clk, unsigned long rate)
  64. {
  65. int idx;
  66. unsigned long fmin = 0, fmax = ~0, ftmp;
  67. const struct scpi_opp *opp = clk->info->opps;
  68. for (idx = 0; idx < clk->info->count; idx++, opp++) {
  69. ftmp = opp->freq;
  70. if (ftmp >= rate) {
  71. if (ftmp <= fmax)
  72. fmax = ftmp;
  73. break;
  74. } else if (ftmp >= fmin) {
  75. fmin = ftmp;
  76. }
  77. }
  78. return fmax != ~0 ? fmax : fmin;
  79. }
  80. static unsigned long scpi_dvfs_recalc_rate(struct clk_hw *hw,
  81. unsigned long parent_rate)
  82. {
  83. struct scpi_clk *clk = to_scpi_clk(hw);
  84. int idx = clk->scpi_ops->dvfs_get_idx(clk->id);
  85. const struct scpi_opp *opp;
  86. if (idx < 0)
  87. return 0;
  88. opp = clk->info->opps + idx;
  89. return opp->freq;
  90. }
  91. static long scpi_dvfs_round_rate(struct clk_hw *hw, unsigned long rate,
  92. unsigned long *parent_rate)
  93. {
  94. struct scpi_clk *clk = to_scpi_clk(hw);
  95. return __scpi_dvfs_round_rate(clk, rate);
  96. }
  97. static int __scpi_find_dvfs_index(struct scpi_clk *clk, unsigned long rate)
  98. {
  99. int idx, max_opp = clk->info->count;
  100. const struct scpi_opp *opp = clk->info->opps;
  101. for (idx = 0; idx < max_opp; idx++, opp++)
  102. if (opp->freq == rate)
  103. return idx;
  104. return -EINVAL;
  105. }
  106. static int scpi_dvfs_set_rate(struct clk_hw *hw, unsigned long rate,
  107. unsigned long parent_rate)
  108. {
  109. struct scpi_clk *clk = to_scpi_clk(hw);
  110. int ret = __scpi_find_dvfs_index(clk, rate);
  111. if (ret < 0)
  112. return ret;
  113. return clk->scpi_ops->dvfs_set_idx(clk->id, (u8)ret);
  114. }
  115. static const struct clk_ops scpi_dvfs_ops = {
  116. .recalc_rate = scpi_dvfs_recalc_rate,
  117. .round_rate = scpi_dvfs_round_rate,
  118. .set_rate = scpi_dvfs_set_rate,
  119. };
  120. static const struct of_device_id scpi_clk_match[] = {
  121. { .compatible = "arm,scpi-dvfs-clocks", .data = &scpi_dvfs_ops, },
  122. { .compatible = "arm,scpi-variable-clocks", .data = &scpi_clk_ops, },
  123. {}
  124. };
  125. static int
  126. scpi_clk_ops_init(struct device *dev, const struct of_device_id *match,
  127. struct scpi_clk *sclk, const char *name)
  128. {
  129. struct clk_init_data init;
  130. unsigned long min = 0, max = 0;
  131. int ret;
  132. init.name = name;
  133. init.flags = 0;
  134. init.num_parents = 0;
  135. init.ops = match->data;
  136. sclk->hw.init = &init;
  137. sclk->scpi_ops = get_scpi_ops();
  138. if (init.ops == &scpi_dvfs_ops) {
  139. sclk->info = sclk->scpi_ops->dvfs_get_info(sclk->id);
  140. if (IS_ERR(sclk->info))
  141. return PTR_ERR(sclk->info);
  142. } else if (init.ops == &scpi_clk_ops) {
  143. if (sclk->scpi_ops->clk_get_range(sclk->id, &min, &max) || !max)
  144. return -EINVAL;
  145. } else {
  146. return -EINVAL;
  147. }
  148. ret = devm_clk_hw_register(dev, &sclk->hw);
  149. if (!ret && max)
  150. clk_hw_set_rate_range(&sclk->hw, min, max);
  151. return ret;
  152. }
  153. struct scpi_clk_data {
  154. struct scpi_clk **clk;
  155. unsigned int clk_num;
  156. };
  157. static struct clk_hw *
  158. scpi_of_clk_src_get(struct of_phandle_args *clkspec, void *data)
  159. {
  160. struct scpi_clk *sclk;
  161. struct scpi_clk_data *clk_data = data;
  162. unsigned int idx = clkspec->args[0], count;
  163. for (count = 0; count < clk_data->clk_num; count++) {
  164. sclk = clk_data->clk[count];
  165. if (idx == sclk->id)
  166. return &sclk->hw;
  167. }
  168. return ERR_PTR(-EINVAL);
  169. }
  170. static int scpi_clk_add(struct device *dev, struct device_node *np,
  171. const struct of_device_id *match)
  172. {
  173. int idx, count, err;
  174. struct scpi_clk_data *clk_data;
  175. count = of_property_count_strings(np, "clock-output-names");
  176. if (count < 0) {
  177. dev_err(dev, "%s: invalid clock output count\n", np->name);
  178. return -EINVAL;
  179. }
  180. clk_data = devm_kmalloc(dev, sizeof(*clk_data), GFP_KERNEL);
  181. if (!clk_data)
  182. return -ENOMEM;
  183. clk_data->clk_num = count;
  184. clk_data->clk = devm_kcalloc(dev, count, sizeof(*clk_data->clk),
  185. GFP_KERNEL);
  186. if (!clk_data->clk)
  187. return -ENOMEM;
  188. for (idx = 0; idx < count; idx++) {
  189. struct scpi_clk *sclk;
  190. const char *name;
  191. u32 val;
  192. sclk = devm_kzalloc(dev, sizeof(*sclk), GFP_KERNEL);
  193. if (!sclk)
  194. return -ENOMEM;
  195. if (of_property_read_string_index(np, "clock-output-names",
  196. idx, &name)) {
  197. dev_err(dev, "invalid clock name @ %s\n", np->name);
  198. return -EINVAL;
  199. }
  200. if (of_property_read_u32_index(np, "clock-indices",
  201. idx, &val)) {
  202. dev_err(dev, "invalid clock index @ %s\n", np->name);
  203. return -EINVAL;
  204. }
  205. sclk->id = val;
  206. err = scpi_clk_ops_init(dev, match, sclk, name);
  207. if (err)
  208. dev_err(dev, "failed to register clock '%s'\n", name);
  209. else
  210. dev_dbg(dev, "Registered clock '%s'\n", name);
  211. clk_data->clk[idx] = sclk;
  212. }
  213. return of_clk_add_hw_provider(np, scpi_of_clk_src_get, clk_data);
  214. }
  215. static int scpi_clocks_remove(struct platform_device *pdev)
  216. {
  217. struct device *dev = &pdev->dev;
  218. struct device_node *child, *np = dev->of_node;
  219. if (cpufreq_dev) {
  220. platform_device_unregister(cpufreq_dev);
  221. cpufreq_dev = NULL;
  222. }
  223. for_each_available_child_of_node(np, child)
  224. of_clk_del_provider(np);
  225. return 0;
  226. }
  227. static int scpi_clocks_probe(struct platform_device *pdev)
  228. {
  229. int ret;
  230. struct device *dev = &pdev->dev;
  231. struct device_node *child, *np = dev->of_node;
  232. const struct of_device_id *match;
  233. if (!get_scpi_ops())
  234. return -ENXIO;
  235. for_each_available_child_of_node(np, child) {
  236. match = of_match_node(scpi_clk_match, child);
  237. if (!match)
  238. continue;
  239. ret = scpi_clk_add(dev, child, match);
  240. if (ret) {
  241. scpi_clocks_remove(pdev);
  242. of_node_put(child);
  243. return ret;
  244. }
  245. if (match->data != &scpi_dvfs_ops)
  246. continue;
  247. /* Add the virtual cpufreq device if it's DVFS clock provider */
  248. cpufreq_dev = platform_device_register_simple("scpi-cpufreq",
  249. -1, NULL, 0);
  250. if (IS_ERR(cpufreq_dev))
  251. pr_warn("unable to register cpufreq device");
  252. }
  253. return 0;
  254. }
  255. static const struct of_device_id scpi_clocks_ids[] = {
  256. { .compatible = "arm,scpi-clocks", },
  257. {}
  258. };
  259. MODULE_DEVICE_TABLE(of, scpi_clocks_ids);
  260. static struct platform_driver scpi_clocks_driver = {
  261. .driver = {
  262. .name = "scpi_clocks",
  263. .of_match_table = scpi_clocks_ids,
  264. },
  265. .probe = scpi_clocks_probe,
  266. .remove = scpi_clocks_remove,
  267. };
  268. module_platform_driver(scpi_clocks_driver);
  269. MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
  270. MODULE_DESCRIPTION("ARM SCPI clock driver");
  271. MODULE_LICENSE("GPL v2");