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