• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Rockchip CPUFreq Driver
3  *
4  * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  *
10  * This program is distributed "as is" WITHOUT ANY WARRANTY of any
11  * kind, whether express or implied; without even the implied warranty
12  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  */
15 
16 #include <linux/clk.h>
17 #include <linux/cpu.h>
18 #include <linux/cpufreq.h>
19 #include <linux/err.h>
20 #include <linux/init.h>
21 #include <linux/kernel.h>
22 #include <linux/mfd/syscon.h>
23 #include <linux/module.h>
24 #include <linux/nvmem-consumer.h>
25 #include <linux/of.h>
26 #include <linux/of_address.h>
27 #include <linux/platform_device.h>
28 #include <linux/pm_opp.h>
29 #include <linux/slab.h>
30 #include <linux/regmap.h>
31 #include <linux/regulator/consumer.h>
32 #include <linux/rockchip/cpu.h>
33 #include <soc/rockchip/rockchip_opp_select.h>
34 #include <soc/rockchip/rockchip_system_monitor.h>
35 
36 #include "cpufreq-dt.h"
37 #include "rockchip-cpufreq.h"
38 
39 struct cluster_info {
40 	struct list_head list_head;
41 	struct monitor_dev_info *mdev_info;
42 	struct rockchip_opp_info opp_info;
43 	cpumask_t cpus;
44 	int scale;
45 };
46 static LIST_HEAD(cluster_info_list);
47 
rk3399_get_soc_info(struct device * dev,struct device_node * np,int * bin,int * process)48 static int rk3399_get_soc_info(struct device *dev, struct device_node *np,
49 			       int *bin, int *process)
50 {
51 	int ret = 0;
52 	u8 value = 0;
53 
54 	if (!bin)
55 		return 0;
56 
57 	if (of_property_match_string(np, "nvmem-cell-names",
58 				     "specification_serial_number") >= 0) {
59 		ret = rockchip_nvmem_cell_read_u8(np,
60 						  "specification_serial_number",
61 						  &value);
62 		if (ret) {
63 			dev_err(dev,
64 				"Failed to get specification_serial_number\n");
65 			goto out;
66 		}
67 
68 		if (value == 0xb) {
69 			*bin = 0;
70 		} else if (value == 0x1) {
71 			if (of_property_match_string(np, "nvmem-cell-names",
72 						     "customer_demand") >= 0) {
73 				ret = rockchip_nvmem_cell_read_u8(np,
74 								  "customer_demand",
75 								  &value);
76 				if (ret) {
77 					dev_err(dev, "Failed to get customer_demand\n");
78 					goto out;
79 				}
80 				if (value == 0x0)
81 					*bin = 0;
82 				else
83 					*bin = 1;
84 			}
85 		} else if (value == 0x10) {
86 			*bin = 1;
87 		}
88 	}
89 
90 out:
91 	if (*bin >= 0)
92 		dev_info(dev, "bin=%d\n", *bin);
93 
94 	return ret;
95 }
96 
97 static const struct rockchip_opp_data rk3399_cpu_opp_data = {
98 	.get_soc_info = rk3399_get_soc_info,
99 };
100 
101 static const struct of_device_id rockchip_cpufreq_of_match[] = {
102 
103 	{
104 		.compatible = "rockchip,rk3399",
105 		.data = (void *)&rk3399_cpu_opp_data,
106 	},
107 	{},
108 };
109 
rockchip_cluster_info_lookup(int cpu)110 static struct cluster_info *rockchip_cluster_info_lookup(int cpu)
111 {
112 	struct cluster_info *cluster;
113 
114 	list_for_each_entry(cluster, &cluster_info_list, list_head) {
115 		if (cpumask_test_cpu(cpu, &cluster->cpus))
116 			return cluster;
117 	}
118 
119 	return NULL;
120 }
121 
rockchip_cpufreq_set_volt(struct device * dev,struct regulator * reg,struct dev_pm_opp_supply * supply,char * reg_name)122 static int rockchip_cpufreq_set_volt(struct device *dev,
123 				     struct regulator *reg,
124 				     struct dev_pm_opp_supply *supply,
125 				     char *reg_name)
126 {
127 	int ret;
128 
129 	dev_dbg(dev, "%s: %s voltages (uV): %lu %lu %lu\n", __func__, reg_name,
130 		supply->u_volt_min, supply->u_volt, supply->u_volt_max);
131 
132 	ret = regulator_set_voltage_triplet(reg, supply->u_volt_min,
133 					    supply->u_volt, supply->u_volt_max);
134 	if (ret)
135 		dev_err(dev, "%s: failed to set voltage (%lu %lu %lu uV): %d\n",
136 			__func__, supply->u_volt_min, supply->u_volt,
137 			supply->u_volt_max, ret);
138 
139 	return ret;
140 }
141 
cpu_opp_helper(struct dev_pm_set_opp_data * data)142 static int cpu_opp_helper(struct dev_pm_set_opp_data *data)
143 {
144 	struct dev_pm_opp_supply *old_supply_vdd = &data->old_opp.supplies[0];
145 	struct dev_pm_opp_supply *old_supply_mem = &data->old_opp.supplies[1];
146 	struct dev_pm_opp_supply *new_supply_vdd = &data->new_opp.supplies[0];
147 	struct dev_pm_opp_supply *new_supply_mem = &data->new_opp.supplies[1];
148 	struct regulator *vdd_reg = data->regulators[0];
149 	struct regulator *mem_reg = data->regulators[1];
150 	struct device *dev = data->dev;
151 	struct clk *clk = data->clk;
152 	struct cluster_info *cluster;
153 	struct rockchip_opp_info *opp_info;
154 	unsigned long old_freq = data->old_opp.rate;
155 	unsigned long new_freq = data->new_opp.rate;
156 	int ret = 0;
157 
158 	cluster = rockchip_cluster_info_lookup(dev->id);
159 	if (!cluster)
160 		return -EINVAL;
161 	opp_info = &cluster->opp_info;
162 
163 	/* Scaling up? Scale voltage before frequency */
164 	if (new_freq >= old_freq) {
165 		ret = rockchip_cpufreq_set_volt(dev, mem_reg, new_supply_mem,
166 						"mem");
167 		if (ret)
168 			goto restore_voltage;
169 		ret = rockchip_cpufreq_set_volt(dev, vdd_reg, new_supply_vdd,
170 						"vdd");
171 		if (ret)
172 			goto restore_voltage;
173 		if (opp_info->data->set_read_margin)
174 			opp_info->data->set_read_margin(dev, opp_info,
175 							new_supply_vdd->u_volt);
176 	}
177 
178 	/* Change frequency */
179 	dev_dbg(dev, "%s: switching OPP: %lu Hz --> %lu Hz\n", __func__,
180 		old_freq, new_freq);
181 	ret = clk_set_rate(clk, new_freq);
182 	if (ret) {
183 		dev_err(dev, "%s: failed to set clk rate: %d\n", __func__, ret);
184 		goto restore_rm;
185 	}
186 
187 	/* Scaling down? Scale voltage after frequency */
188 	if (new_freq < old_freq) {
189 		if (opp_info->data->set_read_margin)
190 			opp_info->data->set_read_margin(dev, opp_info,
191 							new_supply_vdd->u_volt);
192 		ret = rockchip_cpufreq_set_volt(dev, vdd_reg, new_supply_vdd,
193 						"vdd");
194 		if (ret)
195 			goto restore_freq;
196 		ret = rockchip_cpufreq_set_volt(dev, mem_reg, new_supply_mem,
197 						"mem");
198 		if (ret)
199 			goto restore_freq;
200 	}
201 
202 	return 0;
203 
204 restore_freq:
205 	if (clk_set_rate(clk, old_freq))
206 		dev_err(dev, "%s: failed to restore old-freq (%lu Hz)\n",
207 			__func__, old_freq);
208 restore_rm:
209 	if (opp_info->data->set_read_margin)
210 		opp_info->data->set_read_margin(dev, opp_info,
211 						old_supply_vdd->u_volt);
212 restore_voltage:
213 	rockchip_cpufreq_set_volt(dev, mem_reg, old_supply_mem, "mem");
214 	rockchip_cpufreq_set_volt(dev, vdd_reg, old_supply_vdd, "vdd");
215 
216 	return ret;
217 }
218 
rockchip_cpufreq_cluster_init(int cpu,struct cluster_info * cluster)219 static int rockchip_cpufreq_cluster_init(int cpu, struct cluster_info *cluster)
220 {
221 	struct rockchip_opp_info *opp_info = &cluster->opp_info;
222 	struct opp_table *pname_table = NULL;
223 	struct opp_table *reg_table = NULL;
224 	struct opp_table *opp_table;
225 	struct device_node *np;
226 	struct device *dev;
227 	const char * const reg_names[] = {"cpu", "mem"};
228 	char *reg_name = NULL;
229 	int bin = -EINVAL;
230 	int process = -EINVAL;
231 	int volt_sel = -EINVAL;
232 	int ret = 0;
233 
234 	dev = get_cpu_device(cpu);
235 	if (!dev)
236 		return -ENODEV;
237 
238 	if (of_find_property(dev->of_node, "cpu-supply", NULL))
239 		reg_name = "cpu";
240 	else if (of_find_property(dev->of_node, "cpu0-supply", NULL))
241 		reg_name = "cpu0";
242 	else
243 		return -ENOENT;
244 
245 	np = of_parse_phandle(dev->of_node, "operating-points-v2", 0);
246 	if (!np) {
247 		dev_warn(dev, "OPP-v2 not supported\n");
248 		return -ENOENT;
249 	}
250 
251 	ret = dev_pm_opp_of_get_sharing_cpus(dev, &cluster->cpus);
252 	if (ret) {
253 		dev_err(dev, "Failed to get sharing cpus\n");
254 		goto np_err;
255 	}
256 
257 	rockchip_get_opp_data(rockchip_cpufreq_of_match, opp_info);
258 	if (opp_info->data && opp_info->data->set_read_margin) {
259 		opp_info->current_rm = UINT_MAX;
260 		opp_info->grf = syscon_regmap_lookup_by_phandle(np,
261 								"rockchip,grf");
262 		if (IS_ERR(opp_info->grf))
263 			opp_info->grf = NULL;
264 		rockchip_get_volt_rm_table(dev, np, "volt-mem-read-margin",
265 					   &opp_info->volt_rm_tbl);
266 	}
267 	if (opp_info->data && opp_info->data->get_soc_info)
268 		opp_info->data->get_soc_info(dev, np, &bin, &process);
269 	rockchip_get_scale_volt_sel(dev, "cpu_leakage", reg_name, bin, process,
270 				    &cluster->scale, &volt_sel);
271 	pname_table = rockchip_set_opp_prop_name(dev, process, volt_sel);
272 	if (IS_ERR(pname_table)) {
273 		ret = PTR_ERR(pname_table);
274 		goto np_err;
275 	}
276 
277 	if (of_find_property(dev->of_node, "cpu-supply", NULL) &&
278 	    of_find_property(dev->of_node, "mem-supply", NULL)) {
279 		reg_table = dev_pm_opp_set_regulators(dev, reg_names,
280 						      ARRAY_SIZE(reg_names));
281 		if (IS_ERR(reg_table)) {
282 			ret = PTR_ERR(reg_table);
283 			goto pname_opp_table;
284 		}
285 		opp_table = dev_pm_opp_register_set_opp_helper(dev,
286 							       cpu_opp_helper);
287 		if (IS_ERR(opp_table)) {
288 			ret = PTR_ERR(opp_table);
289 			goto reg_opp_table;
290 		}
291 	}
292 
293 	of_node_put(np);
294 
295 	return 0;
296 
297 reg_opp_table:
298 	if (reg_table)
299 		dev_pm_opp_put_regulators(reg_table);
300 pname_opp_table:
301 	if (pname_table)
302 		dev_pm_opp_put_prop_name(pname_table);
303 np_err:
304 	of_node_put(np);
305 
306 	return ret;
307 }
308 
rockchip_cpufreq_adjust_power_scale(struct device * dev)309 int rockchip_cpufreq_adjust_power_scale(struct device *dev)
310 {
311 	struct cluster_info *cluster;
312 
313 	cluster = rockchip_cluster_info_lookup(dev->id);
314 	if (!cluster)
315 		return -EINVAL;
316 	rockchip_adjust_power_scale(dev, cluster->scale);
317 
318 	return 0;
319 }
320 EXPORT_SYMBOL_GPL(rockchip_cpufreq_adjust_power_scale);
321 
rockchip_cpufreq_opp_set_rate(struct device * dev,unsigned long target_freq)322 int rockchip_cpufreq_opp_set_rate(struct device *dev, unsigned long target_freq)
323 {
324 	struct cluster_info *cluster;
325 	int ret = 0;
326 
327 	cluster = rockchip_cluster_info_lookup(dev->id);
328 	if (!cluster)
329 		return -EINVAL;
330 
331 	rockchip_monitor_volt_adjust_lock(cluster->mdev_info);
332 	ret = dev_pm_opp_set_rate(dev, target_freq);
333 	rockchip_monitor_volt_adjust_unlock(cluster->mdev_info);
334 
335 	return ret;
336 }
337 EXPORT_SYMBOL_GPL(rockchip_cpufreq_opp_set_rate);
338 
rockchip_cpufreq_suspend(struct cpufreq_policy * policy)339 static int rockchip_cpufreq_suspend(struct cpufreq_policy *policy)
340 {
341 	int ret = 0;
342 
343 	ret = cpufreq_generic_suspend(policy);
344 	if (!ret)
345 		rockchip_monitor_suspend_low_temp_adjust(policy->cpu);
346 
347 	return ret;
348 }
349 
rockchip_cpufreq_notifier(struct notifier_block * nb,unsigned long event,void * data)350 static int rockchip_cpufreq_notifier(struct notifier_block *nb,
351 				     unsigned long event, void *data)
352 {
353 	struct device *dev;
354 	struct cpufreq_policy *policy = data;
355 	struct cluster_info *cluster;
356 	struct monitor_dev_profile *mdevp = NULL;
357 	struct monitor_dev_info *mdev_info = NULL;
358 
359 	dev = get_cpu_device(policy->cpu);
360 	if (!dev)
361 		return NOTIFY_BAD;
362 
363 	cluster = rockchip_cluster_info_lookup(policy->cpu);
364 	if (!cluster)
365 		return NOTIFY_BAD;
366 
367 	if (event == CPUFREQ_CREATE_POLICY) {
368 		mdevp = kzalloc(sizeof(*mdevp), GFP_KERNEL);
369 		if (!mdevp)
370 			return NOTIFY_BAD;
371 		mdevp->type = MONITOR_TPYE_CPU;
372 		mdevp->low_temp_adjust = rockchip_monitor_cpu_low_temp_adjust;
373 		mdevp->high_temp_adjust = rockchip_monitor_cpu_high_temp_adjust;
374 		mdevp->update_volt = rockchip_monitor_check_rate_volt;
375 		mdevp->data = (void *)policy;
376 		mdevp->opp_info = &cluster->opp_info;
377 		cpumask_copy(&mdevp->allowed_cpus, policy->cpus);
378 		mdev_info = rockchip_system_monitor_register(dev, mdevp);
379 		if (IS_ERR(mdev_info)) {
380 			kfree(mdevp);
381 			dev_err(dev, "failed to register system monitor\n");
382 			return NOTIFY_BAD;
383 		}
384 		mdev_info->devp = mdevp;
385 		cluster->mdev_info = mdev_info;
386 	} else if (event == CPUFREQ_REMOVE_POLICY) {
387 		if (cluster->mdev_info) {
388 			kfree(cluster->mdev_info->devp);
389 			rockchip_system_monitor_unregister(cluster->mdev_info);
390 			cluster->mdev_info = NULL;
391 		}
392 	}
393 
394 	return NOTIFY_OK;
395 }
396 
397 static struct notifier_block rockchip_cpufreq_notifier_block = {
398 	.notifier_call = rockchip_cpufreq_notifier,
399 };
400 
rockchip_cpufreq_driver_init(void)401 static int __init rockchip_cpufreq_driver_init(void)
402 {
403 	struct cluster_info *cluster, *pos;
404 	struct cpufreq_dt_platform_data pdata = {0};
405 	int cpu, ret;
406 
407 	for_each_possible_cpu(cpu) {
408 		cluster = rockchip_cluster_info_lookup(cpu);
409 		if (cluster)
410 			continue;
411 
412 		cluster = kzalloc(sizeof(*cluster), GFP_KERNEL);
413 		if (!cluster) {
414 			ret = -ENOMEM;
415 			goto release_cluster_info;
416 		}
417 
418 		ret = rockchip_cpufreq_cluster_init(cpu, cluster);
419 		if (ret) {
420 			pr_err("Failed to initialize dvfs info cpu%d\n", cpu);
421 			goto release_cluster_info;
422 		}
423 		list_add(&cluster->list_head, &cluster_info_list);
424 	}
425 
426 	pdata.have_governor_per_policy = true;
427 	pdata.suspend = rockchip_cpufreq_suspend;
428 
429 	ret = cpufreq_register_notifier(&rockchip_cpufreq_notifier_block,
430 					CPUFREQ_POLICY_NOTIFIER);
431 	if (ret) {
432 		pr_err("failed to register cpufreq notifier\n");
433 		goto release_cluster_info;
434 	}
435 
436 	return PTR_ERR_OR_ZERO(platform_device_register_data(NULL, "cpufreq-dt",
437 			       -1, (void *)&pdata,
438 			       sizeof(struct cpufreq_dt_platform_data)));
439 
440 release_cluster_info:
441 	list_for_each_entry_safe(cluster, pos, &cluster_info_list, list_head) {
442 		list_del(&cluster->list_head);
443 		kfree(cluster);
444 	}
445 	return ret;
446 }
447 module_init(rockchip_cpufreq_driver_init);
448 
449 MODULE_AUTHOR("Finley Xiao <finley.xiao@rock-chips.com>");
450 MODULE_DESCRIPTION("Rockchip cpufreq driver");
451 MODULE_LICENSE("GPL v2");
452