0055-cpufreq-mediatek-add-driver.patch 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. From 60f4e41b367bdb29530468c91c1e613b17a37755 Mon Sep 17 00:00:00 2001
  2. From: John Crispin <blogic@openwrt.org>
  3. Date: Wed, 30 Mar 2016 23:48:53 +0200
  4. Subject: [PATCH 055/102] cpufreq: mediatek: add driver
  5. Signed-off-by: John Crispin <john@phrozen.org>
  6. ---
  7. drivers/cpufreq/Kconfig.arm | 9 +
  8. drivers/cpufreq/Makefile | 1 +
  9. drivers/cpufreq/mt7623-cpufreq.c | 389 ++++++++++++++++++++++++++++++++++++++
  10. 3 files changed, 399 insertions(+)
  11. create mode 100644 drivers/cpufreq/mt7623-cpufreq.c
  12. --- a/drivers/cpufreq/Kconfig.arm
  13. +++ b/drivers/cpufreq/Kconfig.arm
  14. @@ -81,6 +81,15 @@ config ARM_KIRKWOOD_CPUFREQ
  15. This adds the CPUFreq driver for Marvell Kirkwood
  16. SoCs.
  17. +config ARM_MT7623_CPUFREQ
  18. + bool "Mediatek MT7623 CPUFreq support"
  19. + depends on ARCH_MEDIATEK && REGULATOR
  20. + depends on ARM || (ARM_CPU_TOPOLOGY && COMPILE_TEST)
  21. + depends on !CPU_THERMAL || THERMAL=y
  22. + select PM_OPP
  23. + help
  24. + This adds the CPUFreq driver support for Mediatek MT7623 SoC.
  25. +
  26. config ARM_MT8173_CPUFREQ
  27. bool "Mediatek MT8173 CPUFreq support"
  28. depends on ARCH_MEDIATEK && REGULATOR
  29. --- a/drivers/cpufreq/Makefile
  30. +++ b/drivers/cpufreq/Makefile
  31. @@ -57,6 +57,7 @@ obj-$(CONFIG_ARM_HISI_ACPU_CPUFREQ) += h
  32. obj-$(CONFIG_ARM_IMX6Q_CPUFREQ) += imx6q-cpufreq.o
  33. obj-$(CONFIG_ARM_INTEGRATOR) += integrator-cpufreq.o
  34. obj-$(CONFIG_ARM_KIRKWOOD_CPUFREQ) += kirkwood-cpufreq.o
  35. +obj-$(CONFIG_ARM_MT7623_CPUFREQ) += mt7623-cpufreq.o
  36. obj-$(CONFIG_ARM_MT8173_CPUFREQ) += mt8173-cpufreq.o
  37. obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o
  38. obj-$(CONFIG_ARM_PXA2xx_CPUFREQ) += pxa2xx-cpufreq.o
  39. --- /dev/null
  40. +++ b/drivers/cpufreq/mt7623-cpufreq.c
  41. @@ -0,0 +1,389 @@
  42. +/*
  43. + * Copyright (c) 2015 Linaro Ltd.
  44. + * Author: Pi-Cheng Chen <pi-cheng.chen@linaro.org>
  45. + *
  46. + * This program is free software; you can redistribute it and/or modify
  47. + * it under the terms of the GNU General Public License version 2 as
  48. + * published by the Free Software Foundation.
  49. + *
  50. + * This program is distributed in the hope that it will be useful,
  51. + * but WITHOUT ANY WARRANTY; without even the implied warranty of
  52. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  53. + * GNU General Public License for more details.
  54. + */
  55. +
  56. +#include <linux/clk.h>
  57. +#include <linux/cpu.h>
  58. +#include <linux/cpu_cooling.h>
  59. +#include <linux/cpufreq.h>
  60. +#include <linux/cpumask.h>
  61. +#include <linux/of.h>
  62. +#include <linux/platform_device.h>
  63. +#include <linux/pm_opp.h>
  64. +#include <linux/regulator/consumer.h>
  65. +#include <linux/slab.h>
  66. +#include <linux/thermal.h>
  67. +
  68. +#define VOLT_TOL (10000)
  69. +
  70. +/*
  71. + * When scaling the clock frequency of a CPU clock domain, the clock source
  72. + * needs to be switched to another stable PLL clock temporarily until
  73. + * the original PLL becomes stable at target frequency.
  74. + */
  75. +struct mtk_cpu_dvfs_info {
  76. + struct device *cpu_dev;
  77. + struct regulator *proc_reg;
  78. + struct clk *cpu_clk;
  79. + struct clk *inter_clk;
  80. + struct thermal_cooling_device *cdev;
  81. + int intermediate_voltage;
  82. +};
  83. +
  84. +static int mtk_cpufreq_set_voltage(struct mtk_cpu_dvfs_info *info, int vproc)
  85. +{
  86. + return regulator_set_voltage(info->proc_reg, vproc,
  87. + vproc + VOLT_TOL);
  88. +}
  89. +
  90. +static int mtk_cpufreq_set_target(struct cpufreq_policy *policy,
  91. + unsigned int index)
  92. +{
  93. + struct cpufreq_frequency_table *freq_table = policy->freq_table;
  94. + struct clk *cpu_clk = policy->clk;
  95. + struct clk *armpll = clk_get_parent(cpu_clk);
  96. + struct mtk_cpu_dvfs_info *info = policy->driver_data;
  97. + struct device *cpu_dev = info->cpu_dev;
  98. + struct dev_pm_opp *opp;
  99. + long freq_hz, old_freq_hz;
  100. + int vproc, old_vproc, inter_vproc, target_vproc, ret;
  101. +
  102. + inter_vproc = info->intermediate_voltage;
  103. +
  104. + old_freq_hz = clk_get_rate(cpu_clk);
  105. + old_vproc = regulator_get_voltage(info->proc_reg);
  106. +
  107. + freq_hz = freq_table[index].frequency * 1000;
  108. +
  109. + rcu_read_lock();
  110. + opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_hz);
  111. + if (IS_ERR(opp)) {
  112. + rcu_read_unlock();
  113. + pr_err("cpu%d: failed to find OPP for %ld\n",
  114. + policy->cpu, freq_hz);
  115. + return PTR_ERR(opp);
  116. + }
  117. + vproc = dev_pm_opp_get_voltage(opp);
  118. + rcu_read_unlock();
  119. +
  120. + /*
  121. + * If the new voltage or the intermediate voltage is higher than the
  122. + * current voltage, scale up voltage first.
  123. + */
  124. + target_vproc = (inter_vproc > vproc) ? inter_vproc : vproc;
  125. + if (old_vproc < target_vproc) {
  126. + ret = mtk_cpufreq_set_voltage(info, target_vproc);
  127. + if (ret) {
  128. + pr_err("cpu%d: failed to scale up voltage!\n",
  129. + policy->cpu);
  130. + mtk_cpufreq_set_voltage(info, old_vproc);
  131. + return ret;
  132. + }
  133. + }
  134. +
  135. + /* Reparent the CPU clock to intermediate clock. */
  136. + ret = clk_set_parent(cpu_clk, info->inter_clk);
  137. + if (ret) {
  138. + pr_err("cpu%d: failed to re-parent cpu clock!\n",
  139. + policy->cpu);
  140. + mtk_cpufreq_set_voltage(info, old_vproc);
  141. + WARN_ON(1);
  142. + return ret;
  143. + }
  144. +
  145. + /* Set the original PLL to target rate. */
  146. + ret = clk_set_rate(armpll, freq_hz);
  147. + if (ret) {
  148. + pr_err("cpu%d: failed to scale cpu clock rate!\n",
  149. + policy->cpu);
  150. + clk_set_parent(cpu_clk, armpll);
  151. + mtk_cpufreq_set_voltage(info, old_vproc);
  152. + return ret;
  153. + }
  154. +
  155. + /* Set parent of CPU clock back to the original PLL. */
  156. + ret = clk_set_parent(cpu_clk, armpll);
  157. + if (ret) {
  158. + pr_err("cpu%d: failed to re-parent cpu clock!\n",
  159. + policy->cpu);
  160. + mtk_cpufreq_set_voltage(info, inter_vproc);
  161. + WARN_ON(1);
  162. + return ret;
  163. + }
  164. +
  165. + /*
  166. + * If the new voltage is lower than the intermediate voltage or the
  167. + * original voltage, scale down to the new voltage.
  168. + */
  169. + if (vproc < inter_vproc || vproc < old_vproc) {
  170. + ret = mtk_cpufreq_set_voltage(info, vproc);
  171. + if (ret) {
  172. + pr_err("cpu%d: failed to scale down voltage!\n",
  173. + policy->cpu);
  174. + clk_set_parent(cpu_clk, info->inter_clk);
  175. + clk_set_rate(armpll, old_freq_hz);
  176. + clk_set_parent(cpu_clk, armpll);
  177. + return ret;
  178. + }
  179. + }
  180. +
  181. + return 0;
  182. +}
  183. +
  184. +static void mtk_cpufreq_ready(struct cpufreq_policy *policy)
  185. +{
  186. + struct mtk_cpu_dvfs_info *info = policy->driver_data;
  187. + struct device_node *np = of_node_get(info->cpu_dev->of_node);
  188. +
  189. + if (WARN_ON(!np))
  190. + return;
  191. +
  192. + if (of_find_property(np, "#cooling-cells", NULL)) {
  193. + info->cdev = of_cpufreq_cooling_register(np,
  194. + policy->related_cpus);
  195. +
  196. + if (IS_ERR(info->cdev)) {
  197. + dev_err(info->cpu_dev,
  198. + "running cpufreq without cooling device: %ld\n",
  199. + PTR_ERR(info->cdev));
  200. +
  201. + info->cdev = NULL;
  202. + }
  203. + }
  204. +
  205. + of_node_put(np);
  206. +}
  207. +
  208. +static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu)
  209. +{
  210. + struct device *cpu_dev;
  211. + struct regulator *proc_reg = ERR_PTR(-ENODEV);
  212. + struct clk *cpu_clk = ERR_PTR(-ENODEV);
  213. + struct clk *inter_clk = ERR_PTR(-ENODEV);
  214. + struct dev_pm_opp *opp;
  215. + unsigned long rate;
  216. + int ret;
  217. +
  218. + cpu_dev = get_cpu_device(cpu);
  219. + if (!cpu_dev) {
  220. + pr_err("failed to get cpu%d device\n", cpu);
  221. + return -ENODEV;
  222. + }
  223. +
  224. + cpu_clk = clk_get(cpu_dev, "cpu");
  225. + if (IS_ERR(cpu_clk)) {
  226. + if (PTR_ERR(cpu_clk) == -EPROBE_DEFER)
  227. + pr_warn("cpu clk for cpu%d not ready, retry.\n", cpu);
  228. + else
  229. + pr_err("failed to get cpu clk for cpu%d\n", cpu);
  230. +
  231. + ret = PTR_ERR(cpu_clk);
  232. + return ret;
  233. + }
  234. +
  235. + inter_clk = clk_get(cpu_dev, "intermediate");
  236. + if (IS_ERR(inter_clk)) {
  237. + if (PTR_ERR(inter_clk) == -EPROBE_DEFER)
  238. + pr_warn("intermediate clk for cpu%d not ready, retry.\n",
  239. + cpu);
  240. + else
  241. + pr_err("failed to get intermediate clk for cpu%d\n",
  242. + cpu);
  243. +
  244. + ret = PTR_ERR(inter_clk);
  245. + goto out_free_resources;
  246. + }
  247. +
  248. + proc_reg = regulator_get_exclusive(cpu_dev, "proc");
  249. + if (IS_ERR(proc_reg)) {
  250. + if (PTR_ERR(proc_reg) == -EPROBE_DEFER)
  251. + pr_warn("proc regulator for cpu%d not ready, retry.\n",
  252. + cpu);
  253. + else
  254. + pr_err("failed to get proc regulator for cpu%d\n",
  255. + cpu);
  256. +
  257. + ret = PTR_ERR(proc_reg);
  258. + goto out_free_resources;
  259. + }
  260. +
  261. + ret = dev_pm_opp_of_add_table(cpu_dev);
  262. + if (ret) {
  263. + pr_warn("no OPP table for cpu%d\n", cpu);
  264. + goto out_free_resources;
  265. + }
  266. +
  267. + /* Search a safe voltage for intermediate frequency. */
  268. + rate = clk_get_rate(inter_clk);
  269. + rcu_read_lock();
  270. + opp = dev_pm_opp_find_freq_ceil(cpu_dev, &rate);
  271. + if (IS_ERR(opp)) {
  272. + rcu_read_unlock();
  273. + pr_err("failed to get intermediate opp for cpu%d\n", cpu);
  274. + ret = PTR_ERR(opp);
  275. + goto out_free_opp_table;
  276. + }
  277. + info->intermediate_voltage = dev_pm_opp_get_voltage(opp);
  278. + rcu_read_unlock();
  279. +
  280. + info->cpu_dev = cpu_dev;
  281. + info->proc_reg = proc_reg;
  282. + info->cpu_clk = cpu_clk;
  283. + info->inter_clk = inter_clk;
  284. +
  285. + return 0;
  286. +
  287. +out_free_opp_table:
  288. + dev_pm_opp_of_remove_table(cpu_dev);
  289. +
  290. +out_free_resources:
  291. + if (!IS_ERR(proc_reg))
  292. + regulator_put(proc_reg);
  293. + if (!IS_ERR(cpu_clk))
  294. + clk_put(cpu_clk);
  295. + if (!IS_ERR(inter_clk))
  296. + clk_put(inter_clk);
  297. +
  298. + return ret;
  299. +}
  300. +
  301. +static void mtk_cpu_dvfs_info_release(struct mtk_cpu_dvfs_info *info)
  302. +{
  303. + if (!IS_ERR(info->proc_reg))
  304. + regulator_put(info->proc_reg);
  305. + if (!IS_ERR(info->cpu_clk))
  306. + clk_put(info->cpu_clk);
  307. + if (!IS_ERR(info->inter_clk))
  308. + clk_put(info->inter_clk);
  309. +
  310. + dev_pm_opp_of_remove_table(info->cpu_dev);
  311. +}
  312. +
  313. +static int mtk_cpufreq_init(struct cpufreq_policy *policy)
  314. +{
  315. + struct mtk_cpu_dvfs_info *info;
  316. + struct cpufreq_frequency_table *freq_table;
  317. + int ret;
  318. +
  319. + info = kzalloc(sizeof(*info), GFP_KERNEL);
  320. + if (!info)
  321. + return -ENOMEM;
  322. +
  323. + ret = mtk_cpu_dvfs_info_init(info, policy->cpu);
  324. + if (ret) {
  325. + pr_err("%s failed to initialize dvfs info for cpu%d\n",
  326. + __func__, policy->cpu);
  327. + goto out_free_dvfs_info;
  328. + }
  329. +
  330. + ret = dev_pm_opp_init_cpufreq_table(info->cpu_dev, &freq_table);
  331. + if (ret) {
  332. + pr_err("failed to init cpufreq table for cpu%d: %d\n",
  333. + policy->cpu, ret);
  334. + goto out_release_dvfs_info;
  335. + }
  336. +
  337. + ret = cpufreq_table_validate_and_show(policy, freq_table);
  338. + if (ret) {
  339. + pr_err("%s: invalid frequency table: %d\n", __func__, ret);
  340. + goto out_free_cpufreq_table;
  341. + }
  342. +
  343. + /* CPUs in the same cluster share a clock and power domain. */
  344. + cpumask_setall(policy->cpus);
  345. + policy->driver_data = info;
  346. + policy->clk = info->cpu_clk;
  347. +
  348. + return 0;
  349. +
  350. +out_free_cpufreq_table:
  351. + dev_pm_opp_free_cpufreq_table(info->cpu_dev, &freq_table);
  352. +
  353. +out_release_dvfs_info:
  354. + mtk_cpu_dvfs_info_release(info);
  355. +
  356. +out_free_dvfs_info:
  357. + kfree(info);
  358. +
  359. + return ret;
  360. +}
  361. +
  362. +static int mtk_cpufreq_exit(struct cpufreq_policy *policy)
  363. +{
  364. + struct mtk_cpu_dvfs_info *info = policy->driver_data;
  365. +
  366. + cpufreq_cooling_unregister(info->cdev);
  367. + dev_pm_opp_free_cpufreq_table(info->cpu_dev, &policy->freq_table);
  368. + mtk_cpu_dvfs_info_release(info);
  369. + kfree(info);
  370. +
  371. + return 0;
  372. +}
  373. +
  374. +static struct cpufreq_driver mt7623_cpufreq_driver = {
  375. + .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK,
  376. + .verify = cpufreq_generic_frequency_table_verify,
  377. + .target_index = mtk_cpufreq_set_target,
  378. + .get = cpufreq_generic_get,
  379. + .init = mtk_cpufreq_init,
  380. + .exit = mtk_cpufreq_exit,
  381. + .ready = mtk_cpufreq_ready,
  382. + .name = "mtk-cpufreq",
  383. + .attr = cpufreq_generic_attr,
  384. +};
  385. +
  386. +static int mt7623_cpufreq_probe(struct platform_device *pdev)
  387. +{
  388. + int ret;
  389. +
  390. + ret = cpufreq_register_driver(&mt7623_cpufreq_driver);
  391. + if (ret)
  392. + pr_err("failed to register mtk cpufreq driver\n");
  393. +
  394. + return ret;
  395. +}
  396. +
  397. +static struct platform_driver mt7623_cpufreq_platdrv = {
  398. + .driver = {
  399. + .name = "mt7623-cpufreq",
  400. + },
  401. + .probe = mt7623_cpufreq_probe,
  402. +};
  403. +
  404. +static int mt7623_cpufreq_driver_init(void)
  405. +{
  406. + struct platform_device *pdev;
  407. + int err;
  408. +
  409. + if (!of_machine_is_compatible("mediatek,mt7623"))
  410. + return -ENODEV;
  411. +
  412. + err = platform_driver_register(&mt7623_cpufreq_platdrv);
  413. + if (err)
  414. + return err;
  415. +
  416. + /*
  417. + * Since there's no place to hold device registration code and no
  418. + * device tree based way to match cpufreq driver yet, both the driver
  419. + * and the device registration codes are put here to handle defer
  420. + * probing.
  421. + */
  422. + pdev = platform_device_register_simple("mt7623-cpufreq", -1, NULL, 0);
  423. + if (IS_ERR(pdev)) {
  424. + pr_err("failed to register mtk-cpufreq platform device\n");
  425. + return PTR_ERR(pdev);
  426. + }
  427. +
  428. + return 0;
  429. +}
  430. +device_initcall(mt7623_cpufreq_driver_init);