11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2501c574fSSean Wang /*
3501c574fSSean Wang  * Copyright (c) 2015 Linaro Ltd.
4501c574fSSean Wang  * Author: Pi-Cheng Chen <pi-cheng.chen@linaro.org>
5501c574fSSean Wang  */
6501c574fSSean Wang 
7501c574fSSean Wang #include <linux/clk.h>
8501c574fSSean Wang #include <linux/cpu.h>
9501c574fSSean Wang #include <linux/cpufreq.h>
10501c574fSSean Wang #include <linux/cpumask.h>
116a17b387SJia-Wei Chang #include <linux/minmax.h>
12501c574fSSean Wang #include <linux/module.h>
13501c574fSSean Wang #include <linux/of.h>
14ead858bdSRex-BC Chen #include <linux/of_platform.h>
15501c574fSSean Wang #include <linux/platform_device.h>
16501c574fSSean Wang #include <linux/pm_opp.h>
17501c574fSSean Wang #include <linux/regulator/consumer.h>
18501c574fSSean Wang 
19ead858bdSRex-BC Chen struct mtk_cpufreq_platform_data {
20ead858bdSRex-BC Chen 	int min_volt_shift;
21ead858bdSRex-BC Chen 	int max_volt_shift;
22ead858bdSRex-BC Chen 	int proc_max_volt;
23ead858bdSRex-BC Chen 	int sram_min_volt;
24ead858bdSRex-BC Chen 	int sram_max_volt;
250daa4732SRex-BC Chen 	bool ccifreq_supported;
26ead858bdSRex-BC Chen };
27ead858bdSRex-BC Chen 
28501c574fSSean Wang /*
29501c574fSSean Wang  * The struct mtk_cpu_dvfs_info holds necessary information for doing CPU DVFS
30501c574fSSean Wang  * on each CPU power/clock domain of Mediatek SoCs. Each CPU cluster in
31501c574fSSean Wang  * Mediatek SoCs has two voltage inputs, Vproc and Vsram. In some cases the two
32501c574fSSean Wang  * voltage inputs need to be controlled under a hardware limitation:
33501c574fSSean Wang  * 100mV < Vsram - Vproc < 200mV
34501c574fSSean Wang  *
35501c574fSSean Wang  * When scaling the clock frequency of a CPU clock domain, the clock source
36501c574fSSean Wang  * needs to be switched to another stable PLL clock temporarily until
37501c574fSSean Wang  * the original PLL becomes stable at target frequency.
38501c574fSSean Wang  */
39501c574fSSean Wang struct mtk_cpu_dvfs_info {
40501c574fSSean Wang 	struct cpumask cpus;
41501c574fSSean Wang 	struct device *cpu_dev;
420daa4732SRex-BC Chen 	struct device *cci_dev;
43501c574fSSean Wang 	struct regulator *proc_reg;
44501c574fSSean Wang 	struct regulator *sram_reg;
45501c574fSSean Wang 	struct clk *cpu_clk;
46501c574fSSean Wang 	struct clk *inter_clk;
47501c574fSSean Wang 	struct list_head list_head;
48501c574fSSean Wang 	int intermediate_voltage;
49501c574fSSean Wang 	bool need_voltage_tracking;
500daa4732SRex-BC Chen 	int vproc_on_boot;
51bffcd333SJia-Wei Chang 	int pre_vproc;
52c210063bSRex-BC Chen 	/* Avoid race condition for regulators between notify and policy */
53c210063bSRex-BC Chen 	struct mutex reg_lock;
54c210063bSRex-BC Chen 	struct notifier_block opp_nb;
55c210063bSRex-BC Chen 	unsigned int opp_cpu;
56c210063bSRex-BC Chen 	unsigned long current_freq;
57ead858bdSRex-BC Chen 	const struct mtk_cpufreq_platform_data *soc_data;
586a17b387SJia-Wei Chang 	int vtrack_max;
590daa4732SRex-BC Chen 	bool ccifreq_bound;
60501c574fSSean Wang };
61501c574fSSean Wang 
62f126fbadSRex-BC Chen static struct platform_device *cpufreq_pdev;
63f126fbadSRex-BC Chen 
64501c574fSSean Wang static LIST_HEAD(dvfs_info_list);
65501c574fSSean Wang 
mtk_cpu_dvfs_info_lookup(int cpu)66501c574fSSean Wang static struct mtk_cpu_dvfs_info *mtk_cpu_dvfs_info_lookup(int cpu)
67501c574fSSean Wang {
68501c574fSSean Wang 	struct mtk_cpu_dvfs_info *info;
69501c574fSSean Wang 
70501c574fSSean Wang 	list_for_each_entry(info, &dvfs_info_list, list_head) {
71501c574fSSean Wang 		if (cpumask_test_cpu(cpu, &info->cpus))
72501c574fSSean Wang 			return info;
73501c574fSSean Wang 	}
74501c574fSSean Wang 
75501c574fSSean Wang 	return NULL;
76501c574fSSean Wang }
77501c574fSSean Wang 
mtk_cpufreq_voltage_tracking(struct mtk_cpu_dvfs_info * info,int new_vproc)78501c574fSSean Wang static int mtk_cpufreq_voltage_tracking(struct mtk_cpu_dvfs_info *info,
79501c574fSSean Wang 					int new_vproc)
80501c574fSSean Wang {
81ead858bdSRex-BC Chen 	const struct mtk_cpufreq_platform_data *soc_data = info->soc_data;
82501c574fSSean Wang 	struct regulator *proc_reg = info->proc_reg;
83501c574fSSean Wang 	struct regulator *sram_reg = info->sram_reg;
844aef4aeaSRex-BC Chen 	int pre_vproc, pre_vsram, new_vsram, vsram, vproc, ret;
856a17b387SJia-Wei Chang 	int retry = info->vtrack_max;
86501c574fSSean Wang 
874aef4aeaSRex-BC Chen 	pre_vproc = regulator_get_voltage(proc_reg);
884aef4aeaSRex-BC Chen 	if (pre_vproc < 0) {
899acc0f7aSRex-BC Chen 		dev_err(info->cpu_dev,
904aef4aeaSRex-BC Chen 			"invalid Vproc value: %d\n", pre_vproc);
914aef4aeaSRex-BC Chen 		return pre_vproc;
92501c574fSSean Wang 	}
936a17b387SJia-Wei Chang 
946a17b387SJia-Wei Chang 	pre_vsram = regulator_get_voltage(sram_reg);
956a17b387SJia-Wei Chang 	if (pre_vsram < 0) {
966a17b387SJia-Wei Chang 		dev_err(info->cpu_dev, "invalid Vsram value: %d\n", pre_vsram);
976a17b387SJia-Wei Chang 		return pre_vsram;
986a17b387SJia-Wei Chang 	}
996a17b387SJia-Wei Chang 
1006a17b387SJia-Wei Chang 	new_vsram = clamp(new_vproc + soc_data->min_volt_shift,
1016a17b387SJia-Wei Chang 			  soc_data->sram_min_volt, soc_data->sram_max_volt);
1026a17b387SJia-Wei Chang 
1036a17b387SJia-Wei Chang 	do {
1046a17b387SJia-Wei Chang 		if (pre_vproc <= new_vproc) {
1056a17b387SJia-Wei Chang 			vsram = clamp(pre_vproc + soc_data->max_volt_shift,
1066a17b387SJia-Wei Chang 				      soc_data->sram_min_volt, new_vsram);
1076a17b387SJia-Wei Chang 			ret = regulator_set_voltage(sram_reg, vsram,
108ead858bdSRex-BC Chen 						    soc_data->sram_max_volt);
109501c574fSSean Wang 
110501c574fSSean Wang 			if (ret)
111501c574fSSean Wang 				return ret;
112501c574fSSean Wang 
1136a17b387SJia-Wei Chang 			if (vsram == soc_data->sram_max_volt ||
1146a17b387SJia-Wei Chang 			    new_vsram == soc_data->sram_min_volt)
1156a17b387SJia-Wei Chang 				vproc = new_vproc;
1166a17b387SJia-Wei Chang 			else
1176a17b387SJia-Wei Chang 				vproc = vsram - soc_data->min_volt_shift;
1186a17b387SJia-Wei Chang 
119501c574fSSean Wang 			ret = regulator_set_voltage(proc_reg, vproc,
1206a17b387SJia-Wei Chang 						    soc_data->proc_max_volt);
121501c574fSSean Wang 			if (ret) {
1224aef4aeaSRex-BC Chen 				regulator_set_voltage(sram_reg, pre_vsram,
1236a17b387SJia-Wei Chang 						      soc_data->sram_max_volt);
124501c574fSSean Wang 				return ret;
125501c574fSSean Wang 			}
1264aef4aeaSRex-BC Chen 		} else if (pre_vproc > new_vproc) {
127ead858bdSRex-BC Chen 			vproc = max(new_vproc,
128ead858bdSRex-BC Chen 				    pre_vsram - soc_data->max_volt_shift);
129501c574fSSean Wang 			ret = regulator_set_voltage(proc_reg, vproc,
1306a17b387SJia-Wei Chang 						    soc_data->proc_max_volt);
131501c574fSSean Wang 			if (ret)
132501c574fSSean Wang 				return ret;
133501c574fSSean Wang 
134501c574fSSean Wang 			if (vproc == new_vproc)
135501c574fSSean Wang 				vsram = new_vsram;
136501c574fSSean Wang 			else
137ead858bdSRex-BC Chen 				vsram = max(new_vsram,
138ead858bdSRex-BC Chen 					    vproc + soc_data->min_volt_shift);
139501c574fSSean Wang 
140501c574fSSean Wang 			ret = regulator_set_voltage(sram_reg, vsram,
1416a17b387SJia-Wei Chang 						    soc_data->sram_max_volt);
142501c574fSSean Wang 			if (ret) {
1434aef4aeaSRex-BC Chen 				regulator_set_voltage(proc_reg, pre_vproc,
1446a17b387SJia-Wei Chang 						      soc_data->proc_max_volt);
145501c574fSSean Wang 				return ret;
146501c574fSSean Wang 			}
147501c574fSSean Wang 		}
148501c574fSSean Wang 
1496a17b387SJia-Wei Chang 		pre_vproc = vproc;
1506a17b387SJia-Wei Chang 		pre_vsram = vsram;
1516a17b387SJia-Wei Chang 
1526a17b387SJia-Wei Chang 		if (--retry < 0) {
1536a17b387SJia-Wei Chang 			dev_err(info->cpu_dev,
1546a17b387SJia-Wei Chang 				"over loop count, failed to set voltage\n");
1556a17b387SJia-Wei Chang 			return -EINVAL;
1566a17b387SJia-Wei Chang 		}
1576a17b387SJia-Wei Chang 	} while (vproc != new_vproc || vsram != new_vsram);
1586a17b387SJia-Wei Chang 
159501c574fSSean Wang 	return 0;
160501c574fSSean Wang }
161501c574fSSean Wang 
mtk_cpufreq_set_voltage(struct mtk_cpu_dvfs_info * info,int vproc)162501c574fSSean Wang static int mtk_cpufreq_set_voltage(struct mtk_cpu_dvfs_info *info, int vproc)
163501c574fSSean Wang {
164ead858bdSRex-BC Chen 	const struct mtk_cpufreq_platform_data *soc_data = info->soc_data;
165bffcd333SJia-Wei Chang 	int ret;
166bffcd333SJia-Wei Chang 
167501c574fSSean Wang 	if (info->need_voltage_tracking)
168bffcd333SJia-Wei Chang 		ret = mtk_cpufreq_voltage_tracking(info, vproc);
169501c574fSSean Wang 	else
170bffcd333SJia-Wei Chang 		ret = regulator_set_voltage(info->proc_reg, vproc,
171ead858bdSRex-BC Chen 					    soc_data->proc_max_volt);
172bffcd333SJia-Wei Chang 	if (!ret)
173bffcd333SJia-Wei Chang 		info->pre_vproc = vproc;
174bffcd333SJia-Wei Chang 
175bffcd333SJia-Wei Chang 	return ret;
176501c574fSSean Wang }
177501c574fSSean Wang 
is_ccifreq_ready(struct mtk_cpu_dvfs_info * info)1780daa4732SRex-BC Chen static bool is_ccifreq_ready(struct mtk_cpu_dvfs_info *info)
1790daa4732SRex-BC Chen {
1800daa4732SRex-BC Chen 	struct device_link *sup_link;
1810daa4732SRex-BC Chen 
1820daa4732SRex-BC Chen 	if (info->ccifreq_bound)
1830daa4732SRex-BC Chen 		return true;
1840daa4732SRex-BC Chen 
1850daa4732SRex-BC Chen 	sup_link = device_link_add(info->cpu_dev, info->cci_dev,
1860daa4732SRex-BC Chen 				   DL_FLAG_AUTOREMOVE_CONSUMER);
1870daa4732SRex-BC Chen 	if (!sup_link) {
1880daa4732SRex-BC Chen 		dev_err(info->cpu_dev, "cpu%d: sup_link is NULL\n", info->opp_cpu);
1890daa4732SRex-BC Chen 		return false;
1900daa4732SRex-BC Chen 	}
1910daa4732SRex-BC Chen 
1920daa4732SRex-BC Chen 	if (sup_link->supplier->links.status != DL_DEV_DRIVER_BOUND)
1930daa4732SRex-BC Chen 		return false;
1940daa4732SRex-BC Chen 
1950daa4732SRex-BC Chen 	info->ccifreq_bound = true;
1960daa4732SRex-BC Chen 
1970daa4732SRex-BC Chen 	return true;
1980daa4732SRex-BC Chen }
1990daa4732SRex-BC Chen 
mtk_cpufreq_set_target(struct cpufreq_policy * policy,unsigned int index)200501c574fSSean Wang static int mtk_cpufreq_set_target(struct cpufreq_policy *policy,
201501c574fSSean Wang 				  unsigned int index)
202501c574fSSean Wang {
203501c574fSSean Wang 	struct cpufreq_frequency_table *freq_table = policy->freq_table;
204501c574fSSean Wang 	struct clk *cpu_clk = policy->clk;
205501c574fSSean Wang 	struct clk *armpll = clk_get_parent(cpu_clk);
206501c574fSSean Wang 	struct mtk_cpu_dvfs_info *info = policy->driver_data;
207501c574fSSean Wang 	struct device *cpu_dev = info->cpu_dev;
208501c574fSSean Wang 	struct dev_pm_opp *opp;
2094aef4aeaSRex-BC Chen 	long freq_hz, pre_freq_hz;
2104aef4aeaSRex-BC Chen 	int vproc, pre_vproc, inter_vproc, target_vproc, ret;
211501c574fSSean Wang 
212501c574fSSean Wang 	inter_vproc = info->intermediate_voltage;
213501c574fSSean Wang 
2144aef4aeaSRex-BC Chen 	pre_freq_hz = clk_get_rate(cpu_clk);
215bffcd333SJia-Wei Chang 
216c210063bSRex-BC Chen 	mutex_lock(&info->reg_lock);
217c210063bSRex-BC Chen 
218bffcd333SJia-Wei Chang 	if (unlikely(info->pre_vproc <= 0))
2194aef4aeaSRex-BC Chen 		pre_vproc = regulator_get_voltage(info->proc_reg);
220bffcd333SJia-Wei Chang 	else
221bffcd333SJia-Wei Chang 		pre_vproc = info->pre_vproc;
222bffcd333SJia-Wei Chang 
2234aef4aeaSRex-BC Chen 	if (pre_vproc < 0) {
2244aef4aeaSRex-BC Chen 		dev_err(cpu_dev, "invalid Vproc value: %d\n", pre_vproc);
22585f5b3c4SWan Jiabing 		ret = pre_vproc;
22685f5b3c4SWan Jiabing 		goto out;
227501c574fSSean Wang 	}
228501c574fSSean Wang 
229501c574fSSean Wang 	freq_hz = freq_table[index].frequency * 1000;
230501c574fSSean Wang 
231501c574fSSean Wang 	opp = dev_pm_opp_find_freq_ceil(cpu_dev, &freq_hz);
232501c574fSSean Wang 	if (IS_ERR(opp)) {
2339acc0f7aSRex-BC Chen 		dev_err(cpu_dev, "cpu%d: failed to find OPP for %ld\n",
234501c574fSSean Wang 			policy->cpu, freq_hz);
23585f5b3c4SWan Jiabing 		ret = PTR_ERR(opp);
23685f5b3c4SWan Jiabing 		goto out;
237501c574fSSean Wang 	}
238501c574fSSean Wang 	vproc = dev_pm_opp_get_voltage(opp);
239501c574fSSean Wang 	dev_pm_opp_put(opp);
240501c574fSSean Wang 
241501c574fSSean Wang 	/*
2420daa4732SRex-BC Chen 	 * If MediaTek cci is supported but is not ready, we will use the value
2430daa4732SRex-BC Chen 	 * of max(target cpu voltage, booting voltage) to prevent high freqeuncy
2440daa4732SRex-BC Chen 	 * low voltage crash.
2450daa4732SRex-BC Chen 	 */
2460daa4732SRex-BC Chen 	if (info->soc_data->ccifreq_supported && !is_ccifreq_ready(info))
2470daa4732SRex-BC Chen 		vproc = max(vproc, info->vproc_on_boot);
2480daa4732SRex-BC Chen 
2490daa4732SRex-BC Chen 	/*
250501c574fSSean Wang 	 * If the new voltage or the intermediate voltage is higher than the
251501c574fSSean Wang 	 * current voltage, scale up voltage first.
252501c574fSSean Wang 	 */
2536a17b387SJia-Wei Chang 	target_vproc = max(inter_vproc, vproc);
2546a17b387SJia-Wei Chang 	if (pre_vproc <= target_vproc) {
255501c574fSSean Wang 		ret = mtk_cpufreq_set_voltage(info, target_vproc);
256501c574fSSean Wang 		if (ret) {
2579acc0f7aSRex-BC Chen 			dev_err(cpu_dev,
2589acc0f7aSRex-BC Chen 				"cpu%d: failed to scale up voltage!\n", policy->cpu);
2594aef4aeaSRex-BC Chen 			mtk_cpufreq_set_voltage(info, pre_vproc);
260c210063bSRex-BC Chen 			goto out;
261501c574fSSean Wang 		}
262501c574fSSean Wang 	}
263501c574fSSean Wang 
264501c574fSSean Wang 	/* Reparent the CPU clock to intermediate clock. */
265501c574fSSean Wang 	ret = clk_set_parent(cpu_clk, info->inter_clk);
266501c574fSSean Wang 	if (ret) {
2679acc0f7aSRex-BC Chen 		dev_err(cpu_dev,
2689acc0f7aSRex-BC Chen 			"cpu%d: failed to re-parent cpu clock!\n", policy->cpu);
2694aef4aeaSRex-BC Chen 		mtk_cpufreq_set_voltage(info, pre_vproc);
270c210063bSRex-BC Chen 		goto out;
271501c574fSSean Wang 	}
272501c574fSSean Wang 
273501c574fSSean Wang 	/* Set the original PLL to target rate. */
274501c574fSSean Wang 	ret = clk_set_rate(armpll, freq_hz);
275501c574fSSean Wang 	if (ret) {
2769acc0f7aSRex-BC Chen 		dev_err(cpu_dev,
2779acc0f7aSRex-BC Chen 			"cpu%d: failed to scale cpu clock rate!\n", policy->cpu);
278501c574fSSean Wang 		clk_set_parent(cpu_clk, armpll);
2794aef4aeaSRex-BC Chen 		mtk_cpufreq_set_voltage(info, pre_vproc);
280c210063bSRex-BC Chen 		goto out;
281501c574fSSean Wang 	}
282501c574fSSean Wang 
283501c574fSSean Wang 	/* Set parent of CPU clock back to the original PLL. */
284501c574fSSean Wang 	ret = clk_set_parent(cpu_clk, armpll);
285501c574fSSean Wang 	if (ret) {
2869acc0f7aSRex-BC Chen 		dev_err(cpu_dev,
2879acc0f7aSRex-BC Chen 			"cpu%d: failed to re-parent cpu clock!\n", policy->cpu);
288501c574fSSean Wang 		mtk_cpufreq_set_voltage(info, inter_vproc);
289c210063bSRex-BC Chen 		goto out;
290501c574fSSean Wang 	}
291501c574fSSean Wang 
292501c574fSSean Wang 	/*
293501c574fSSean Wang 	 * If the new voltage is lower than the intermediate voltage or the
294501c574fSSean Wang 	 * original voltage, scale down to the new voltage.
295501c574fSSean Wang 	 */
2964aef4aeaSRex-BC Chen 	if (vproc < inter_vproc || vproc < pre_vproc) {
297501c574fSSean Wang 		ret = mtk_cpufreq_set_voltage(info, vproc);
298501c574fSSean Wang 		if (ret) {
2999acc0f7aSRex-BC Chen 			dev_err(cpu_dev,
3009acc0f7aSRex-BC Chen 				"cpu%d: failed to scale down voltage!\n", policy->cpu);
301501c574fSSean Wang 			clk_set_parent(cpu_clk, info->inter_clk);
3024aef4aeaSRex-BC Chen 			clk_set_rate(armpll, pre_freq_hz);
303501c574fSSean Wang 			clk_set_parent(cpu_clk, armpll);
304c210063bSRex-BC Chen 			goto out;
305501c574fSSean Wang 		}
306501c574fSSean Wang 	}
307501c574fSSean Wang 
308c210063bSRex-BC Chen 	info->current_freq = freq_hz;
309c210063bSRex-BC Chen 
310c210063bSRex-BC Chen out:
311c210063bSRex-BC Chen 	mutex_unlock(&info->reg_lock);
312c210063bSRex-BC Chen 
313c210063bSRex-BC Chen 	return ret;
314501c574fSSean Wang }
315501c574fSSean Wang 
mtk_cpufreq_opp_notifier(struct notifier_block * nb,unsigned long event,void * data)316c210063bSRex-BC Chen static int mtk_cpufreq_opp_notifier(struct notifier_block *nb,
317c210063bSRex-BC Chen 				    unsigned long event, void *data)
318c210063bSRex-BC Chen {
319c210063bSRex-BC Chen 	struct dev_pm_opp *opp = data;
320c210063bSRex-BC Chen 	struct dev_pm_opp *new_opp;
321c210063bSRex-BC Chen 	struct mtk_cpu_dvfs_info *info;
322c210063bSRex-BC Chen 	unsigned long freq, volt;
323c210063bSRex-BC Chen 	struct cpufreq_policy *policy;
324c210063bSRex-BC Chen 	int ret = 0;
325c210063bSRex-BC Chen 
326c210063bSRex-BC Chen 	info = container_of(nb, struct mtk_cpu_dvfs_info, opp_nb);
327c210063bSRex-BC Chen 
328c210063bSRex-BC Chen 	if (event == OPP_EVENT_ADJUST_VOLTAGE) {
329c210063bSRex-BC Chen 		freq = dev_pm_opp_get_freq(opp);
330c210063bSRex-BC Chen 
331c210063bSRex-BC Chen 		mutex_lock(&info->reg_lock);
332c210063bSRex-BC Chen 		if (info->current_freq == freq) {
333c210063bSRex-BC Chen 			volt = dev_pm_opp_get_voltage(opp);
334c210063bSRex-BC Chen 			ret = mtk_cpufreq_set_voltage(info, volt);
335c210063bSRex-BC Chen 			if (ret)
336c210063bSRex-BC Chen 				dev_err(info->cpu_dev,
337c210063bSRex-BC Chen 					"failed to scale voltage: %d\n", ret);
338c210063bSRex-BC Chen 		}
339c210063bSRex-BC Chen 		mutex_unlock(&info->reg_lock);
340c210063bSRex-BC Chen 	} else if (event == OPP_EVENT_DISABLE) {
341c210063bSRex-BC Chen 		freq = dev_pm_opp_get_freq(opp);
342c210063bSRex-BC Chen 
343c210063bSRex-BC Chen 		/* case of current opp item is disabled */
344c210063bSRex-BC Chen 		if (info->current_freq == freq) {
345c210063bSRex-BC Chen 			freq = 1;
346c210063bSRex-BC Chen 			new_opp = dev_pm_opp_find_freq_ceil(info->cpu_dev,
347c210063bSRex-BC Chen 							    &freq);
348c210063bSRex-BC Chen 			if (IS_ERR(new_opp)) {
349c210063bSRex-BC Chen 				dev_err(info->cpu_dev,
350c210063bSRex-BC Chen 					"all opp items are disabled\n");
351c210063bSRex-BC Chen 				ret = PTR_ERR(new_opp);
352c210063bSRex-BC Chen 				return notifier_from_errno(ret);
353c210063bSRex-BC Chen 			}
354c210063bSRex-BC Chen 
355c210063bSRex-BC Chen 			dev_pm_opp_put(new_opp);
356c210063bSRex-BC Chen 			policy = cpufreq_cpu_get(info->opp_cpu);
357c210063bSRex-BC Chen 			if (policy) {
358c210063bSRex-BC Chen 				cpufreq_driver_target(policy, freq / 1000,
359c210063bSRex-BC Chen 						      CPUFREQ_RELATION_L);
360c210063bSRex-BC Chen 				cpufreq_cpu_put(policy);
361c210063bSRex-BC Chen 			}
362c210063bSRex-BC Chen 		}
363c210063bSRex-BC Chen 	}
364c210063bSRex-BC Chen 
365c210063bSRex-BC Chen 	return notifier_from_errno(ret);
366c210063bSRex-BC Chen }
367c210063bSRex-BC Chen 
of_get_cci(struct device * cpu_dev)3680daa4732SRex-BC Chen static struct device *of_get_cci(struct device *cpu_dev)
3690daa4732SRex-BC Chen {
3700daa4732SRex-BC Chen 	struct device_node *np;
3710daa4732SRex-BC Chen 	struct platform_device *pdev;
3720daa4732SRex-BC Chen 
3730daa4732SRex-BC Chen 	np = of_parse_phandle(cpu_dev->of_node, "mediatek,cci", 0);
374d51c6323SJia-Wei Chang 	if (!np)
375d51c6323SJia-Wei Chang 		return ERR_PTR(-ENODEV);
3760daa4732SRex-BC Chen 
3770daa4732SRex-BC Chen 	pdev = of_find_device_by_node(np);
3780daa4732SRex-BC Chen 	of_node_put(np);
379d51c6323SJia-Wei Chang 	if (!pdev)
380d51c6323SJia-Wei Chang 		return ERR_PTR(-ENODEV);
3810daa4732SRex-BC Chen 
3820daa4732SRex-BC Chen 	return &pdev->dev;
3830daa4732SRex-BC Chen }
3840daa4732SRex-BC Chen 
mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info * info,int cpu)385501c574fSSean Wang static int mtk_cpu_dvfs_info_init(struct mtk_cpu_dvfs_info *info, int cpu)
386501c574fSSean Wang {
387501c574fSSean Wang 	struct device *cpu_dev;
388501c574fSSean Wang 	struct dev_pm_opp *opp;
389501c574fSSean Wang 	unsigned long rate;
390501c574fSSean Wang 	int ret;
391501c574fSSean Wang 
392501c574fSSean Wang 	cpu_dev = get_cpu_device(cpu);
393501c574fSSean Wang 	if (!cpu_dev) {
394396dee97SJia-Wei Chang 		dev_err(cpu_dev, "failed to get cpu%d device\n", cpu);
395501c574fSSean Wang 		return -ENODEV;
396501c574fSSean Wang 	}
397396dee97SJia-Wei Chang 	info->cpu_dev = cpu_dev;
398501c574fSSean Wang 
3990daa4732SRex-BC Chen 	info->ccifreq_bound = false;
4000daa4732SRex-BC Chen 	if (info->soc_data->ccifreq_supported) {
4010daa4732SRex-BC Chen 		info->cci_dev = of_get_cci(info->cpu_dev);
402d51c6323SJia-Wei Chang 		if (IS_ERR(info->cci_dev)) {
4030daa4732SRex-BC Chen 			ret = PTR_ERR(info->cci_dev);
4040daa4732SRex-BC Chen 			dev_err(cpu_dev, "cpu%d: failed to get cci device\n", cpu);
4050daa4732SRex-BC Chen 			return -ENODEV;
4060daa4732SRex-BC Chen 		}
4070daa4732SRex-BC Chen 	}
4080daa4732SRex-BC Chen 
409396dee97SJia-Wei Chang 	info->cpu_clk = clk_get(cpu_dev, "cpu");
410396dee97SJia-Wei Chang 	if (IS_ERR(info->cpu_clk)) {
411396dee97SJia-Wei Chang 		ret = PTR_ERR(info->cpu_clk);
412396dee97SJia-Wei Chang 		return dev_err_probe(cpu_dev, ret,
413396dee97SJia-Wei Chang 				     "cpu%d: failed to get cpu clk\n", cpu);
414501c574fSSean Wang 	}
415501c574fSSean Wang 
416396dee97SJia-Wei Chang 	info->inter_clk = clk_get(cpu_dev, "intermediate");
417396dee97SJia-Wei Chang 	if (IS_ERR(info->inter_clk)) {
418396dee97SJia-Wei Chang 		ret = PTR_ERR(info->inter_clk);
419396dee97SJia-Wei Chang 		dev_err_probe(cpu_dev, ret,
420396dee97SJia-Wei Chang 			      "cpu%d: failed to get intermediate clk\n", cpu);
421d51e1062SJia-Wei Chang 		goto out_free_mux_clock;
422501c574fSSean Wang 	}
423501c574fSSean Wang 
424396dee97SJia-Wei Chang 	info->proc_reg = regulator_get_optional(cpu_dev, "proc");
425396dee97SJia-Wei Chang 	if (IS_ERR(info->proc_reg)) {
426396dee97SJia-Wei Chang 		ret = PTR_ERR(info->proc_reg);
427396dee97SJia-Wei Chang 		dev_err_probe(cpu_dev, ret,
428396dee97SJia-Wei Chang 			      "cpu%d: failed to get proc regulator\n", cpu);
429d51e1062SJia-Wei Chang 		goto out_free_inter_clock;
430501c574fSSean Wang 	}
431501c574fSSean Wang 
4324b9ceb75SJia-Wei Chang 	ret = regulator_enable(info->proc_reg);
4334b9ceb75SJia-Wei Chang 	if (ret) {
4344b9ceb75SJia-Wei Chang 		dev_warn(cpu_dev, "cpu%d: failed to enable vproc\n", cpu);
435d51e1062SJia-Wei Chang 		goto out_free_proc_reg;
4364b9ceb75SJia-Wei Chang 	}
4374b9ceb75SJia-Wei Chang 
438501c574fSSean Wang 	/* Both presence and absence of sram regulator are valid cases. */
439ffa7bdf7SJia-Wei Chang 	info->sram_reg = regulator_get_optional(cpu_dev, "sram");
440d2394860SAngeloGioacchino Del Regno 	if (IS_ERR(info->sram_reg)) {
441d2394860SAngeloGioacchino Del Regno 		ret = PTR_ERR(info->sram_reg);
442d2394860SAngeloGioacchino Del Regno 		if (ret == -EPROBE_DEFER)
443d51e1062SJia-Wei Chang 			goto out_disable_proc_reg;
444d2394860SAngeloGioacchino Del Regno 
445396dee97SJia-Wei Chang 		info->sram_reg = NULL;
446d2394860SAngeloGioacchino Del Regno 	} else {
4474b9ceb75SJia-Wei Chang 		ret = regulator_enable(info->sram_reg);
4484b9ceb75SJia-Wei Chang 		if (ret) {
4494b9ceb75SJia-Wei Chang 			dev_warn(cpu_dev, "cpu%d: failed to enable vsram\n", cpu);
450d51e1062SJia-Wei Chang 			goto out_free_sram_reg;
4514b9ceb75SJia-Wei Chang 		}
4524b9ceb75SJia-Wei Chang 	}
453501c574fSSean Wang 
454501c574fSSean Wang 	/* Get OPP-sharing information from "operating-points-v2" bindings */
455501c574fSSean Wang 	ret = dev_pm_opp_of_get_sharing_cpus(cpu_dev, &info->cpus);
456501c574fSSean Wang 	if (ret) {
457396dee97SJia-Wei Chang 		dev_err(cpu_dev,
458396dee97SJia-Wei Chang 			"cpu%d: failed to get OPP-sharing information\n", cpu);
459d51e1062SJia-Wei Chang 		goto out_disable_sram_reg;
460501c574fSSean Wang 	}
461501c574fSSean Wang 
462501c574fSSean Wang 	ret = dev_pm_opp_of_cpumask_add_table(&info->cpus);
463501c574fSSean Wang 	if (ret) {
464396dee97SJia-Wei Chang 		dev_warn(cpu_dev, "cpu%d: no OPP table\n", cpu);
465d51e1062SJia-Wei Chang 		goto out_disable_sram_reg;
466501c574fSSean Wang 	}
467501c574fSSean Wang 
4684b9ceb75SJia-Wei Chang 	ret = clk_prepare_enable(info->cpu_clk);
4694b9ceb75SJia-Wei Chang 	if (ret)
4704b9ceb75SJia-Wei Chang 		goto out_free_opp_table;
4714b9ceb75SJia-Wei Chang 
4724b9ceb75SJia-Wei Chang 	ret = clk_prepare_enable(info->inter_clk);
4734b9ceb75SJia-Wei Chang 	if (ret)
4744b9ceb75SJia-Wei Chang 		goto out_disable_mux_clock;
4754b9ceb75SJia-Wei Chang 
4760daa4732SRex-BC Chen 	if (info->soc_data->ccifreq_supported) {
4770daa4732SRex-BC Chen 		info->vproc_on_boot = regulator_get_voltage(info->proc_reg);
4780daa4732SRex-BC Chen 		if (info->vproc_on_boot < 0) {
47972d67d6bSYang Yingliang 			ret = info->vproc_on_boot;
4800daa4732SRex-BC Chen 			dev_err(info->cpu_dev,
4810daa4732SRex-BC Chen 				"invalid Vproc value: %d\n", info->vproc_on_boot);
4820daa4732SRex-BC Chen 			goto out_disable_inter_clock;
4830daa4732SRex-BC Chen 		}
4840daa4732SRex-BC Chen 	}
4850daa4732SRex-BC Chen 
486501c574fSSean Wang 	/* Search a safe voltage for intermediate frequency. */
487396dee97SJia-Wei Chang 	rate = clk_get_rate(info->inter_clk);
488501c574fSSean Wang 	opp = dev_pm_opp_find_freq_ceil(cpu_dev, &rate);
489501c574fSSean Wang 	if (IS_ERR(opp)) {
490396dee97SJia-Wei Chang 		dev_err(cpu_dev, "cpu%d: failed to get intermediate opp\n", cpu);
491501c574fSSean Wang 		ret = PTR_ERR(opp);
4924b9ceb75SJia-Wei Chang 		goto out_disable_inter_clock;
493501c574fSSean Wang 	}
494501c574fSSean Wang 	info->intermediate_voltage = dev_pm_opp_get_voltage(opp);
495501c574fSSean Wang 	dev_pm_opp_put(opp);
496501c574fSSean Wang 
497c210063bSRex-BC Chen 	mutex_init(&info->reg_lock);
498c210063bSRex-BC Chen 	info->current_freq = clk_get_rate(info->cpu_clk);
499c210063bSRex-BC Chen 
500c210063bSRex-BC Chen 	info->opp_cpu = cpu;
501c210063bSRex-BC Chen 	info->opp_nb.notifier_call = mtk_cpufreq_opp_notifier;
502c210063bSRex-BC Chen 	ret = dev_pm_opp_register_notifier(cpu_dev, &info->opp_nb);
503c210063bSRex-BC Chen 	if (ret) {
504c210063bSRex-BC Chen 		dev_err(cpu_dev, "cpu%d: failed to register opp notifier\n", cpu);
505c210063bSRex-BC Chen 		goto out_disable_inter_clock;
506c210063bSRex-BC Chen 	}
507c210063bSRex-BC Chen 
508501c574fSSean Wang 	/*
509501c574fSSean Wang 	 * If SRAM regulator is present, software "voltage tracking" is needed
510501c574fSSean Wang 	 * for this CPU power domain.
511501c574fSSean Wang 	 */
512396dee97SJia-Wei Chang 	info->need_voltage_tracking = (info->sram_reg != NULL);
513501c574fSSean Wang 
5146a17b387SJia-Wei Chang 	/*
5156a17b387SJia-Wei Chang 	 * We assume min voltage is 0 and tracking target voltage using
5166a17b387SJia-Wei Chang 	 * min_volt_shift for each iteration.
5176a17b387SJia-Wei Chang 	 * The vtrack_max is 3 times of expeted iteration count.
5186a17b387SJia-Wei Chang 	 */
5196a17b387SJia-Wei Chang 	info->vtrack_max = 3 * DIV_ROUND_UP(max(info->soc_data->sram_max_volt,
5206a17b387SJia-Wei Chang 						info->soc_data->proc_max_volt),
5216a17b387SJia-Wei Chang 					    info->soc_data->min_volt_shift);
5226a17b387SJia-Wei Chang 
523501c574fSSean Wang 	return 0;
524501c574fSSean Wang 
5254b9ceb75SJia-Wei Chang out_disable_inter_clock:
5264b9ceb75SJia-Wei Chang 	clk_disable_unprepare(info->inter_clk);
5274b9ceb75SJia-Wei Chang 
5284b9ceb75SJia-Wei Chang out_disable_mux_clock:
5294b9ceb75SJia-Wei Chang 	clk_disable_unprepare(info->cpu_clk);
5304b9ceb75SJia-Wei Chang 
531501c574fSSean Wang out_free_opp_table:
532501c574fSSean Wang 	dev_pm_opp_of_cpumask_remove_table(&info->cpus);
533501c574fSSean Wang 
534d51e1062SJia-Wei Chang out_disable_sram_reg:
535d51e1062SJia-Wei Chang 	if (info->sram_reg)
5364b9ceb75SJia-Wei Chang 		regulator_disable(info->sram_reg);
5374b9ceb75SJia-Wei Chang 
538d51e1062SJia-Wei Chang out_free_sram_reg:
539d51e1062SJia-Wei Chang 	if (info->sram_reg)
540396dee97SJia-Wei Chang 		regulator_put(info->sram_reg);
541d51e1062SJia-Wei Chang 
542d51e1062SJia-Wei Chang out_disable_proc_reg:
543d51e1062SJia-Wei Chang 	regulator_disable(info->proc_reg);
544d51e1062SJia-Wei Chang 
545d51e1062SJia-Wei Chang out_free_proc_reg:
546d51e1062SJia-Wei Chang 	regulator_put(info->proc_reg);
547d51e1062SJia-Wei Chang 
548d51e1062SJia-Wei Chang out_free_inter_clock:
549396dee97SJia-Wei Chang 	clk_put(info->inter_clk);
550501c574fSSean Wang 
551d51e1062SJia-Wei Chang out_free_mux_clock:
552d51e1062SJia-Wei Chang 	clk_put(info->cpu_clk);
553d51e1062SJia-Wei Chang 
554501c574fSSean Wang 	return ret;
555501c574fSSean Wang }
556501c574fSSean Wang 
mtk_cpu_dvfs_info_release(struct mtk_cpu_dvfs_info * info)557501c574fSSean Wang static void mtk_cpu_dvfs_info_release(struct mtk_cpu_dvfs_info *info)
558501c574fSSean Wang {
5594b9ceb75SJia-Wei Chang 	regulator_disable(info->proc_reg);
560501c574fSSean Wang 	regulator_put(info->proc_reg);
561d51e1062SJia-Wei Chang 	if (info->sram_reg) {
5624b9ceb75SJia-Wei Chang 		regulator_disable(info->sram_reg);
563501c574fSSean Wang 		regulator_put(info->sram_reg);
5644b9ceb75SJia-Wei Chang 	}
5654b9ceb75SJia-Wei Chang 	clk_disable_unprepare(info->cpu_clk);
566501c574fSSean Wang 	clk_put(info->cpu_clk);
5674b9ceb75SJia-Wei Chang 	clk_disable_unprepare(info->inter_clk);
568501c574fSSean Wang 	clk_put(info->inter_clk);
569501c574fSSean Wang 	dev_pm_opp_of_cpumask_remove_table(&info->cpus);
570c210063bSRex-BC Chen 	dev_pm_opp_unregister_notifier(info->cpu_dev, &info->opp_nb);
571501c574fSSean Wang }
572501c574fSSean Wang 
mtk_cpufreq_init(struct cpufreq_policy * policy)573501c574fSSean Wang static int mtk_cpufreq_init(struct cpufreq_policy *policy)
574501c574fSSean Wang {
575501c574fSSean Wang 	struct mtk_cpu_dvfs_info *info;
576501c574fSSean Wang 	struct cpufreq_frequency_table *freq_table;
577501c574fSSean Wang 	int ret;
578501c574fSSean Wang 
579501c574fSSean Wang 	info = mtk_cpu_dvfs_info_lookup(policy->cpu);
580501c574fSSean Wang 	if (!info) {
581a3b8d1b1SWan Jiabing 		pr_err("dvfs info for cpu%d is not initialized.\n",
582a3b8d1b1SWan Jiabing 			policy->cpu);
583501c574fSSean Wang 		return -EINVAL;
584501c574fSSean Wang 	}
585501c574fSSean Wang 
586501c574fSSean Wang 	ret = dev_pm_opp_init_cpufreq_table(info->cpu_dev, &freq_table);
587501c574fSSean Wang 	if (ret) {
5889acc0f7aSRex-BC Chen 		dev_err(info->cpu_dev,
5899acc0f7aSRex-BC Chen 			"failed to init cpufreq table for cpu%d: %d\n",
590501c574fSSean Wang 			policy->cpu, ret);
591501c574fSSean Wang 		return ret;
592501c574fSSean Wang 	}
593501c574fSSean Wang 
594501c574fSSean Wang 	cpumask_copy(policy->cpus, &info->cpus);
595b563afbaSViresh Kumar 	policy->freq_table = freq_table;
596501c574fSSean Wang 	policy->driver_data = info;
597501c574fSSean Wang 	policy->clk = info->cpu_clk;
598501c574fSSean Wang 
599501c574fSSean Wang 	return 0;
600501c574fSSean Wang }
601501c574fSSean Wang 
mtk_cpufreq_exit(struct cpufreq_policy * policy)602501c574fSSean Wang static int mtk_cpufreq_exit(struct cpufreq_policy *policy)
603501c574fSSean Wang {
604501c574fSSean Wang 	struct mtk_cpu_dvfs_info *info = policy->driver_data;
605501c574fSSean Wang 
606501c574fSSean Wang 	dev_pm_opp_free_cpufreq_table(info->cpu_dev, &policy->freq_table);
607501c574fSSean Wang 
608501c574fSSean Wang 	return 0;
609501c574fSSean Wang }
610501c574fSSean Wang 
611862e0104SSean Wang static struct cpufreq_driver mtk_cpufreq_driver = {
6125ae4a4b4SViresh Kumar 	.flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK |
6130db60d6bSAmit Kucheria 		 CPUFREQ_HAVE_GOVERNOR_PER_POLICY |
6140db60d6bSAmit Kucheria 		 CPUFREQ_IS_COOLING_DEV,
615501c574fSSean Wang 	.verify = cpufreq_generic_frequency_table_verify,
616501c574fSSean Wang 	.target_index = mtk_cpufreq_set_target,
617501c574fSSean Wang 	.get = cpufreq_generic_get,
618501c574fSSean Wang 	.init = mtk_cpufreq_init,
619501c574fSSean Wang 	.exit = mtk_cpufreq_exit,
6203701fd64SViresh Kumar 	.register_em = cpufreq_register_em_with_opp,
621501c574fSSean Wang 	.name = "mtk-cpufreq",
622501c574fSSean Wang 	.attr = cpufreq_generic_attr,
623501c574fSSean Wang };
624501c574fSSean Wang 
mtk_cpufreq_probe(struct platform_device * pdev)625862e0104SSean Wang static int mtk_cpufreq_probe(struct platform_device *pdev)
626501c574fSSean Wang {
627ead858bdSRex-BC Chen 	const struct mtk_cpufreq_platform_data *data;
628501c574fSSean Wang 	struct mtk_cpu_dvfs_info *info, *tmp;
629501c574fSSean Wang 	int cpu, ret;
630501c574fSSean Wang 
631ead858bdSRex-BC Chen 	data = dev_get_platdata(&pdev->dev);
632ead858bdSRex-BC Chen 	if (!data) {
633ead858bdSRex-BC Chen 		dev_err(&pdev->dev,
634ead858bdSRex-BC Chen 			"failed to get mtk cpufreq platform data\n");
635ead858bdSRex-BC Chen 		return -ENODEV;
636ead858bdSRex-BC Chen 	}
637ead858bdSRex-BC Chen 
638501c574fSSean Wang 	for_each_possible_cpu(cpu) {
639501c574fSSean Wang 		info = mtk_cpu_dvfs_info_lookup(cpu);
640501c574fSSean Wang 		if (info)
641501c574fSSean Wang 			continue;
642501c574fSSean Wang 
643501c574fSSean Wang 		info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
644501c574fSSean Wang 		if (!info) {
645501c574fSSean Wang 			ret = -ENOMEM;
646501c574fSSean Wang 			goto release_dvfs_info_list;
647501c574fSSean Wang 		}
648501c574fSSean Wang 
649ead858bdSRex-BC Chen 		info->soc_data = data;
650501c574fSSean Wang 		ret = mtk_cpu_dvfs_info_init(info, cpu);
651501c574fSSean Wang 		if (ret) {
652501c574fSSean Wang 			dev_err(&pdev->dev,
653501c574fSSean Wang 				"failed to initialize dvfs info for cpu%d\n",
654501c574fSSean Wang 				cpu);
655501c574fSSean Wang 			goto release_dvfs_info_list;
656501c574fSSean Wang 		}
657501c574fSSean Wang 
658501c574fSSean Wang 		list_add(&info->list_head, &dvfs_info_list);
659501c574fSSean Wang 	}
660501c574fSSean Wang 
661862e0104SSean Wang 	ret = cpufreq_register_driver(&mtk_cpufreq_driver);
662501c574fSSean Wang 	if (ret) {
663501c574fSSean Wang 		dev_err(&pdev->dev, "failed to register mtk cpufreq driver\n");
664501c574fSSean Wang 		goto release_dvfs_info_list;
665501c574fSSean Wang 	}
666501c574fSSean Wang 
667501c574fSSean Wang 	return 0;
668501c574fSSean Wang 
669501c574fSSean Wang release_dvfs_info_list:
670501c574fSSean Wang 	list_for_each_entry_safe(info, tmp, &dvfs_info_list, list_head) {
671501c574fSSean Wang 		mtk_cpu_dvfs_info_release(info);
672501c574fSSean Wang 		list_del(&info->list_head);
673501c574fSSean Wang 	}
674501c574fSSean Wang 
675501c574fSSean Wang 	return ret;
676501c574fSSean Wang }
677501c574fSSean Wang 
678862e0104SSean Wang static struct platform_driver mtk_cpufreq_platdrv = {
679501c574fSSean Wang 	.driver = {
680862e0104SSean Wang 		.name	= "mtk-cpufreq",
681501c574fSSean Wang 	},
682862e0104SSean Wang 	.probe		= mtk_cpufreq_probe,
683501c574fSSean Wang };
684501c574fSSean Wang 
685ead858bdSRex-BC Chen static const struct mtk_cpufreq_platform_data mt2701_platform_data = {
686ead858bdSRex-BC Chen 	.min_volt_shift = 100000,
687ead858bdSRex-BC Chen 	.max_volt_shift = 200000,
688ead858bdSRex-BC Chen 	.proc_max_volt = 1150000,
689ead858bdSRex-BC Chen 	.sram_min_volt = 0,
690ead858bdSRex-BC Chen 	.sram_max_volt = 1150000,
6910daa4732SRex-BC Chen 	.ccifreq_supported = false,
6920daa4732SRex-BC Chen };
6930daa4732SRex-BC Chen 
6940883426fSAngeloGioacchino Del Regno static const struct mtk_cpufreq_platform_data mt7622_platform_data = {
6950883426fSAngeloGioacchino Del Regno 	.min_volt_shift = 100000,
6960883426fSAngeloGioacchino Del Regno 	.max_volt_shift = 200000,
697*f8553411SDaniel Golle 	.proc_max_volt = 1350000,
6980883426fSAngeloGioacchino Del Regno 	.sram_min_volt = 0,
699*f8553411SDaniel Golle 	.sram_max_volt = 1350000,
700*f8553411SDaniel Golle 	.ccifreq_supported = false,
701*f8553411SDaniel Golle };
702*f8553411SDaniel Golle 
703*f8553411SDaniel Golle static const struct mtk_cpufreq_platform_data mt7623_platform_data = {
704*f8553411SDaniel Golle 	.min_volt_shift = 100000,
705*f8553411SDaniel Golle 	.max_volt_shift = 200000,
706*f8553411SDaniel Golle 	.proc_max_volt = 1300000,
7070883426fSAngeloGioacchino Del Regno 	.ccifreq_supported = false,
7080883426fSAngeloGioacchino Del Regno };
7090883426fSAngeloGioacchino Del Regno 
7100daa4732SRex-BC Chen static const struct mtk_cpufreq_platform_data mt8183_platform_data = {
7110daa4732SRex-BC Chen 	.min_volt_shift = 100000,
7120daa4732SRex-BC Chen 	.max_volt_shift = 200000,
7130daa4732SRex-BC Chen 	.proc_max_volt = 1150000,
7140daa4732SRex-BC Chen 	.sram_min_volt = 0,
7150daa4732SRex-BC Chen 	.sram_max_volt = 1150000,
7160daa4732SRex-BC Chen 	.ccifreq_supported = true,
717ead858bdSRex-BC Chen };
718ead858bdSRex-BC Chen 
71939b36010SJia-Wei Chang static const struct mtk_cpufreq_platform_data mt8186_platform_data = {
72039b36010SJia-Wei Chang 	.min_volt_shift = 100000,
72139b36010SJia-Wei Chang 	.max_volt_shift = 250000,
72239b36010SJia-Wei Chang 	.proc_max_volt = 1118750,
72339b36010SJia-Wei Chang 	.sram_min_volt = 850000,
72439b36010SJia-Wei Chang 	.sram_max_volt = 1118750,
72539b36010SJia-Wei Chang 	.ccifreq_supported = true,
72639b36010SJia-Wei Chang };
72739b36010SJia-Wei Chang 
728d3296bb4SJia-Wei Chang static const struct mtk_cpufreq_platform_data mt8516_platform_data = {
729d3296bb4SJia-Wei Chang 	.min_volt_shift = 100000,
730d3296bb4SJia-Wei Chang 	.max_volt_shift = 200000,
731d3296bb4SJia-Wei Chang 	.proc_max_volt = 1310000,
732d3296bb4SJia-Wei Chang 	.sram_min_volt = 0,
733d3296bb4SJia-Wei Chang 	.sram_max_volt = 1310000,
734d3296bb4SJia-Wei Chang 	.ccifreq_supported = false,
735d3296bb4SJia-Wei Chang };
736d3296bb4SJia-Wei Chang 
737501c574fSSean Wang /* List of machines supported by this driver */
738862e0104SSean Wang static const struct of_device_id mtk_cpufreq_machines[] __initconst = {
739ead858bdSRex-BC Chen 	{ .compatible = "mediatek,mt2701", .data = &mt2701_platform_data },
740ead858bdSRex-BC Chen 	{ .compatible = "mediatek,mt2712", .data = &mt2701_platform_data },
7410883426fSAngeloGioacchino Del Regno 	{ .compatible = "mediatek,mt7622", .data = &mt7622_platform_data },
742*f8553411SDaniel Golle 	{ .compatible = "mediatek,mt7623", .data = &mt7623_platform_data },
743d3296bb4SJia-Wei Chang 	{ .compatible = "mediatek,mt8167", .data = &mt8516_platform_data },
744ead858bdSRex-BC Chen 	{ .compatible = "mediatek,mt817x", .data = &mt2701_platform_data },
745ead858bdSRex-BC Chen 	{ .compatible = "mediatek,mt8173", .data = &mt2701_platform_data },
746ead858bdSRex-BC Chen 	{ .compatible = "mediatek,mt8176", .data = &mt2701_platform_data },
7470daa4732SRex-BC Chen 	{ .compatible = "mediatek,mt8183", .data = &mt8183_platform_data },
74839b36010SJia-Wei Chang 	{ .compatible = "mediatek,mt8186", .data = &mt8186_platform_data },
749ead858bdSRex-BC Chen 	{ .compatible = "mediatek,mt8365", .data = &mt2701_platform_data },
750d3296bb4SJia-Wei Chang 	{ .compatible = "mediatek,mt8516", .data = &mt8516_platform_data },
751501c574fSSean Wang 	{ }
752501c574fSSean Wang };
753af6eca06SPali Rohár MODULE_DEVICE_TABLE(of, mtk_cpufreq_machines);
754501c574fSSean Wang 
mtk_cpufreq_driver_init(void)755862e0104SSean Wang static int __init mtk_cpufreq_driver_init(void)
756501c574fSSean Wang {
757501c574fSSean Wang 	struct device_node *np;
758501c574fSSean Wang 	const struct of_device_id *match;
759ead858bdSRex-BC Chen 	const struct mtk_cpufreq_platform_data *data;
760501c574fSSean Wang 	int err;
761501c574fSSean Wang 
762501c574fSSean Wang 	np = of_find_node_by_path("/");
763501c574fSSean Wang 	if (!np)
764501c574fSSean Wang 		return -ENODEV;
765501c574fSSean Wang 
766862e0104SSean Wang 	match = of_match_node(mtk_cpufreq_machines, np);
767501c574fSSean Wang 	of_node_put(np);
768501c574fSSean Wang 	if (!match) {
769cb8bd2ffSViresh Kumar 		pr_debug("Machine is not compatible with mtk-cpufreq\n");
770501c574fSSean Wang 		return -ENODEV;
771501c574fSSean Wang 	}
772ead858bdSRex-BC Chen 	data = match->data;
773501c574fSSean Wang 
774862e0104SSean Wang 	err = platform_driver_register(&mtk_cpufreq_platdrv);
775501c574fSSean Wang 	if (err)
776501c574fSSean Wang 		return err;
777501c574fSSean Wang 
778501c574fSSean Wang 	/*
779501c574fSSean Wang 	 * Since there's no place to hold device registration code and no
780501c574fSSean Wang 	 * device tree based way to match cpufreq driver yet, both the driver
781501c574fSSean Wang 	 * and the device registration codes are put here to handle defer
782501c574fSSean Wang 	 * probing.
783501c574fSSean Wang 	 */
784ead858bdSRex-BC Chen 	cpufreq_pdev = platform_device_register_data(NULL, "mtk-cpufreq", -1,
785ead858bdSRex-BC Chen 						     data, sizeof(*data));
786f126fbadSRex-BC Chen 	if (IS_ERR(cpufreq_pdev)) {
787501c574fSSean Wang 		pr_err("failed to register mtk-cpufreq platform device\n");
7882f05c19dSQinglang Miao 		platform_driver_unregister(&mtk_cpufreq_platdrv);
789f126fbadSRex-BC Chen 		return PTR_ERR(cpufreq_pdev);
790501c574fSSean Wang 	}
791501c574fSSean Wang 
792501c574fSSean Wang 	return 0;
793501c574fSSean Wang }
module_init(mtk_cpufreq_driver_init)794b7070187SJia-Wei Chang module_init(mtk_cpufreq_driver_init)
795b7070187SJia-Wei Chang 
796b7070187SJia-Wei Chang static void __exit mtk_cpufreq_driver_exit(void)
797b7070187SJia-Wei Chang {
798f126fbadSRex-BC Chen 	platform_device_unregister(cpufreq_pdev);
799b7070187SJia-Wei Chang 	platform_driver_unregister(&mtk_cpufreq_platdrv);
800b7070187SJia-Wei Chang }
801b7070187SJia-Wei Chang module_exit(mtk_cpufreq_driver_exit)
8027e8a09e0SJesse Chan 
8037e8a09e0SJesse Chan MODULE_DESCRIPTION("MediaTek CPUFreq driver");
8047e8a09e0SJesse Chan MODULE_AUTHOR("Pi-Cheng Chen <pi-cheng.chen@linaro.org>");
8057e8a09e0SJesse Chan MODULE_LICENSE("GPL v2");
806