1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright 2022 StarFive Technology Co., Ltd.
5 * Starfive CPUfreq Support
8 #include <linux/kernel.h>
9 #include <linux/types.h>
10 #include <linux/init.h>
11 #include <linux/cpufreq.h>
12 #include <linux/clk.h>
13 #include <linux/err.h>
14 #include <linux/regulator/consumer.h>
15 #include <linux/module.h>
17 #define VOLT_TOL (10000)
19 struct stf_cpu_dvfs_info {
20 struct regulator *vddcpu;
24 unsigned long regulator_latency;
25 struct device *cpu_dev;
29 static int stf_cpufreq_set_target_index(struct cpufreq_policy *policy,
32 struct cpufreq_frequency_table *freq_table = policy->freq_table;
33 struct stf_cpu_dvfs_info *info = cpufreq_get_driver_data();
34 struct dev_pm_opp *opp;
35 unsigned long old_freq, new_freq;
36 int old_vdd, target_vdd, ret;
38 old_freq = clk_get_rate(info->cpu_clk);
39 old_vdd = regulator_get_voltage(info->vddcpu);
41 pr_err("Invalid cpu regulator value: %d\n", old_vdd);
45 new_freq = freq_table[index].frequency * 1000;
46 opp = dev_pm_opp_find_freq_ceil(info->cpu_dev, &new_freq);
48 pr_err("Failed to find OPP for %ld\n", new_freq);
51 target_vdd = dev_pm_opp_get_voltage(opp);
55 if (info->vddcpu && new_freq > old_freq) {
56 ret = regulator_set_voltage(info->vddcpu,
57 target_vdd, target_vdd + VOLT_TOL);
59 pr_err("Failed to set vddcpu for %ldkHz: %d\n",
65 if (clk_set_parent(policy->clk, info->osc_clk))
66 pr_err("cpu set parent osc failed\n");
68 ret = clk_set_rate(info->pll0_clk, new_freq);
70 pr_err("Failed to set rate %ldkHz: %d\n",
73 if (clk_set_parent(policy->clk, info->pll0_clk))
74 pr_err("cpu set parent pll0 failed\n");
76 if (info->vddcpu && new_freq < old_freq) {
77 ret = regulator_set_voltage(info->vddcpu,
78 target_vdd, target_vdd + VOLT_TOL);
80 pr_err("Failed to set vddcpu for %ldkHz: %d\n",
82 if (clk_set_rate(policy->clk, old_freq * 1000) < 0)
83 pr_err("Failed to restore original clock rate\n");
89 pr_debug("Set actual frequency %lukHz\n",
90 clk_get_rate(policy->clk) / 1000);
95 static int stf_cpufreq_driver_init(struct cpufreq_policy *policy)
97 struct stf_cpu_dvfs_info *info = cpufreq_get_driver_data();
98 struct cpufreq_frequency_table *freq_table;
101 ret = dev_pm_opp_init_cpufreq_table(info->cpu_dev, &freq_table);
103 pr_err("Failed to init cpufreq table for cpu%d: %d\n",
108 cpumask_copy(policy->cpus, &info->cpus);
109 policy->freq_table = freq_table;
110 policy->driver_data = info;
111 policy->clk = info->cpu_clk;
116 static int stf_cpu_dvfs_info_init(struct platform_device *pdev,
117 struct stf_cpu_dvfs_info *info)
119 struct device *dev = &pdev->dev;
121 static int retry = 3;
123 info->vddcpu = regulator_get_optional(&pdev->dev, "cpu_vdd_0p9");
124 if (IS_ERR(info->vddcpu)) {
125 if (PTR_ERR(info->vddcpu) == -EPROBE_DEFER)
126 dev_warn(&pdev->dev, "The cpu regulator is not ready, retry.\n");
128 dev_err(&pdev->dev, "Failed to get regulator for cpu\n");
130 return -EPROBE_DEFER;
132 return PTR_ERR(info->vddcpu);
135 info->cpu_clk = devm_clk_get(dev, "cpu_clk");
136 if (IS_ERR(info->cpu_clk)) {
137 dev_err(&pdev->dev, "Unable to obtain cpu_clk: %ld\n",
138 PTR_ERR(info->cpu_clk));
139 return PTR_ERR(info->cpu_clk);
141 info->pll0_clk = devm_clk_get(dev, "pll0");
142 if (IS_ERR(info->pll0_clk)) {
143 dev_err(&pdev->dev, "Unable to obtain cpu_clk: %ld\n",
144 PTR_ERR(info->pll0_clk));
145 return PTR_ERR(info->pll0_clk);
148 info->osc_clk = devm_clk_get(dev, "osc");
149 if (IS_ERR(info->osc_clk)) {
150 dev_err(&pdev->dev, "Unable to obtain osc_clk: %ld\n",
151 PTR_ERR(info->osc_clk));
152 return PTR_ERR(info->osc_clk);
155 info->cpu_dev = get_cpu_device(1);
156 if (!info->cpu_dev) {
157 dev_err(&pdev->dev, "Failed to get cpu device\n");
160 /* Get OPP-sharing information from "operating-points-v2" bindings */
161 ret = dev_pm_opp_of_get_sharing_cpus(info->cpu_dev, &info->cpus);
163 dev_err(&pdev->dev, "Failed to get OPP-sharing information for cpu\n");
167 ret = dev_pm_opp_of_cpumask_add_table(&info->cpus);
169 pr_warn("no OPP table for cpu\n");
176 static struct cpufreq_driver stf_cpufreq_driver = {
177 .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK,
178 .verify = cpufreq_generic_frequency_table_verify,
179 .target_index = stf_cpufreq_set_target_index,
180 .get = cpufreq_generic_get,
181 .init = stf_cpufreq_driver_init,
182 .name = "stf-cpufreq",
183 .attr = cpufreq_generic_attr,
186 static int stf_cpufreq_probe(struct platform_device *pdev)
188 struct stf_cpu_dvfs_info *info;
191 info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
195 ret = stf_cpu_dvfs_info_init(pdev, info);
197 dev_err(&pdev->dev, "Failed to init stf cpu dvfs info\n");
201 stf_cpufreq_driver.driver_data = info;
202 ret = cpufreq_register_driver(&stf_cpufreq_driver);
204 dev_err(&pdev->dev, "Failed to register stf cpufreq driver\n");
210 static const struct of_device_id stf_cpufreq_match_table[] = {
211 { .compatible = "starfive,stf-cpufreq" },
215 static struct platform_driver stf_cpufreq_plat_driver = {
216 .probe = stf_cpufreq_probe,
218 .name = "stf-cpufreq",
219 .of_match_table = stf_cpufreq_match_table,
223 static int __init stf_cpufreq_init(void)
225 return platform_driver_register(&stf_cpufreq_plat_driver);
227 postcore_initcall(stf_cpufreq_init);
229 MODULE_DESCRIPTION("STARFIVE CPUFREQ Driver");
230 MODULE_AUTHOR("Mason Huuo <mason.huo@starfivetech.com>");
231 MODULE_LICENSE("GPL v2");