xref: /OK3568_Linux_fs/kernel/drivers/cpufreq/omap-cpufreq.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  *  CPU frequency scaling for OMAP using OPP information
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  *  Copyright (C) 2005 Nokia Corporation
6*4882a593Smuzhiyun  *  Written by Tony Lindgren <tony@atomide.com>
7*4882a593Smuzhiyun  *
8*4882a593Smuzhiyun  *  Based on cpu-sa1110.c, Copyright (C) 2001 Russell King
9*4882a593Smuzhiyun  *
10*4882a593Smuzhiyun  * Copyright (C) 2007-2011 Texas Instruments, Inc.
11*4882a593Smuzhiyun  * - OMAP3/4 support by Rajendra Nayak, Santosh Shilimkar
12*4882a593Smuzhiyun  */
13*4882a593Smuzhiyun 
14*4882a593Smuzhiyun #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
15*4882a593Smuzhiyun 
16*4882a593Smuzhiyun #include <linux/types.h>
17*4882a593Smuzhiyun #include <linux/kernel.h>
18*4882a593Smuzhiyun #include <linux/sched.h>
19*4882a593Smuzhiyun #include <linux/cpufreq.h>
20*4882a593Smuzhiyun #include <linux/delay.h>
21*4882a593Smuzhiyun #include <linux/init.h>
22*4882a593Smuzhiyun #include <linux/err.h>
23*4882a593Smuzhiyun #include <linux/clk.h>
24*4882a593Smuzhiyun #include <linux/io.h>
25*4882a593Smuzhiyun #include <linux/pm_opp.h>
26*4882a593Smuzhiyun #include <linux/cpu.h>
27*4882a593Smuzhiyun #include <linux/module.h>
28*4882a593Smuzhiyun #include <linux/platform_device.h>
29*4882a593Smuzhiyun #include <linux/regulator/consumer.h>
30*4882a593Smuzhiyun 
31*4882a593Smuzhiyun #include <asm/smp_plat.h>
32*4882a593Smuzhiyun #include <asm/cpu.h>
33*4882a593Smuzhiyun 
34*4882a593Smuzhiyun /* OPP tolerance in percentage */
35*4882a593Smuzhiyun #define	OPP_TOLERANCE	4
36*4882a593Smuzhiyun 
37*4882a593Smuzhiyun static struct cpufreq_frequency_table *freq_table;
38*4882a593Smuzhiyun static atomic_t freq_table_users = ATOMIC_INIT(0);
39*4882a593Smuzhiyun static struct device *mpu_dev;
40*4882a593Smuzhiyun static struct regulator *mpu_reg;
41*4882a593Smuzhiyun 
omap_target(struct cpufreq_policy * policy,unsigned int index)42*4882a593Smuzhiyun static int omap_target(struct cpufreq_policy *policy, unsigned int index)
43*4882a593Smuzhiyun {
44*4882a593Smuzhiyun 	int r, ret;
45*4882a593Smuzhiyun 	struct dev_pm_opp *opp;
46*4882a593Smuzhiyun 	unsigned long freq, volt = 0, volt_old = 0, tol = 0;
47*4882a593Smuzhiyun 	unsigned int old_freq, new_freq;
48*4882a593Smuzhiyun 
49*4882a593Smuzhiyun 	old_freq = policy->cur;
50*4882a593Smuzhiyun 	new_freq = freq_table[index].frequency;
51*4882a593Smuzhiyun 
52*4882a593Smuzhiyun 	freq = new_freq * 1000;
53*4882a593Smuzhiyun 	ret = clk_round_rate(policy->clk, freq);
54*4882a593Smuzhiyun 	if (ret < 0) {
55*4882a593Smuzhiyun 		dev_warn(mpu_dev,
56*4882a593Smuzhiyun 			 "CPUfreq: Cannot find matching frequency for %lu\n",
57*4882a593Smuzhiyun 			 freq);
58*4882a593Smuzhiyun 		return ret;
59*4882a593Smuzhiyun 	}
60*4882a593Smuzhiyun 	freq = ret;
61*4882a593Smuzhiyun 
62*4882a593Smuzhiyun 	if (mpu_reg) {
63*4882a593Smuzhiyun 		opp = dev_pm_opp_find_freq_ceil(mpu_dev, &freq);
64*4882a593Smuzhiyun 		if (IS_ERR(opp)) {
65*4882a593Smuzhiyun 			dev_err(mpu_dev, "%s: unable to find MPU OPP for %d\n",
66*4882a593Smuzhiyun 				__func__, new_freq);
67*4882a593Smuzhiyun 			return -EINVAL;
68*4882a593Smuzhiyun 		}
69*4882a593Smuzhiyun 		volt = dev_pm_opp_get_voltage(opp);
70*4882a593Smuzhiyun 		dev_pm_opp_put(opp);
71*4882a593Smuzhiyun 		tol = volt * OPP_TOLERANCE / 100;
72*4882a593Smuzhiyun 		volt_old = regulator_get_voltage(mpu_reg);
73*4882a593Smuzhiyun 	}
74*4882a593Smuzhiyun 
75*4882a593Smuzhiyun 	dev_dbg(mpu_dev, "cpufreq-omap: %u MHz, %ld mV --> %u MHz, %ld mV\n",
76*4882a593Smuzhiyun 		old_freq / 1000, volt_old ? volt_old / 1000 : -1,
77*4882a593Smuzhiyun 		new_freq / 1000, volt ? volt / 1000 : -1);
78*4882a593Smuzhiyun 
79*4882a593Smuzhiyun 	/* scaling up?  scale voltage before frequency */
80*4882a593Smuzhiyun 	if (mpu_reg && (new_freq > old_freq)) {
81*4882a593Smuzhiyun 		r = regulator_set_voltage(mpu_reg, volt - tol, volt + tol);
82*4882a593Smuzhiyun 		if (r < 0) {
83*4882a593Smuzhiyun 			dev_warn(mpu_dev, "%s: unable to scale voltage up.\n",
84*4882a593Smuzhiyun 				 __func__);
85*4882a593Smuzhiyun 			return r;
86*4882a593Smuzhiyun 		}
87*4882a593Smuzhiyun 	}
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun 	ret = clk_set_rate(policy->clk, new_freq * 1000);
90*4882a593Smuzhiyun 
91*4882a593Smuzhiyun 	/* scaling down?  scale voltage after frequency */
92*4882a593Smuzhiyun 	if (mpu_reg && (new_freq < old_freq)) {
93*4882a593Smuzhiyun 		r = regulator_set_voltage(mpu_reg, volt - tol, volt + tol);
94*4882a593Smuzhiyun 		if (r < 0) {
95*4882a593Smuzhiyun 			dev_warn(mpu_dev, "%s: unable to scale voltage down.\n",
96*4882a593Smuzhiyun 				 __func__);
97*4882a593Smuzhiyun 			clk_set_rate(policy->clk, old_freq * 1000);
98*4882a593Smuzhiyun 			return r;
99*4882a593Smuzhiyun 		}
100*4882a593Smuzhiyun 	}
101*4882a593Smuzhiyun 
102*4882a593Smuzhiyun 	return ret;
103*4882a593Smuzhiyun }
104*4882a593Smuzhiyun 
freq_table_free(void)105*4882a593Smuzhiyun static inline void freq_table_free(void)
106*4882a593Smuzhiyun {
107*4882a593Smuzhiyun 	if (atomic_dec_and_test(&freq_table_users))
108*4882a593Smuzhiyun 		dev_pm_opp_free_cpufreq_table(mpu_dev, &freq_table);
109*4882a593Smuzhiyun }
110*4882a593Smuzhiyun 
omap_cpu_init(struct cpufreq_policy * policy)111*4882a593Smuzhiyun static int omap_cpu_init(struct cpufreq_policy *policy)
112*4882a593Smuzhiyun {
113*4882a593Smuzhiyun 	int result;
114*4882a593Smuzhiyun 
115*4882a593Smuzhiyun 	policy->clk = clk_get(NULL, "cpufreq_ck");
116*4882a593Smuzhiyun 	if (IS_ERR(policy->clk))
117*4882a593Smuzhiyun 		return PTR_ERR(policy->clk);
118*4882a593Smuzhiyun 
119*4882a593Smuzhiyun 	if (!freq_table) {
120*4882a593Smuzhiyun 		result = dev_pm_opp_init_cpufreq_table(mpu_dev, &freq_table);
121*4882a593Smuzhiyun 		if (result) {
122*4882a593Smuzhiyun 			dev_err(mpu_dev,
123*4882a593Smuzhiyun 				"%s: cpu%d: failed creating freq table[%d]\n",
124*4882a593Smuzhiyun 				__func__, policy->cpu, result);
125*4882a593Smuzhiyun 			clk_put(policy->clk);
126*4882a593Smuzhiyun 			return result;
127*4882a593Smuzhiyun 		}
128*4882a593Smuzhiyun 	}
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun 	atomic_inc_return(&freq_table_users);
131*4882a593Smuzhiyun 
132*4882a593Smuzhiyun 	/* FIXME: what's the actual transition time? */
133*4882a593Smuzhiyun 	cpufreq_generic_init(policy, freq_table, 300 * 1000);
134*4882a593Smuzhiyun 	dev_pm_opp_of_register_em(mpu_dev, policy->cpus);
135*4882a593Smuzhiyun 
136*4882a593Smuzhiyun 	return 0;
137*4882a593Smuzhiyun }
138*4882a593Smuzhiyun 
omap_cpu_exit(struct cpufreq_policy * policy)139*4882a593Smuzhiyun static int omap_cpu_exit(struct cpufreq_policy *policy)
140*4882a593Smuzhiyun {
141*4882a593Smuzhiyun 	freq_table_free();
142*4882a593Smuzhiyun 	clk_put(policy->clk);
143*4882a593Smuzhiyun 	return 0;
144*4882a593Smuzhiyun }
145*4882a593Smuzhiyun 
146*4882a593Smuzhiyun static struct cpufreq_driver omap_driver = {
147*4882a593Smuzhiyun 	.flags		= CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK,
148*4882a593Smuzhiyun 	.verify		= cpufreq_generic_frequency_table_verify,
149*4882a593Smuzhiyun 	.target_index	= omap_target,
150*4882a593Smuzhiyun 	.get		= cpufreq_generic_get,
151*4882a593Smuzhiyun 	.init		= omap_cpu_init,
152*4882a593Smuzhiyun 	.exit		= omap_cpu_exit,
153*4882a593Smuzhiyun 	.name		= "omap",
154*4882a593Smuzhiyun 	.attr		= cpufreq_generic_attr,
155*4882a593Smuzhiyun };
156*4882a593Smuzhiyun 
omap_cpufreq_probe(struct platform_device * pdev)157*4882a593Smuzhiyun static int omap_cpufreq_probe(struct platform_device *pdev)
158*4882a593Smuzhiyun {
159*4882a593Smuzhiyun 	mpu_dev = get_cpu_device(0);
160*4882a593Smuzhiyun 	if (!mpu_dev) {
161*4882a593Smuzhiyun 		pr_warn("%s: unable to get the MPU device\n", __func__);
162*4882a593Smuzhiyun 		return -EINVAL;
163*4882a593Smuzhiyun 	}
164*4882a593Smuzhiyun 
165*4882a593Smuzhiyun 	mpu_reg = regulator_get(mpu_dev, "vcc");
166*4882a593Smuzhiyun 	if (IS_ERR(mpu_reg)) {
167*4882a593Smuzhiyun 		pr_warn("%s: unable to get MPU regulator\n", __func__);
168*4882a593Smuzhiyun 		mpu_reg = NULL;
169*4882a593Smuzhiyun 	} else {
170*4882a593Smuzhiyun 		/*
171*4882a593Smuzhiyun 		 * Ensure physical regulator is present.
172*4882a593Smuzhiyun 		 * (e.g. could be dummy regulator.)
173*4882a593Smuzhiyun 		 */
174*4882a593Smuzhiyun 		if (regulator_get_voltage(mpu_reg) < 0) {
175*4882a593Smuzhiyun 			pr_warn("%s: physical regulator not present for MPU\n",
176*4882a593Smuzhiyun 				__func__);
177*4882a593Smuzhiyun 			regulator_put(mpu_reg);
178*4882a593Smuzhiyun 			mpu_reg = NULL;
179*4882a593Smuzhiyun 		}
180*4882a593Smuzhiyun 	}
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun 	return cpufreq_register_driver(&omap_driver);
183*4882a593Smuzhiyun }
184*4882a593Smuzhiyun 
omap_cpufreq_remove(struct platform_device * pdev)185*4882a593Smuzhiyun static int omap_cpufreq_remove(struct platform_device *pdev)
186*4882a593Smuzhiyun {
187*4882a593Smuzhiyun 	return cpufreq_unregister_driver(&omap_driver);
188*4882a593Smuzhiyun }
189*4882a593Smuzhiyun 
190*4882a593Smuzhiyun static struct platform_driver omap_cpufreq_platdrv = {
191*4882a593Smuzhiyun 	.driver = {
192*4882a593Smuzhiyun 		.name	= "omap-cpufreq",
193*4882a593Smuzhiyun 	},
194*4882a593Smuzhiyun 	.probe		= omap_cpufreq_probe,
195*4882a593Smuzhiyun 	.remove		= omap_cpufreq_remove,
196*4882a593Smuzhiyun };
197*4882a593Smuzhiyun module_platform_driver(omap_cpufreq_platdrv);
198*4882a593Smuzhiyun 
199*4882a593Smuzhiyun MODULE_DESCRIPTION("cpufreq driver for OMAP SoCs");
200*4882a593Smuzhiyun MODULE_LICENSE("GPL");
201