1501c574fSSean Wang /*
2501c574fSSean Wang  * Copyright (c) 2015 Linaro Ltd.
3501c574fSSean Wang  * Author: Pi-Cheng Chen <pi-cheng.chen@linaro.org>
4501c574fSSean Wang  *
5501c574fSSean Wang  * This program is free software; you can redistribute it and/or modify
6501c574fSSean Wang  * it under the terms of the GNU General Public License version 2 as
7501c574fSSean Wang  * published by the Free Software Foundation.
8501c574fSSean Wang  *
9501c574fSSean Wang  * This program is distributed in the hope that it will be useful,
10501c574fSSean Wang  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11501c574fSSean Wang  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12501c574fSSean Wang  * GNU General Public License for more details.
13501c574fSSean Wang  */
14501c574fSSean Wang 
15501c574fSSean Wang #include <linux/clk.h>
16501c574fSSean Wang #include <linux/cpu.h>
17501c574fSSean Wang #include <linux/cpu_cooling.h>
18501c574fSSean Wang #include <linux/cpufreq.h>
19501c574fSSean Wang #include <linux/cpumask.h>
20501c574fSSean Wang #include <linux/module.h>
21501c574fSSean Wang #include <linux/of.h>
22501c574fSSean Wang #include <linux/platform_device.h>
23501c574fSSean Wang #include <linux/pm_opp.h>
24501c574fSSean Wang #include <linux/regulator/consumer.h>
25501c574fSSean Wang #include <linux/slab.h>
26501c574fSSean Wang #include <linux/thermal.h>
27501c574fSSean Wang 
28501c574fSSean Wang #define MIN_VOLT_SHIFT		(100000)
29501c574fSSean Wang #define MAX_VOLT_SHIFT		(200000)
30501c574fSSean Wang #define MAX_VOLT_LIMIT		(1150000)
31501c574fSSean Wang #define VOLT_TOL		(10000)
32501c574fSSean Wang 
33501c574fSSean Wang /*
34501c574fSSean Wang  * The struct mtk_cpu_dvfs_info holds necessary information for doing CPU DVFS
35501c574fSSean Wang  * on each CPU power/clock domain of Mediatek SoCs. Each CPU cluster in
36501c574fSSean Wang  * Mediatek SoCs has two voltage inputs, Vproc and Vsram. In some cases the two
37501c574fSSean Wang  * voltage inputs need to be controlled under a hardware limitation:
38501c574fSSean Wang  * 100mV < Vsram - Vproc < 200mV
39501c574fSSean Wang  *
40501c574fSSean Wang  * When scaling the clock frequency of a CPU clock domain, the clock source
41501c574fSSean Wang  * needs to be switched to another stable PLL clock temporarily until
42501c574fSSean Wang  * the original PLL becomes stable at target frequency.
43501c574fSSean Wang  */
44501c574fSSean Wang struct mtk_cpu_dvfs_info {
45501c574fSSean Wang 	struct cpumask cpus;
46501c574fSSean Wang 	struct device *cpu_dev;
47501c574fSSean Wang 	struct regulator *proc_reg;
48501c574fSSean Wang 	struct regulator *sram_reg;
49501c574fSSean Wang 	struct clk *cpu_clk;
50501c574fSSean Wang 	struct clk *inter_clk;
51501c574fSSean Wang 	struct thermal_cooling_device *cdev;
52501c574fSSean Wang 	struct list_head list_head;
53501c574fSSean Wang 	int intermediate_voltage;
54501c574fSSean Wang 	bool need_voltage_tracking;
55501c574fSSean Wang };
56501c574fSSean Wang 
57501c574fSSean Wang static LIST_HEAD(dvfs_info_list);
58501c574fSSean Wang 
59501c574fSSean Wang static struct mtk_cpu_dvfs_info *mtk_cpu_dvfs_info_lookup(int cpu)
60501c574fSSean Wang {
61501c574fSSean Wang 	struct mtk_cpu_dvfs_info *info;
62501c574fSSean Wang 
63501c574fSSean Wang 	list_for_each_entry(info, &dvfs_info_list, list_head) {
64501c574fSSean Wang 		if (cpumask_test_cpu(cpu, &info->cpus))
65501c574fSSean Wang 			return info;
66501c574fSSean Wang 	}
67501c574fSSean Wang 
68501c574fSSean Wang 	return NULL;
69501c574fSSean Wang }
70501c574fSSean Wang 
71501c574fSSean Wang static int mtk_cpufreq_voltage_tracking(struct mtk_cpu_dvfs_info *info,
72501c574fSSean Wang 					int new_vproc)
73501c574fSSean Wang {
74501c574fSSean Wang 	struct regulator *proc_reg = info->proc_reg;
75501c574fSSean Wang 	struct regulator *sram_reg = info->sram_reg;
76501c574fSSean Wang 	int old_vproc, old_vsram, new_vsram, vsram, vproc, ret;
77501c574fSSean Wang 
78501c574fSSean Wang 	old_vproc = regulator_get_voltage(proc_reg);
79501c574fSSean Wang 	if (old_vproc < 0) {
80501c574fSSean Wang 		pr_err("%s: invalid Vproc value: %d\n", __func__, old_vproc);
81501c574fSSean Wang 		return old_vproc;
82501c574fSSean Wang 	}
83501c574fSSean Wang 	/* Vsram should not exceed the maximum allowed voltage of SoC. */
84501c574fSSean Wang 	new_vsram = min(new_vproc + MIN_VOLT_SHIFT, MAX_VOLT_LIMIT);
85501c574fSSean Wang 
86501c574fSSean Wang 	if (old_vproc < new_vproc) {
87501c574fSSean Wang 		/*
88501c574fSSean Wang 		 * When scaling up voltages, Vsram and Vproc scale up step
89501c574fSSean Wang 		 * by step. At each step, set Vsram to (Vproc + 200mV) first,
90501c574fSSean Wang 		 * then set Vproc to (Vsram - 100mV).
91501c574fSSean Wang 		 * Keep doing it until Vsram and Vproc hit target voltages.
92501c574fSSean Wang 		 */
93501c574fSSean Wang 		do {
94501c574fSSean Wang 			old_vsram = regulator_get_voltage(sram_reg);
95501c574fSSean Wang 			if (old_vsram < 0) {
96501c574fSSean Wang 				pr_err("%s: invalid Vsram value: %d\n",
97501c574fSSean Wang 				       __func__, old_vsram);
98501c574fSSean Wang 				return old_vsram;
99501c574fSSean Wang 			}
100501c574fSSean Wang 			old_vproc = regulator_get_voltage(proc_reg);
101501c574fSSean Wang 			if (old_vproc < 0) {
102501c574fSSean Wang 				pr_err("%s: invalid Vproc value: %d\n",
103501c574fSSean Wang 				       __func__, old_vproc);
104501c574fSSean Wang 				return old_vproc;
105501c574fSSean Wang 			}
106501c574fSSean Wang 
107501c574fSSean Wang 			vsram = min(new_vsram, old_vproc + MAX_VOLT_SHIFT);
108501c574fSSean Wang 
109501c574fSSean Wang 			if (vsram + VOLT_TOL >= MAX_VOLT_LIMIT) {
110501c574fSSean Wang 				vsram = MAX_VOLT_LIMIT;
111501c574fSSean Wang 
112501c574fSSean Wang 				/*
113501c574fSSean Wang 				 * If the target Vsram hits the maximum voltage,
114501c574fSSean Wang 				 * try to set the exact voltage value first.
115501c574fSSean Wang 				 */
116501c574fSSean Wang 				ret = regulator_set_voltage(sram_reg, vsram,
117501c574fSSean Wang 							    vsram);
118501c574fSSean Wang 				if (ret)
119501c574fSSean Wang 					ret = regulator_set_voltage(sram_reg,
120501c574fSSean Wang 							vsram - VOLT_TOL,
121501c574fSSean Wang 							vsram);
122501c574fSSean Wang 
123501c574fSSean Wang 				vproc = new_vproc;
124501c574fSSean Wang 			} else {
125501c574fSSean Wang 				ret = regulator_set_voltage(sram_reg, vsram,
126501c574fSSean Wang 							    vsram + VOLT_TOL);
127501c574fSSean Wang 
128501c574fSSean Wang 				vproc = vsram - MIN_VOLT_SHIFT;
129501c574fSSean Wang 			}
130501c574fSSean Wang 			if (ret)
131501c574fSSean Wang 				return ret;
132501c574fSSean Wang 
133501c574fSSean Wang 			ret = regulator_set_voltage(proc_reg, vproc,
134501c574fSSean Wang 						    vproc + VOLT_TOL);
135501c574fSSean Wang 			if (ret) {
136501c574fSSean Wang 				regulator_set_voltage(sram_reg, old_vsram,
137501c574fSSean Wang 						      old_vsram);
138501c574fSSean Wang 				return ret;
139501c574fSSean Wang 			}
140501c574fSSean Wang 		} while (vproc < new_vproc || vsram < new_vsram);
141501c574fSSean Wang 	} else if (old_vproc > new_vproc) {
142501c574fSSean Wang 		/*
143501c574fSSean Wang 		 * When scaling down voltages, Vsram and Vproc scale down step
144501c574fSSean Wang 		 * by step. At each step, set Vproc to (Vsram - 200mV) first,
145501c574fSSean Wang 		 * then set Vproc to (Vproc + 100mV).
146501c574fSSean Wang 		 * Keep doing it until Vsram and Vproc hit target voltages.
147501c574fSSean Wang 		 */
148501c574fSSean Wang 		do {
149501c574fSSean Wang 			old_vproc = regulator_get_voltage(proc_reg);
150501c574fSSean Wang 			if (old_vproc < 0) {
151501c574fSSean Wang 				pr_err("%s: invalid Vproc value: %d\n",
152501c574fSSean Wang 				       __func__, old_vproc);
153501c574fSSean Wang 				return old_vproc;
154501c574fSSean Wang 			}
155501c574fSSean Wang 			old_vsram = regulator_get_voltage(sram_reg);
156501c574fSSean Wang 			if (old_vsram < 0) {
157501c574fSSean Wang 				pr_err("%s: invalid Vsram value: %d\n",
158501c574fSSean Wang 				       __func__, old_vsram);
159501c574fSSean Wang 				return old_vsram;
160501c574fSSean Wang 			}
161501c574fSSean Wang 
162501c574fSSean Wang 			vproc = max(new_vproc, old_vsram - MAX_VOLT_SHIFT);
163501c574fSSean Wang 			ret = regulator_set_voltage(proc_reg, vproc,
164501c574fSSean Wang 						    vproc + VOLT_TOL);
165501c574fSSean Wang 			if (ret)
166501c574fSSean Wang 				return ret;
167501c574fSSean Wang 
168501c574fSSean Wang 			if (vproc == new_vproc)
169501c574fSSean Wang 				vsram = new_vsram;
170501c574fSSean Wang 			else
171501c574fSSean Wang 				vsram = max(new_vsram, vproc + MIN_VOLT_SHIFT);
172501c574fSSean Wang 
173501c574fSSean Wang 			if (vsram + VOLT_TOL >= MAX_VOLT_LIMIT) {
174501c574fSSean Wang 				vsram = MAX_VOLT_LIMIT;
175501c574fSSean Wang 
176501c574fSSean Wang 				/*
177501c574fSSean Wang 				 * If the target Vsram hits the maximum voltage,
178501c574fSSean Wang 				 * try to set the exact voltage value first.
179501c574fSSean Wang 				 */
180501c574fSSean Wang 				ret = regulator_set_voltage(sram_reg, vsram,
181501c574fSSean Wang 							    vsram);
182501c574fSSean Wang 				if (ret)
183501c574fSSean Wang 					ret = regulator_set_voltage(sram_reg,
184501c574fSSean Wang 							vsram - VOLT_TOL,
185501c574fSSean Wang 							vsram);
186501c574fSSean Wang 			} else {
187501c574fSSean Wang 				ret = regulator_set_voltage(sram_reg, vsram,
188501c574fSSean Wang 							    vsram + VOLT_TOL);
189501c574fSSean Wang 			}
190501c574fSSean Wang 
191501c574fSSean Wang 			if (ret) {
192501c574fSSean Wang 				regulator_set_voltage(proc_reg, old_vproc,
193501c574fSSean Wang 						      old_vproc);
194501c574fSSean Wang 				return ret;
195501c574fSSean Wang 			}
196501c574fSSean Wang 		} while (vproc > new_vproc + VOLT_TOL ||
197501c574fSSean Wang 			 vsram > new_vsram + VOLT_TOL);
198501c574fSSean Wang 	}
199501c574fSSean Wang 
200501c574fSSean Wang 	return 0;
201501c574fSSean Wang }
202501c574fSSean Wang 
203501c574fSSean Wang static int mtk_cpufreq_set_voltage(struct mtk_cpu_dvfs_info *info, int vproc)
204501c574fSSean Wang {
205501c574fSSean Wang 	if (info->need_voltage_tracking)
206501c574fSSean Wang 		return mtk_cpufreq_voltage_tracking(info, vproc);
207501c574fSSean Wang 	else
208501c574fSSean Wang 		return regulator_set_voltage(info->proc_reg, vproc,
209501c574fSSean Wang 					     vproc + VOLT_TOL);
210501c574fSSean Wang }
211501c574fSSean Wang 
212501c574fSSean Wang static int mtk_cpufreq_set_target(struct cpufreq_policy *policy,
213501c574fSSean Wang 				  unsigned int index)
214501c574fSSean Wang {
215501c574fSSean Wang 	struct cpufreq_frequency_table *freq_table = policy->freq_table;
216501c574fSSean Wang 	struct clk *cpu_clk = policy->clk;
217501c574fSSean Wang 	struct clk *armpll = clk_get_parent(cpu_clk);
218501c574fSSean Wang 	struct mtk_cpu_dvfs_info *info = policy->driver_data;
219501c574fSSean Wang 	struct device *cpu_dev = info->cpu_dev;
220501c574fSSean Wang 	struct dev_pm_opp *opp;
221501c574fSSean Wang 	long freq_hz, old_freq_hz;
222501c574fSSean Wang 	int vproc, old_vproc, inter_vproc, target_vproc, ret;
223501c574fSSean Wang 
224501c574fSSean Wang 	inter_vproc = info->intermediate_voltage;
225501c574fSSean Wang 
226501c574fSSean Wang 	old_freq_hz = clk_get_rate(cpu_clk);
227501c574fSSean Wang 	old_vproc = regulator_get_voltage(info->proc_reg);
228501c574fSSean Wang 	if (old_vproc < 0) {
229501c574fSSean Wang 		pr_err("%s: invalid Vproc value: %d\n", __func__, old_vproc);
230501c574fSSean Wang 		return old_vproc;
231501c574fSSean Wang 	}
232501c574fSSean Wang 
233501c574fSSean Wang 	freq_hz = freq_table[index].frequency * 1000;
234501c574fSSean Wang 
235501c574fSSean Wang 	opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_hz);
236501c574fSSean Wang 	if (IS_ERR(opp)) {
237501c574fSSean Wang 		pr_err("cpu%d: failed to find OPP for %ld\n",
238501c574fSSean Wang 		       policy->cpu, freq_hz);
239501c574fSSean Wang 		return PTR_ERR(opp);
240501c574fSSean Wang 	}
241501c574fSSean Wang 	vproc = dev_pm_opp_get_voltage(opp);
242501c574fSSean Wang 	dev_pm_opp_put(opp);
243501c574fSSean Wang 
244501c574fSSean Wang 	/*
245501c574fSSean Wang 	 * If the new voltage or the intermediate voltage is higher than the
246501c574fSSean Wang 	 * current voltage, scale up voltage first.
247501c574fSSean Wang 	 */
248501c574fSSean Wang 	target_vproc = (inter_vproc > vproc) ? inter_vproc : vproc;
249501c574fSSean Wang 	if (old_vproc < target_vproc) {
250501c574fSSean Wang 		ret = mtk_cpufreq_set_voltage(info, target_vproc);
251501c574fSSean Wang 		if (ret) {
252501c574fSSean Wang 			pr_err("cpu%d: failed to scale up voltage!\n",
253501c574fSSean Wang 			       policy->cpu);
254501c574fSSean Wang 			mtk_cpufreq_set_voltage(info, old_vproc);
255501c574fSSean Wang 			return ret;
256501c574fSSean Wang 		}
257501c574fSSean Wang 	}
258501c574fSSean Wang 
259501c574fSSean Wang 	/* Reparent the CPU clock to intermediate clock. */
260501c574fSSean Wang 	ret = clk_set_parent(cpu_clk, info->inter_clk);
261501c574fSSean Wang 	if (ret) {
262501c574fSSean Wang 		pr_err("cpu%d: failed to re-parent cpu clock!\n",
263501c574fSSean Wang 		       policy->cpu);
264501c574fSSean Wang 		mtk_cpufreq_set_voltage(info, old_vproc);
265501c574fSSean Wang 		WARN_ON(1);
266501c574fSSean Wang 		return ret;
267501c574fSSean Wang 	}
268501c574fSSean Wang 
269501c574fSSean Wang 	/* Set the original PLL to target rate. */
270501c574fSSean Wang 	ret = clk_set_rate(armpll, freq_hz);
271501c574fSSean Wang 	if (ret) {
272501c574fSSean Wang 		pr_err("cpu%d: failed to scale cpu clock rate!\n",
273501c574fSSean Wang 		       policy->cpu);
274501c574fSSean Wang 		clk_set_parent(cpu_clk, armpll);
275501c574fSSean Wang 		mtk_cpufreq_set_voltage(info, old_vproc);
276501c574fSSean Wang 		return ret;
277501c574fSSean Wang 	}
278501c574fSSean Wang 
279501c574fSSean Wang 	/* Set parent of CPU clock back to the original PLL. */
280501c574fSSean Wang 	ret = clk_set_parent(cpu_clk, armpll);
281501c574fSSean Wang 	if (ret) {
282501c574fSSean Wang 		pr_err("cpu%d: failed to re-parent cpu clock!\n",
283501c574fSSean Wang 		       policy->cpu);
284501c574fSSean Wang 		mtk_cpufreq_set_voltage(info, inter_vproc);
285501c574fSSean Wang 		WARN_ON(1);
286501c574fSSean Wang 		return ret;
287501c574fSSean Wang 	}
288501c574fSSean Wang 
289501c574fSSean Wang 	/*
290501c574fSSean Wang 	 * If the new voltage is lower than the intermediate voltage or the
291501c574fSSean Wang 	 * original voltage, scale down to the new voltage.
292501c574fSSean Wang 	 */
293501c574fSSean Wang 	if (vproc < inter_vproc || vproc < old_vproc) {
294501c574fSSean Wang 		ret = mtk_cpufreq_set_voltage(info, vproc);
295501c574fSSean Wang 		if (ret) {
296501c574fSSean Wang 			pr_err("cpu%d: failed to scale down voltage!\n",
297501c574fSSean Wang 			       policy->cpu);
298501c574fSSean Wang 			clk_set_parent(cpu_clk, info->inter_clk);
299501c574fSSean Wang 			clk_set_rate(armpll, old_freq_hz);
300501c574fSSean Wang 			clk_set_parent(cpu_clk, armpll);
301501c574fSSean Wang 			return ret;
302501c574fSSean Wang 		}
303501c574fSSean Wang 	}
304501c574fSSean Wang 
305501c574fSSean Wang 	return 0;
306501c574fSSean Wang }
307501c574fSSean Wang 
308501c574fSSean Wang #define DYNAMIC_POWER "dynamic-power-coefficient"
309501c574fSSean Wang 
310501c574fSSean Wang static void mtk_cpufreq_ready(struct cpufreq_policy *policy)
311501c574fSSean Wang {
312501c574fSSean Wang 	struct mtk_cpu_dvfs_info *info = policy->driver_data;
313501c574fSSean Wang 	struct device_node *np = of_node_get(info->cpu_dev->of_node);
314501c574fSSean Wang 	u32 capacitance = 0;
315501c574fSSean Wang 
316501c574fSSean Wang 	if (WARN_ON(!np))
317501c574fSSean Wang 		return;
318501c574fSSean Wang 
319501c574fSSean Wang 	if (of_find_property(np, "#cooling-cells", NULL)) {
320501c574fSSean Wang 		of_property_read_u32(np, DYNAMIC_POWER, &capacitance);
321501c574fSSean Wang 
322501c574fSSean Wang 		info->cdev = of_cpufreq_power_cooling_register(np,
323501c574fSSean Wang 						policy, capacitance, NULL);
324501c574fSSean Wang 
325501c574fSSean Wang 		if (IS_ERR(info->cdev)) {
326501c574fSSean Wang 			dev_err(info->cpu_dev,
327501c574fSSean Wang 				"running cpufreq without cooling device: %ld\n",
328501c574fSSean Wang 				PTR_ERR(info->cdev));
329501c574fSSean Wang 
330501c574fSSean Wang 			info->cdev = NULL;
331501c574fSSean Wang 		}
332501c574fSSean Wang 	}
333501c574fSSean Wang 
334501c574fSSean Wang 	of_node_put(np);
335501c574fSSean Wang }
336501c574fSSean Wang 
337501c574fSSean Wang static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu)
338501c574fSSean Wang {
339501c574fSSean Wang 	struct device *cpu_dev;
340501c574fSSean Wang 	struct regulator *proc_reg = ERR_PTR(-ENODEV);
341501c574fSSean Wang 	struct regulator *sram_reg = ERR_PTR(-ENODEV);
342501c574fSSean Wang 	struct clk *cpu_clk = ERR_PTR(-ENODEV);
343501c574fSSean Wang 	struct clk *inter_clk = ERR_PTR(-ENODEV);
344501c574fSSean Wang 	struct dev_pm_opp *opp;
345501c574fSSean Wang 	unsigned long rate;
346501c574fSSean Wang 	int ret;
347501c574fSSean Wang 
348501c574fSSean Wang 	cpu_dev = get_cpu_device(cpu);
349501c574fSSean Wang 	if (!cpu_dev) {
350501c574fSSean Wang 		pr_err("failed to get cpu%d device\n", cpu);
351501c574fSSean Wang 		return -ENODEV;
352501c574fSSean Wang 	}
353501c574fSSean Wang 
354501c574fSSean Wang 	cpu_clk = clk_get(cpu_dev, "cpu");
355501c574fSSean Wang 	if (IS_ERR(cpu_clk)) {
356501c574fSSean Wang 		if (PTR_ERR(cpu_clk) == -EPROBE_DEFER)
357501c574fSSean Wang 			pr_warn("cpu clk for cpu%d not ready, retry.\n", cpu);
358501c574fSSean Wang 		else
359501c574fSSean Wang 			pr_err("failed to get cpu clk for cpu%d\n", cpu);
360501c574fSSean Wang 
361501c574fSSean Wang 		ret = PTR_ERR(cpu_clk);
362501c574fSSean Wang 		return ret;
363501c574fSSean Wang 	}
364501c574fSSean Wang 
365501c574fSSean Wang 	inter_clk = clk_get(cpu_dev, "intermediate");
366501c574fSSean Wang 	if (IS_ERR(inter_clk)) {
367501c574fSSean Wang 		if (PTR_ERR(inter_clk) == -EPROBE_DEFER)
368501c574fSSean Wang 			pr_warn("intermediate clk for cpu%d not ready, retry.\n",
369501c574fSSean Wang 				cpu);
370501c574fSSean Wang 		else
371501c574fSSean Wang 			pr_err("failed to get intermediate clk for cpu%d\n",
372501c574fSSean Wang 			       cpu);
373501c574fSSean Wang 
374501c574fSSean Wang 		ret = PTR_ERR(inter_clk);
375501c574fSSean Wang 		goto out_free_resources;
376501c574fSSean Wang 	}
377501c574fSSean Wang 
378501c574fSSean Wang 	proc_reg = regulator_get_exclusive(cpu_dev, "proc");
379501c574fSSean Wang 	if (IS_ERR(proc_reg)) {
380501c574fSSean Wang 		if (PTR_ERR(proc_reg) == -EPROBE_DEFER)
381501c574fSSean Wang 			pr_warn("proc regulator for cpu%d not ready, retry.\n",
382501c574fSSean Wang 				cpu);
383501c574fSSean Wang 		else
384501c574fSSean Wang 			pr_err("failed to get proc regulator for cpu%d\n",
385501c574fSSean Wang 			       cpu);
386501c574fSSean Wang 
387501c574fSSean Wang 		ret = PTR_ERR(proc_reg);
388501c574fSSean Wang 		goto out_free_resources;
389501c574fSSean Wang 	}
390501c574fSSean Wang 
391501c574fSSean Wang 	/* Both presence and absence of sram regulator are valid cases. */
392501c574fSSean Wang 	sram_reg = regulator_get_exclusive(cpu_dev, "sram");
393501c574fSSean Wang 
394501c574fSSean Wang 	/* Get OPP-sharing information from "operating-points-v2" bindings */
395501c574fSSean Wang 	ret = dev_pm_opp_of_get_sharing_cpus(cpu_dev, &info->cpus);
396501c574fSSean Wang 	if (ret) {
397501c574fSSean Wang 		pr_err("failed to get OPP-sharing information for cpu%d\n",
398501c574fSSean Wang 		       cpu);
399501c574fSSean Wang 		goto out_free_resources;
400501c574fSSean Wang 	}
401501c574fSSean Wang 
402501c574fSSean Wang 	ret = dev_pm_opp_of_cpumask_add_table(&info->cpus);
403501c574fSSean Wang 	if (ret) {
404501c574fSSean Wang 		pr_warn("no OPP table for cpu%d\n", cpu);
405501c574fSSean Wang 		goto out_free_resources;
406501c574fSSean Wang 	}
407501c574fSSean Wang 
408501c574fSSean Wang 	/* Search a safe voltage for intermediate frequency. */
409501c574fSSean Wang 	rate = clk_get_rate(inter_clk);
410501c574fSSean Wang 	opp = dev_pm_opp_find_freq_ceil(cpu_dev, &rate);
411501c574fSSean Wang 	if (IS_ERR(opp)) {
412501c574fSSean Wang 		pr_err("failed to get intermediate opp for cpu%d\n", cpu);
413501c574fSSean Wang 		ret = PTR_ERR(opp);
414501c574fSSean Wang 		goto out_free_opp_table;
415501c574fSSean Wang 	}
416501c574fSSean Wang 	info->intermediate_voltage = dev_pm_opp_get_voltage(opp);
417501c574fSSean Wang 	dev_pm_opp_put(opp);
418501c574fSSean Wang 
419501c574fSSean Wang 	info->cpu_dev = cpu_dev;
420501c574fSSean Wang 	info->proc_reg = proc_reg;
421501c574fSSean Wang 	info->sram_reg = IS_ERR(sram_reg) ? NULL : sram_reg;
422501c574fSSean Wang 	info->cpu_clk = cpu_clk;
423501c574fSSean Wang 	info->inter_clk = inter_clk;
424501c574fSSean Wang 
425501c574fSSean Wang 	/*
426501c574fSSean Wang 	 * If SRAM regulator is present, software "voltage tracking" is needed
427501c574fSSean Wang 	 * for this CPU power domain.
428501c574fSSean Wang 	 */
429501c574fSSean Wang 	info->need_voltage_tracking = !IS_ERR(sram_reg);
430501c574fSSean Wang 
431501c574fSSean Wang 	return 0;
432501c574fSSean Wang 
433501c574fSSean Wang out_free_opp_table:
434501c574fSSean Wang 	dev_pm_opp_of_cpumask_remove_table(&info->cpus);
435501c574fSSean Wang 
436501c574fSSean Wang out_free_resources:
437501c574fSSean Wang 	if (!IS_ERR(proc_reg))
438501c574fSSean Wang 		regulator_put(proc_reg);
439501c574fSSean Wang 	if (!IS_ERR(sram_reg))
440501c574fSSean Wang 		regulator_put(sram_reg);
441501c574fSSean Wang 	if (!IS_ERR(cpu_clk))
442501c574fSSean Wang 		clk_put(cpu_clk);
443501c574fSSean Wang 	if (!IS_ERR(inter_clk))
444501c574fSSean Wang 		clk_put(inter_clk);
445501c574fSSean Wang 
446501c574fSSean Wang 	return ret;
447501c574fSSean Wang }
448501c574fSSean Wang 
449501c574fSSean Wang static void mtk_cpu_dvfs_info_release(struct mtk_cpu_dvfs_info *info)
450501c574fSSean Wang {
451501c574fSSean Wang 	if (!IS_ERR(info->proc_reg))
452501c574fSSean Wang 		regulator_put(info->proc_reg);
453501c574fSSean Wang 	if (!IS_ERR(info->sram_reg))
454501c574fSSean Wang 		regulator_put(info->sram_reg);
455501c574fSSean Wang 	if (!IS_ERR(info->cpu_clk))
456501c574fSSean Wang 		clk_put(info->cpu_clk);
457501c574fSSean Wang 	if (!IS_ERR(info->inter_clk))
458501c574fSSean Wang 		clk_put(info->inter_clk);
459501c574fSSean Wang 
460501c574fSSean Wang 	dev_pm_opp_of_cpumask_remove_table(&info->cpus);
461501c574fSSean Wang }
462501c574fSSean Wang 
463501c574fSSean Wang static int mtk_cpufreq_init(struct cpufreq_policy *policy)
464501c574fSSean Wang {
465501c574fSSean Wang 	struct mtk_cpu_dvfs_info *info;
466501c574fSSean Wang 	struct cpufreq_frequency_table *freq_table;
467501c574fSSean Wang 	int ret;
468501c574fSSean Wang 
469501c574fSSean Wang 	info = mtk_cpu_dvfs_info_lookup(policy->cpu);
470501c574fSSean Wang 	if (!info) {
471501c574fSSean Wang 		pr_err("dvfs info for cpu%d is not initialized.\n",
472501c574fSSean Wang 		       policy->cpu);
473501c574fSSean Wang 		return -EINVAL;
474501c574fSSean Wang 	}
475501c574fSSean Wang 
476501c574fSSean Wang 	ret = dev_pm_opp_init_cpufreq_table(info->cpu_dev, &freq_table);
477501c574fSSean Wang 	if (ret) {
478501c574fSSean Wang 		pr_err("failed to init cpufreq table for cpu%d: %d\n",
479501c574fSSean Wang 		       policy->cpu, ret);
480501c574fSSean Wang 		return ret;
481501c574fSSean Wang 	}
482501c574fSSean Wang 
483501c574fSSean Wang 	ret = cpufreq_table_validate_and_show(policy, freq_table);
484501c574fSSean Wang 	if (ret) {
485501c574fSSean Wang 		pr_err("%s: invalid frequency table: %d\n", __func__, ret);
486501c574fSSean Wang 		goto out_free_cpufreq_table;
487501c574fSSean Wang 	}
488501c574fSSean Wang 
489501c574fSSean Wang 	cpumask_copy(policy->cpus, &info->cpus);
490501c574fSSean Wang 	policy->driver_data = info;
491501c574fSSean Wang 	policy->clk = info->cpu_clk;
492501c574fSSean Wang 
493501c574fSSean Wang 	return 0;
494501c574fSSean Wang 
495501c574fSSean Wang out_free_cpufreq_table:
496501c574fSSean Wang 	dev_pm_opp_free_cpufreq_table(info->cpu_dev, &freq_table);
497501c574fSSean Wang 	return ret;
498501c574fSSean Wang }
499501c574fSSean Wang 
500501c574fSSean Wang static int mtk_cpufreq_exit(struct cpufreq_policy *policy)
501501c574fSSean Wang {
502501c574fSSean Wang 	struct mtk_cpu_dvfs_info *info = policy->driver_data;
503501c574fSSean Wang 
504501c574fSSean Wang 	cpufreq_cooling_unregister(info->cdev);
505501c574fSSean Wang 	dev_pm_opp_free_cpufreq_table(info->cpu_dev, &policy->freq_table);
506501c574fSSean Wang 
507501c574fSSean Wang 	return 0;
508501c574fSSean Wang }
509501c574fSSean Wang 
510862e0104SSean Wang static struct cpufreq_driver mtk_cpufreq_driver = {
511501c574fSSean Wang 	.flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK |
512501c574fSSean Wang 		 CPUFREQ_HAVE_GOVERNOR_PER_POLICY,
513501c574fSSean Wang 	.verify = cpufreq_generic_frequency_table_verify,
514501c574fSSean Wang 	.target_index = mtk_cpufreq_set_target,
515501c574fSSean Wang 	.get = cpufreq_generic_get,
516501c574fSSean Wang 	.init = mtk_cpufreq_init,
517501c574fSSean Wang 	.exit = mtk_cpufreq_exit,
518501c574fSSean Wang 	.ready = mtk_cpufreq_ready,
519501c574fSSean Wang 	.name = "mtk-cpufreq",
520501c574fSSean Wang 	.attr = cpufreq_generic_attr,
521501c574fSSean Wang };
522501c574fSSean Wang 
523862e0104SSean Wang static int mtk_cpufreq_probe(struct platform_device *pdev)
524501c574fSSean Wang {
525501c574fSSean Wang 	struct mtk_cpu_dvfs_info *info, *tmp;
526501c574fSSean Wang 	int cpu, ret;
527501c574fSSean Wang 
528501c574fSSean Wang 	for_each_possible_cpu(cpu) {
529501c574fSSean Wang 		info = mtk_cpu_dvfs_info_lookup(cpu);
530501c574fSSean Wang 		if (info)
531501c574fSSean Wang 			continue;
532501c574fSSean Wang 
533501c574fSSean Wang 		info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
534501c574fSSean Wang 		if (!info) {
535501c574fSSean Wang 			ret = -ENOMEM;
536501c574fSSean Wang 			goto release_dvfs_info_list;
537501c574fSSean Wang 		}
538501c574fSSean Wang 
539501c574fSSean Wang 		ret = mtk_cpu_dvfs_info_init(info, cpu);
540501c574fSSean Wang 		if (ret) {
541501c574fSSean Wang 			dev_err(&pdev->dev,
542501c574fSSean Wang 				"failed to initialize dvfs info for cpu%d\n",
543501c574fSSean Wang 				cpu);
544501c574fSSean Wang 			goto release_dvfs_info_list;
545501c574fSSean Wang 		}
546501c574fSSean Wang 
547501c574fSSean Wang 		list_add(&info->list_head, &dvfs_info_list);
548501c574fSSean Wang 	}
549501c574fSSean Wang 
550862e0104SSean Wang 	ret = cpufreq_register_driver(&mtk_cpufreq_driver);
551501c574fSSean Wang 	if (ret) {
552501c574fSSean Wang 		dev_err(&pdev->dev, "failed to register mtk cpufreq driver\n");
553501c574fSSean Wang 		goto release_dvfs_info_list;
554501c574fSSean Wang 	}
555501c574fSSean Wang 
556501c574fSSean Wang 	return 0;
557501c574fSSean Wang 
558501c574fSSean Wang release_dvfs_info_list:
559501c574fSSean Wang 	list_for_each_entry_safe(info, tmp, &dvfs_info_list, list_head) {
560501c574fSSean Wang 		mtk_cpu_dvfs_info_release(info);
561501c574fSSean Wang 		list_del(&info->list_head);
562501c574fSSean Wang 	}
563501c574fSSean Wang 
564501c574fSSean Wang 	return ret;
565501c574fSSean Wang }
566501c574fSSean Wang 
567862e0104SSean Wang static struct platform_driver mtk_cpufreq_platdrv = {
568501c574fSSean Wang 	.driver = {
569862e0104SSean Wang 		.name	= "mtk-cpufreq",
570501c574fSSean Wang 	},
571862e0104SSean Wang 	.probe		= mtk_cpufreq_probe,
572501c574fSSean Wang };
573501c574fSSean Wang 
574501c574fSSean Wang /* List of machines supported by this driver */
575862e0104SSean Wang static const struct of_device_id mtk_cpufreq_machines[] __initconst = {
576501c574fSSean Wang 	{ .compatible = "mediatek,mt2701", },
577ccc03d86SSean Wang 	{ .compatible = "mediatek,mt7622", },
578501c574fSSean Wang 	{ .compatible = "mediatek,mt7623", },
579501c574fSSean Wang 	{ .compatible = "mediatek,mt817x", },
580501c574fSSean Wang 	{ .compatible = "mediatek,mt8173", },
581501c574fSSean Wang 	{ .compatible = "mediatek,mt8176", },
582501c574fSSean Wang 
583501c574fSSean Wang 	{ }
584501c574fSSean Wang };
585501c574fSSean Wang 
586862e0104SSean Wang static int __init mtk_cpufreq_driver_init(void)
587501c574fSSean Wang {
588501c574fSSean Wang 	struct device_node *np;
589501c574fSSean Wang 	const struct of_device_id *match;
590501c574fSSean Wang 	struct platform_device *pdev;
591501c574fSSean Wang 	int err;
592501c574fSSean Wang 
593501c574fSSean Wang 	np = of_find_node_by_path("/");
594501c574fSSean Wang 	if (!np)
595501c574fSSean Wang 		return -ENODEV;
596501c574fSSean Wang 
597862e0104SSean Wang 	match = of_match_node(mtk_cpufreq_machines, np);
598501c574fSSean Wang 	of_node_put(np);
599501c574fSSean Wang 	if (!match) {
600862e0104SSean Wang 		pr_warn("Machine is not compatible with mtk-cpufreq\n");
601501c574fSSean Wang 		return -ENODEV;
602501c574fSSean Wang 	}
603501c574fSSean Wang 
604862e0104SSean Wang 	err = platform_driver_register(&mtk_cpufreq_platdrv);
605501c574fSSean Wang 	if (err)
606501c574fSSean Wang 		return err;
607501c574fSSean Wang 
608501c574fSSean Wang 	/*
609501c574fSSean Wang 	 * Since there's no place to hold device registration code and no
610501c574fSSean Wang 	 * device tree based way to match cpufreq driver yet, both the driver
611501c574fSSean Wang 	 * and the device registration codes are put here to handle defer
612501c574fSSean Wang 	 * probing.
613501c574fSSean Wang 	 */
614862e0104SSean Wang 	pdev = platform_device_register_simple("mtk-cpufreq", -1, NULL, 0);
615501c574fSSean Wang 	if (IS_ERR(pdev)) {
616501c574fSSean Wang 		pr_err("failed to register mtk-cpufreq platform device\n");
617501c574fSSean Wang 		return PTR_ERR(pdev);
618501c574fSSean Wang 	}
619501c574fSSean Wang 
620501c574fSSean Wang 	return 0;
621501c574fSSean Wang }
622862e0104SSean Wang device_initcall(mtk_cpufreq_driver_init);
623