Date: | 2011-03-16 03:16:04 (12 years 2 months ago) |
---|---|
Author: | Maarten ter Huurne |
Commit: | 694c7fbe86b8a9c91392e505afcb9fcfc91deccc |
Message: | MIPS: JZ4740: Add cpufreq support This is a squashed version of Uli's driver that was further developed in the qi-kernel repository. |
Files: |
arch/mips/Kconfig (1 diff) arch/mips/jz4740/Makefile (1 diff) arch/mips/jz4740/cpufreq.c (1 diff) drivers/cpufreq/Kconfig (1 diff) |
Change Details
arch/mips/Kconfig | ||
---|---|---|
236 | 236 | select HAVE_PWM |
237 | 237 | select HAVE_CLK |
238 | 238 | select GENERIC_IRQ_CHIP |
239 | select CPU_SUPPORTS_CPUFREQ | |
239 | 240 | |
240 | 241 | config LANTIQ |
241 | 242 | bool "Lantiq based platforms" |
arch/mips/jz4740/Makefile | ||
---|---|---|
16 | 16 | # PM support |
17 | 17 | |
18 | 18 | obj-$(CONFIG_PM) += pm.o |
19 | obj-$(CONFIG_CPU_FREQ_JZ) += cpufreq.o |
arch/mips/jz4740/cpufreq.c | ||
---|---|---|
1 | /* | |
2 | * linux/arch/mips/jz4740/cpufreq.c | |
3 | * | |
4 | * cpufreq driver for JZ4740 | |
5 | * | |
6 | * Copyright (c) 2010 Ulrich Hecht <ulrich.hecht@gmail.com> | |
7 | * Copyright (c) 2010 Maarten ter Huurne <maarten@treewalker.org> | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License version 2 as | |
11 | * published by the Free Software Foundation. | |
12 | */ | |
13 | ||
14 | #include <linux/kernel.h> | |
15 | #include <linux/module.h> | |
16 | #include <linux/init.h> | |
17 | #include <linux/err.h> | |
18 | ||
19 | #include <linux/cpufreq.h> | |
20 | ||
21 | #include <linux/clk.h> | |
22 | #include <asm/mach-jz4740/base.h> | |
23 | ||
24 | #include "clock.h" | |
25 | ||
26 | #define DEBUG_CPUFREQ | |
27 | ||
28 | #ifdef DEBUG_CPUFREQ | |
29 | #define dprintk(X...) printk(KERN_INFO X) | |
30 | #else | |
31 | #define dprintk(X...) do { } while(0) | |
32 | #endif | |
33 | ||
34 | #define HCLK_MIN 30000 | |
35 | /* TODO: The maximum MCLK most likely depends on the SDRAM chips used, | |
36 | so it is board-specific. */ | |
37 | #define MCLK_MAX 140000 | |
38 | ||
39 | /* Same as jz_clk_main_divs, but with 24 and 32 removed because the hardware | |
40 | spec states those dividers must not be used for CCLK or HCLK. */ | |
41 | static const unsigned int jz4740_freq_cpu_divs[] = {1, 2, 3, 4, 6, 8, 12, 16}; | |
42 | ||
43 | struct jz4740_freq_percpu_info { | |
44 | unsigned int pll_rate; | |
45 | struct cpufreq_frequency_table table[ | |
46 | ARRAY_SIZE(jz4740_freq_cpu_divs) + 1]; | |
47 | }; | |
48 | ||
49 | static struct clk *pll; | |
50 | static struct clk *cclk; | |
51 | ||
52 | static struct jz4740_freq_percpu_info jz4740_freq_info; | |
53 | ||
54 | static struct cpufreq_driver cpufreq_jz4740_driver; | |
55 | ||
56 | static void jz4740_freq_fill_table(struct cpufreq_policy *policy, | |
57 | unsigned int pll_rate) | |
58 | { | |
59 | struct cpufreq_frequency_table *table = &jz4740_freq_info.table[0]; | |
60 | int i; | |
61 | ||
62 | #ifdef CONFIG_CPU_FREQ_STAT_DETAILS | |
63 | /* for showing /sys/devices/system/cpu/cpuX/cpufreq/stats/ */ | |
64 | static bool init = false; | |
65 | if (init) | |
66 | cpufreq_frequency_table_put_attr(policy->cpu); | |
67 | else | |
68 | init = true; | |
69 | #endif | |
70 | ||
71 | jz4740_freq_info.pll_rate = pll_rate; | |
72 | ||
73 | for (i = 0; i < ARRAY_SIZE(jz4740_freq_cpu_divs); i++) { | |
74 | unsigned int freq = pll_rate / jz4740_freq_cpu_divs[i]; | |
75 | if (freq < HCLK_MIN) break; | |
76 | table[i].index = i; | |
77 | table[i].frequency = freq; | |
78 | } | |
79 | table[i].index = i; | |
80 | table[i].frequency = CPUFREQ_TABLE_END; | |
81 | ||
82 | policy->min = table[i - 1].frequency; | |
83 | policy->max = table[0].frequency; | |
84 | ||
85 | #ifdef CONFIG_CPU_FREQ_STAT_DETAILS | |
86 | cpufreq_frequency_table_get_attr(table, policy->cpu); | |
87 | #endif | |
88 | } | |
89 | ||
90 | static unsigned int jz4740_freq_get(unsigned int cpu) | |
91 | { | |
92 | return clk_get_rate(cclk) / 1000; | |
93 | } | |
94 | ||
95 | static int jz4740_freq_verify(struct cpufreq_policy *policy) | |
96 | { | |
97 | unsigned int new_pll; | |
98 | ||
99 | cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, | |
100 | policy->cpuinfo.max_freq); | |
101 | ||
102 | new_pll = clk_round_rate(pll, policy->max * 1000) / 1000; | |
103 | if (jz4740_freq_info.pll_rate != new_pll) | |
104 | jz4740_freq_fill_table(policy, new_pll); | |
105 | ||
106 | return 0; | |
107 | } | |
108 | ||
109 | static int jz4740_freq_target(struct cpufreq_policy *policy, | |
110 | unsigned int target_freq, | |
111 | unsigned int relation) | |
112 | { | |
113 | struct cpufreq_frequency_table *table = &jz4740_freq_info.table[0]; | |
114 | struct cpufreq_freqs freqs; | |
115 | unsigned int new_index = 0; | |
116 | unsigned int old_pll = clk_get_rate(pll) / 1000; | |
117 | unsigned int new_pll = jz4740_freq_info.pll_rate; | |
118 | int ret = 0; | |
119 | ||
120 | if (cpufreq_frequency_table_target(policy, table, | |
121 | target_freq, relation, &new_index)) | |
122 | return -EINVAL; | |
123 | freqs = (struct cpufreq_freqs) { | |
124 | .old = jz4740_freq_get(policy->cpu), | |
125 | .new = table[new_index].frequency, | |
126 | .cpu = policy->cpu, | |
127 | .flags = cpufreq_jz4740_driver.flags, | |
128 | }; | |
129 | if (freqs.new != freqs.old || new_pll != old_pll) { | |
130 | unsigned int cdiv, hdiv, mdiv, pdiv; | |
131 | cdiv = jz4740_freq_cpu_divs[new_index]; | |
132 | hdiv = (cdiv == 3 || cdiv == 6) ? cdiv * 2 : cdiv * 3; | |
133 | while (new_pll < HCLK_MIN * hdiv) | |
134 | hdiv -= cdiv; | |
135 | mdiv = hdiv; | |
136 | if (new_pll > MCLK_MAX * mdiv) { | |
137 | /* 4,4 performs better than 3,6 */ | |
138 | if (new_pll > MCLK_MAX * 4) | |
139 | mdiv *= 2; | |
140 | else | |
141 | hdiv = mdiv = cdiv * 4; | |
142 | } | |
143 | pdiv = mdiv; | |
144 | dprintk(KERN_INFO "%s: cclk %p, setting from %d to %d, " | |
145 | "dividers %d, %d, %d, %d\n", | |
146 | __FUNCTION__, cclk, freqs.old, freqs.new, | |
147 | cdiv, hdiv, mdiv, pdiv); | |
148 | cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); | |
149 | ret = clk_main_set_dividers(new_pll == old_pll, | |
150 | cdiv, hdiv, mdiv, pdiv); | |
151 | if (ret) { | |
152 | dprintk(KERN_INFO "failed to set dividers\n"); | |
153 | } else if (new_pll != old_pll) { | |
154 | dprintk(KERN_INFO "%s: pll %p, setting from %d to %d\n", | |
155 | __FUNCTION__, pll, old_pll, new_pll); | |
156 | ret = clk_set_rate(pll, new_pll * 1000); | |
157 | } | |
158 | cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); | |
159 | } | |
160 | ||
161 | return ret; | |
162 | } | |
163 | ||
164 | static int jz4740_cpufreq_driver_init(struct cpufreq_policy *policy) | |
165 | { | |
166 | int ret; | |
167 | ||
168 | dprintk(KERN_INFO "Jz4740 cpufreq driver\n"); | |
169 | ||
170 | if (policy->cpu != 0) | |
171 | return -EINVAL; | |
172 | ||
173 | pll = clk_get(NULL, "pll"); | |
174 | if (IS_ERR(pll)) { | |
175 | ret = PTR_ERR(pll); | |
176 | goto err_exit; | |
177 | } | |
178 | ||
179 | cclk = clk_get(NULL, "cclk"); | |
180 | if (IS_ERR(cclk)) { | |
181 | ret = PTR_ERR(cclk); | |
182 | goto err_clk_put_pll; | |
183 | } | |
184 | ||
185 | policy->cpuinfo.min_freq = HCLK_MIN; | |
186 | policy->cpuinfo.max_freq = 500000; | |
187 | policy->cpuinfo.transition_latency = 100000; /* in nanoseconds */ | |
188 | policy->cur = jz4740_freq_get(policy->cpu); | |
189 | policy->governor = CPUFREQ_DEFAULT_GOVERNOR; | |
190 | /* min and max are set by jz4740_freq_fill_table() */ | |
191 | ||
192 | jz4740_freq_fill_table(policy, clk_get_rate(pll) / 1000 /* in kHz */); | |
193 | ||
194 | return 0; | |
195 | ||
196 | err_clk_put_pll: | |
197 | clk_put(pll); | |
198 | err_exit: | |
199 | return ret; | |
200 | } | |
201 | ||
202 | static struct cpufreq_driver cpufreq_jz4740_driver = { | |
203 | .init = jz4740_cpufreq_driver_init, | |
204 | .verify = jz4740_freq_verify, | |
205 | .target = jz4740_freq_target, | |
206 | .get = jz4740_freq_get, | |
207 | .name = "jz4740", | |
208 | }; | |
209 | ||
210 | static int __init jz4740_cpufreq_init(void) | |
211 | { | |
212 | return cpufreq_register_driver(&cpufreq_jz4740_driver); | |
213 | } | |
214 | ||
215 | static void __exit jz4740_cpufreq_exit(void) | |
216 | { | |
217 | cpufreq_unregister_driver(&cpufreq_jz4740_driver); | |
218 | } | |
219 | ||
220 | module_init(jz4740_cpufreq_init); | |
221 | module_exit(jz4740_cpufreq_exit); | |
222 | ||
223 | MODULE_AUTHOR("Ulrich Hecht <ulrich.hecht@gmail.com>, " | |
224 | "Maarten ter Huurne <maarten@treewalker.org>"); | |
225 | MODULE_DESCRIPTION("cpufreq driver for Jz4740"); | |
226 | MODULE_LICENSE("GPL"); |
drivers/cpufreq/Kconfig | ||
---|---|---|
243 | 243 | |
244 | 244 | If in doubt, say N. |
245 | 245 | |
246 | config CPU_FREQ_JZ | |
247 | tristate "CPUfreq driver for JZ CPUs" | |
248 | select CPU_FREQ_TABLE | |
249 | depends on MACH_JZ4740 | |
250 | default n | |
251 | help | |
252 | This enables the CPUfreq driver for JZ CPUs. | |
253 | ||
254 | If in doubt, say N. | |
255 | ||
246 | 256 | endmenu |
247 | 257 | |
248 | 258 | menu "PowerPC CPU frequency scaling drivers" |
Branches:
ben-wpan
ben-wpan-stefan
5396a9238205f20f811ea57898980d3ca82df0b6
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9