xref: /openbmc/linux/drivers/cpufreq/omap-cpufreq.c (revision 731e0cc639364646d36981d90ab0b6af12b8face)
1*731e0cc6SSantosh Shilimkar /*
2*731e0cc6SSantosh Shilimkar  *  CPU frequency scaling for OMAP
3*731e0cc6SSantosh Shilimkar  *
4*731e0cc6SSantosh Shilimkar  *  Copyright (C) 2005 Nokia Corporation
5*731e0cc6SSantosh Shilimkar  *  Written by Tony Lindgren <tony@atomide.com>
6*731e0cc6SSantosh Shilimkar  *
7*731e0cc6SSantosh Shilimkar  *  Based on cpu-sa1110.c, Copyright (C) 2001 Russell King
8*731e0cc6SSantosh Shilimkar  *
9*731e0cc6SSantosh Shilimkar  * Copyright (C) 2007-2011 Texas Instruments, Inc.
10*731e0cc6SSantosh Shilimkar  * - OMAP3/4 support by Rajendra Nayak, Santosh Shilimkar
11*731e0cc6SSantosh Shilimkar  *
12*731e0cc6SSantosh Shilimkar  * This program is free software; you can redistribute it and/or modify
13*731e0cc6SSantosh Shilimkar  * it under the terms of the GNU General Public License version 2 as
14*731e0cc6SSantosh Shilimkar  * published by the Free Software Foundation.
15*731e0cc6SSantosh Shilimkar  */
16*731e0cc6SSantosh Shilimkar #include <linux/types.h>
17*731e0cc6SSantosh Shilimkar #include <linux/kernel.h>
18*731e0cc6SSantosh Shilimkar #include <linux/sched.h>
19*731e0cc6SSantosh Shilimkar #include <linux/cpufreq.h>
20*731e0cc6SSantosh Shilimkar #include <linux/delay.h>
21*731e0cc6SSantosh Shilimkar #include <linux/init.h>
22*731e0cc6SSantosh Shilimkar #include <linux/err.h>
23*731e0cc6SSantosh Shilimkar #include <linux/clk.h>
24*731e0cc6SSantosh Shilimkar #include <linux/io.h>
25*731e0cc6SSantosh Shilimkar #include <linux/opp.h>
26*731e0cc6SSantosh Shilimkar 
27*731e0cc6SSantosh Shilimkar #include <asm/system.h>
28*731e0cc6SSantosh Shilimkar #include <asm/smp_plat.h>
29*731e0cc6SSantosh Shilimkar 
30*731e0cc6SSantosh Shilimkar #include <plat/clock.h>
31*731e0cc6SSantosh Shilimkar #include <plat/omap-pm.h>
32*731e0cc6SSantosh Shilimkar #include <plat/common.h>
33*731e0cc6SSantosh Shilimkar 
34*731e0cc6SSantosh Shilimkar #include <mach/hardware.h>
35*731e0cc6SSantosh Shilimkar 
36*731e0cc6SSantosh Shilimkar #define VERY_HI_RATE	900000000
37*731e0cc6SSantosh Shilimkar 
38*731e0cc6SSantosh Shilimkar static struct cpufreq_frequency_table *freq_table;
39*731e0cc6SSantosh Shilimkar static struct clk *mpu_clk;
40*731e0cc6SSantosh Shilimkar 
41*731e0cc6SSantosh Shilimkar static int omap_verify_speed(struct cpufreq_policy *policy)
42*731e0cc6SSantosh Shilimkar {
43*731e0cc6SSantosh Shilimkar 	if (freq_table)
44*731e0cc6SSantosh Shilimkar 		return cpufreq_frequency_table_verify(policy, freq_table);
45*731e0cc6SSantosh Shilimkar 
46*731e0cc6SSantosh Shilimkar 	if (policy->cpu)
47*731e0cc6SSantosh Shilimkar 		return -EINVAL;
48*731e0cc6SSantosh Shilimkar 
49*731e0cc6SSantosh Shilimkar 	cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
50*731e0cc6SSantosh Shilimkar 				     policy->cpuinfo.max_freq);
51*731e0cc6SSantosh Shilimkar 
52*731e0cc6SSantosh Shilimkar 	policy->min = clk_round_rate(mpu_clk, policy->min * 1000) / 1000;
53*731e0cc6SSantosh Shilimkar 	policy->max = clk_round_rate(mpu_clk, policy->max * 1000) / 1000;
54*731e0cc6SSantosh Shilimkar 	cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
55*731e0cc6SSantosh Shilimkar 				     policy->cpuinfo.max_freq);
56*731e0cc6SSantosh Shilimkar 	return 0;
57*731e0cc6SSantosh Shilimkar }
58*731e0cc6SSantosh Shilimkar 
59*731e0cc6SSantosh Shilimkar static unsigned int omap_getspeed(unsigned int cpu)
60*731e0cc6SSantosh Shilimkar {
61*731e0cc6SSantosh Shilimkar 	unsigned long rate;
62*731e0cc6SSantosh Shilimkar 
63*731e0cc6SSantosh Shilimkar 	if (cpu)
64*731e0cc6SSantosh Shilimkar 		return 0;
65*731e0cc6SSantosh Shilimkar 
66*731e0cc6SSantosh Shilimkar 	rate = clk_get_rate(mpu_clk) / 1000;
67*731e0cc6SSantosh Shilimkar 	return rate;
68*731e0cc6SSantosh Shilimkar }
69*731e0cc6SSantosh Shilimkar 
70*731e0cc6SSantosh Shilimkar static int omap_target(struct cpufreq_policy *policy,
71*731e0cc6SSantosh Shilimkar 		       unsigned int target_freq,
72*731e0cc6SSantosh Shilimkar 		       unsigned int relation)
73*731e0cc6SSantosh Shilimkar {
74*731e0cc6SSantosh Shilimkar 	int ret = 0;
75*731e0cc6SSantosh Shilimkar 	struct cpufreq_freqs freqs;
76*731e0cc6SSantosh Shilimkar 
77*731e0cc6SSantosh Shilimkar 	/* Ensure desired rate is within allowed range.  Some govenors
78*731e0cc6SSantosh Shilimkar 	 * (ondemand) will just pass target_freq=0 to get the minimum. */
79*731e0cc6SSantosh Shilimkar 	if (target_freq < policy->min)
80*731e0cc6SSantosh Shilimkar 		target_freq = policy->min;
81*731e0cc6SSantosh Shilimkar 	if (target_freq > policy->max)
82*731e0cc6SSantosh Shilimkar 		target_freq = policy->max;
83*731e0cc6SSantosh Shilimkar 
84*731e0cc6SSantosh Shilimkar 	freqs.old = omap_getspeed(0);
85*731e0cc6SSantosh Shilimkar 	freqs.new = clk_round_rate(mpu_clk, target_freq * 1000) / 1000;
86*731e0cc6SSantosh Shilimkar 	freqs.cpu = 0;
87*731e0cc6SSantosh Shilimkar 
88*731e0cc6SSantosh Shilimkar 	if (freqs.old == freqs.new)
89*731e0cc6SSantosh Shilimkar 		return ret;
90*731e0cc6SSantosh Shilimkar 
91*731e0cc6SSantosh Shilimkar 	cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
92*731e0cc6SSantosh Shilimkar 
93*731e0cc6SSantosh Shilimkar #ifdef CONFIG_CPU_FREQ_DEBUG
94*731e0cc6SSantosh Shilimkar 	pr_info("cpufreq-omap: transition: %u --> %u\n", freqs.old, freqs.new);
95*731e0cc6SSantosh Shilimkar #endif
96*731e0cc6SSantosh Shilimkar 
97*731e0cc6SSantosh Shilimkar 	ret = clk_set_rate(mpu_clk, freqs.new * 1000);
98*731e0cc6SSantosh Shilimkar 
99*731e0cc6SSantosh Shilimkar 	cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
100*731e0cc6SSantosh Shilimkar 
101*731e0cc6SSantosh Shilimkar 	return ret;
102*731e0cc6SSantosh Shilimkar }
103*731e0cc6SSantosh Shilimkar 
104*731e0cc6SSantosh Shilimkar static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy)
105*731e0cc6SSantosh Shilimkar {
106*731e0cc6SSantosh Shilimkar 	int result = 0;
107*731e0cc6SSantosh Shilimkar 	struct device *mpu_dev;
108*731e0cc6SSantosh Shilimkar 
109*731e0cc6SSantosh Shilimkar 	if (cpu_is_omap24xx())
110*731e0cc6SSantosh Shilimkar 		mpu_clk = clk_get(NULL, "virt_prcm_set");
111*731e0cc6SSantosh Shilimkar 	else if (cpu_is_omap34xx())
112*731e0cc6SSantosh Shilimkar 		mpu_clk = clk_get(NULL, "dpll1_ck");
113*731e0cc6SSantosh Shilimkar 	else if (cpu_is_omap44xx())
114*731e0cc6SSantosh Shilimkar 		mpu_clk = clk_get(NULL, "dpll_mpu_ck");
115*731e0cc6SSantosh Shilimkar 
116*731e0cc6SSantosh Shilimkar 	if (IS_ERR(mpu_clk))
117*731e0cc6SSantosh Shilimkar 		return PTR_ERR(mpu_clk);
118*731e0cc6SSantosh Shilimkar 
119*731e0cc6SSantosh Shilimkar 	if (policy->cpu != 0)
120*731e0cc6SSantosh Shilimkar 		return -EINVAL;
121*731e0cc6SSantosh Shilimkar 
122*731e0cc6SSantosh Shilimkar 	policy->cur = policy->min = policy->max = omap_getspeed(0);
123*731e0cc6SSantosh Shilimkar 
124*731e0cc6SSantosh Shilimkar 	mpu_dev = omap2_get_mpuss_device();
125*731e0cc6SSantosh Shilimkar 	if (!mpu_dev) {
126*731e0cc6SSantosh Shilimkar 		pr_warning("%s: unable to get the mpu device\n", __func__);
127*731e0cc6SSantosh Shilimkar 		return -EINVAL;
128*731e0cc6SSantosh Shilimkar 	}
129*731e0cc6SSantosh Shilimkar 	opp_init_cpufreq_table(mpu_dev, &freq_table);
130*731e0cc6SSantosh Shilimkar 
131*731e0cc6SSantosh Shilimkar 	if (freq_table) {
132*731e0cc6SSantosh Shilimkar 		result = cpufreq_frequency_table_cpuinfo(policy, freq_table);
133*731e0cc6SSantosh Shilimkar 		if (!result)
134*731e0cc6SSantosh Shilimkar 			cpufreq_frequency_table_get_attr(freq_table,
135*731e0cc6SSantosh Shilimkar 							policy->cpu);
136*731e0cc6SSantosh Shilimkar 	} else {
137*731e0cc6SSantosh Shilimkar 		policy->cpuinfo.min_freq = clk_round_rate(mpu_clk, 0) / 1000;
138*731e0cc6SSantosh Shilimkar 		policy->cpuinfo.max_freq = clk_round_rate(mpu_clk,
139*731e0cc6SSantosh Shilimkar 							VERY_HI_RATE) / 1000;
140*731e0cc6SSantosh Shilimkar 	}
141*731e0cc6SSantosh Shilimkar 
142*731e0cc6SSantosh Shilimkar 	policy->min = policy->cpuinfo.min_freq;
143*731e0cc6SSantosh Shilimkar 	policy->max = policy->cpuinfo.max_freq;
144*731e0cc6SSantosh Shilimkar 	policy->cur = omap_getspeed(0);
145*731e0cc6SSantosh Shilimkar 
146*731e0cc6SSantosh Shilimkar 	/* FIXME: what's the actual transition time? */
147*731e0cc6SSantosh Shilimkar 	policy->cpuinfo.transition_latency = 300 * 1000;
148*731e0cc6SSantosh Shilimkar 
149*731e0cc6SSantosh Shilimkar 	return 0;
150*731e0cc6SSantosh Shilimkar }
151*731e0cc6SSantosh Shilimkar 
152*731e0cc6SSantosh Shilimkar static int omap_cpu_exit(struct cpufreq_policy *policy)
153*731e0cc6SSantosh Shilimkar {
154*731e0cc6SSantosh Shilimkar 	clk_exit_cpufreq_table(&freq_table);
155*731e0cc6SSantosh Shilimkar 	clk_put(mpu_clk);
156*731e0cc6SSantosh Shilimkar 	return 0;
157*731e0cc6SSantosh Shilimkar }
158*731e0cc6SSantosh Shilimkar 
159*731e0cc6SSantosh Shilimkar static struct freq_attr *omap_cpufreq_attr[] = {
160*731e0cc6SSantosh Shilimkar 	&cpufreq_freq_attr_scaling_available_freqs,
161*731e0cc6SSantosh Shilimkar 	NULL,
162*731e0cc6SSantosh Shilimkar };
163*731e0cc6SSantosh Shilimkar 
164*731e0cc6SSantosh Shilimkar static struct cpufreq_driver omap_driver = {
165*731e0cc6SSantosh Shilimkar 	.flags		= CPUFREQ_STICKY,
166*731e0cc6SSantosh Shilimkar 	.verify		= omap_verify_speed,
167*731e0cc6SSantosh Shilimkar 	.target		= omap_target,
168*731e0cc6SSantosh Shilimkar 	.get		= omap_getspeed,
169*731e0cc6SSantosh Shilimkar 	.init		= omap_cpu_init,
170*731e0cc6SSantosh Shilimkar 	.exit		= omap_cpu_exit,
171*731e0cc6SSantosh Shilimkar 	.name		= "omap",
172*731e0cc6SSantosh Shilimkar 	.attr		= omap_cpufreq_attr,
173*731e0cc6SSantosh Shilimkar };
174*731e0cc6SSantosh Shilimkar 
175*731e0cc6SSantosh Shilimkar static int __init omap_cpufreq_init(void)
176*731e0cc6SSantosh Shilimkar {
177*731e0cc6SSantosh Shilimkar 	return cpufreq_register_driver(&omap_driver);
178*731e0cc6SSantosh Shilimkar }
179*731e0cc6SSantosh Shilimkar 
180*731e0cc6SSantosh Shilimkar static void __exit omap_cpufreq_exit(void)
181*731e0cc6SSantosh Shilimkar {
182*731e0cc6SSantosh Shilimkar 	cpufreq_unregister_driver(&omap_driver);
183*731e0cc6SSantosh Shilimkar }
184*731e0cc6SSantosh Shilimkar 
185*731e0cc6SSantosh Shilimkar MODULE_DESCRIPTION("cpufreq driver for OMAP SoCs");
186*731e0cc6SSantosh Shilimkar MODULE_LICENSE("GPL");
187*731e0cc6SSantosh Shilimkar module_init(omap_cpufreq_init);
188*731e0cc6SSantosh Shilimkar module_exit(omap_cpufreq_exit);
189