-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathcpufreq.c
195 lines (156 loc) · 4.3 KB
/
cpufreq.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
/*
* cpufreq.c -- MX35 CPUfreq driver.
* MX35 cpufreq driver
*
* Copyright 2009 Lab126, Inc. All rights reserved.
* Manish Lachwani ([email protected])
*
* Support for CPUFreq on the Luigi. It supports two operating points:
* 266MHz and 532MHz. Along with the CPU Frequency scaling, the voltage
* is also adjusted to be 1.2V (266MHz) or 1.4V (532MHz).
*
*/
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/cpufreq.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <asm/hardware.h>
#include <asm/setup.h>
#include <asm/io.h>
#include <asm/arch/clock.h>
#include <asm/cacheflush.h>
#include "crm_regs.h"
static struct clk *cpu_clk;
DEFINE_RAW_SPINLOCK(cpufreq_lock);
#define MXC_PERCLK_DIVIDER 0x1000
/*
* turn debug on/off
*/
#undef MX3_CPU_FREQ_DEBUG
static struct cpufreq_frequency_table mx35_freq_table_con[] = {
{0x01, 266000},
{0x02, 532000},
{0, CPUFREQ_TABLE_END},
};
static int mx35_verify_speed(struct cpufreq_policy *policy)
{
if (policy->cpu != 0)
return -EINVAL;
return cpufreq_frequency_table_verify(policy, mx35_freq_table_con);
}
static unsigned int mx35_get_speed(unsigned int cpu)
{
if (cpu)
return 0;
return clk_get_rate(cpu_clk) / 1000;
}
static int calc_frequency_con(int target, unsigned int relation)
{
int i = 0;
if (relation == CPUFREQ_RELATION_H) {
for (i = 1; i >= 0; i--) {
if (mx35_freq_table_con[i].frequency <= target)
return mx35_freq_table_con[i].frequency;
}
} else if (relation == CPUFREQ_RELATION_L) {
for (i = 0; i <= 1; i++) {
if (mx35_freq_table_con[i].frequency >= target)
return mx35_freq_table_con[i].frequency;
}
}
printk(KERN_ERR "Error: No valid cpufreq relation\n");
return 532000;
}
/*
* Set the destination CPU frequency target
*/
static int mx35_set_target(struct cpufreq_policy *policy,
unsigned int target_freq,
unsigned int relation)
{
struct cpufreq_freqs freqs;
long freq;
unsigned long flags;
unsigned long pdr0;
int orig_cpu_rate = clk_get_rate(cpu_clk);
if (target_freq < policy->cpuinfo.min_freq)
target_freq = policy->cpuinfo.min_freq;
if (target_freq < policy->min)
target_freq = policy->min;
freq = calc_frequency_con(target_freq, relation) * 1000;
freqs.old = clk_get_rate(cpu_clk) / 1000;
freqs.new = (freq + 500) / 1000;
freqs.cpu = 0;
freqs.flags = 0;
if (freq == orig_cpu_rate)
return 0;
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
#ifdef MX3_CPU_FREQ_DEBUG
printk ("ARM frequency: %dHz\n", (int)clk_get_rate(cpu_clk));
#endif
spin_lock_irqsave(&cpufreq_lock, flags);
/*
* MCU clk
*/
clk_set_rate(cpu_clk, freq);
pdr0 = __raw_readl(MXC_CCM_PDR0);
pdr0 |= MXC_PERCLK_DIVIDER;
__raw_writel(pdr0, MXC_CCM_PDR0);
spin_unlock_irqrestore(&cpufreq_lock, flags);
#ifdef MX3_CPU_FREQ_DEBUG
printk ("ARM frequency after change: %dHz\n", (int)clk_get_rate(cpu_clk));
#endif
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
clk_put(cpu_clk);
return 0;
}
/*
* Driver initialization
*/
static int __init mx35_cpufreq_driver_init(struct cpufreq_policy *policy)
{
int ret = 0;
printk("Luigi/Freescale MX35 CPUFREQ driver\n");
if (policy->cpu != 0)
return -EINVAL;
cpu_clk = clk_get(NULL, "cpu_clk");
if (IS_ERR(cpu_clk))
return PTR_ERR(cpu_clk);
policy->cur = policy->min = policy->max =
clk_get_rate(cpu_clk) / 1000;
policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
policy->cpuinfo.min_freq = 266000;
policy->cpuinfo.max_freq = 532000;
/* Set the transition latency to 50 us */
policy->cpuinfo.transition_latency = 5 * 10000;
ret = cpufreq_frequency_table_cpuinfo(policy, mx35_freq_table_con);
if (ret < 0)
return ret;
clk_put(cpu_clk);
cpufreq_frequency_table_get_attr(mx35_freq_table_con, policy->cpu);
return 0;
}
static int mx35_cpufreq_driver_exit(struct cpufreq_policy *policy)
{
cpufreq_frequency_table_put_attr(policy->cpu);
clk_set_rate(cpu_clk, 532000 * 1000);
clk_put(cpu_clk);
return 0;
}
static struct cpufreq_driver mx35_driver = {
.flags = CPUFREQ_STICKY,
.verify = mx35_verify_speed,
.target = mx35_set_target,
.get = mx35_get_speed,
.init = mx35_cpufreq_driver_init,
.exit = mx35_cpufreq_driver_exit,
.name = "MX31",
};
static int __init mx35_cpufreq_init(void)
{
return cpufreq_register_driver(&mx35_driver);
}
arch_initcall(mx35_cpufreq_init);