diff options
-rw-r--r-- | arch/arm/Kconfig | 1 | ||||
-rw-r--r-- | arch/arm/mach-ux500/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/mach-ux500/cpu-db8500.c | 1 | ||||
-rw-r--r-- | arch/arm/mach-ux500/cpufreq.c | 211 |
4 files changed, 214 insertions, 0 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index db524e75c4a2..c8d035afcac7 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig | |||
@@ -797,6 +797,7 @@ config ARCH_U8500 | |||
797 | select GENERIC_CLOCKEVENTS | 797 | select GENERIC_CLOCKEVENTS |
798 | select COMMON_CLKDEV | 798 | select COMMON_CLKDEV |
799 | select ARCH_REQUIRE_GPIOLIB | 799 | select ARCH_REQUIRE_GPIOLIB |
800 | select ARCH_HAS_CPUFREQ | ||
800 | help | 801 | help |
801 | Support for ST-Ericsson's Ux500 architecture | 802 | Support for ST-Ericsson's Ux500 architecture |
802 | 803 | ||
diff --git a/arch/arm/mach-ux500/Makefile b/arch/arm/mach-ux500/Makefile index 93b0cf162017..841118335607 100644 --- a/arch/arm/mach-ux500/Makefile +++ b/arch/arm/mach-ux500/Makefile | |||
@@ -13,3 +13,4 @@ obj-$(CONFIG_LOCAL_TIMERS) += localtimer.o | |||
13 | obj-$(CONFIG_REGULATOR_AB8500) += board-mop500-regulators.o | 13 | obj-$(CONFIG_REGULATOR_AB8500) += board-mop500-regulators.o |
14 | obj-$(CONFIG_U5500_MODEM_IRQ) += modem_irq.o | 14 | obj-$(CONFIG_U5500_MODEM_IRQ) += modem_irq.o |
15 | obj-$(CONFIG_U5500_MBOX) += mbox.o | 15 | obj-$(CONFIG_U5500_MBOX) += mbox.o |
16 | obj-$(CONFIG_CPU_FREQ) += cpufreq.o | ||
diff --git a/arch/arm/mach-ux500/cpu-db8500.c b/arch/arm/mach-ux500/cpu-db8500.c index 658384c86933..c0f34a404c53 100644 --- a/arch/arm/mach-ux500/cpu-db8500.c +++ b/arch/arm/mach-ux500/cpu-db8500.c | |||
@@ -179,6 +179,7 @@ void __init u8500_init_devices(void) | |||
179 | db8500_add_rtc(); | 179 | db8500_add_rtc(); |
180 | db8500_add_gpios(); | 180 | db8500_add_gpios(); |
181 | 181 | ||
182 | platform_device_register_simple("cpufreq-u8500", -1, NULL, 0); | ||
182 | platform_add_devices(platform_devs, ARRAY_SIZE(platform_devs)); | 183 | platform_add_devices(platform_devs, ARRAY_SIZE(platform_devs)); |
183 | 184 | ||
184 | return ; | 185 | return ; |
diff --git a/arch/arm/mach-ux500/cpufreq.c b/arch/arm/mach-ux500/cpufreq.c new file mode 100644 index 000000000000..5c5b747f134d --- /dev/null +++ b/arch/arm/mach-ux500/cpufreq.c | |||
@@ -0,0 +1,211 @@ | |||
1 | /* | ||
2 | * CPU frequency scaling for u8500 | ||
3 | * Inspired by linux/arch/arm/mach-davinci/cpufreq.c | ||
4 | * | ||
5 | * Copyright (C) STMicroelectronics 2009 | ||
6 | * Copyright (C) ST-Ericsson SA 2010 | ||
7 | * | ||
8 | * License Terms: GNU General Public License v2 | ||
9 | * | ||
10 | * Author: Sundar Iyer <sundar.iyer@stericsson.com> | ||
11 | * Author: Martin Persson <martin.persson@stericsson.com> | ||
12 | * Author: Jonas Aaberg <jonas.aberg@stericsson.com> | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #include <linux/platform_device.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/cpufreq.h> | ||
19 | #include <linux/delay.h> | ||
20 | |||
21 | #include <mach/hardware.h> | ||
22 | #include <mach/prcmu.h> | ||
23 | #include <mach/prcmu-defs.h> | ||
24 | |||
25 | #define DRIVER_NAME "cpufreq-u8500" | ||
26 | #define CPUFREQ_NAME "u8500" | ||
27 | |||
28 | static struct device *dev; | ||
29 | |||
30 | static struct cpufreq_frequency_table freq_table[] = { | ||
31 | [0] = { | ||
32 | .index = 0, | ||
33 | .frequency = 200000, | ||
34 | }, | ||
35 | [1] = { | ||
36 | .index = 1, | ||
37 | .frequency = 300000, | ||
38 | }, | ||
39 | [2] = { | ||
40 | .index = 2, | ||
41 | .frequency = 600000, | ||
42 | }, | ||
43 | [3] = { | ||
44 | /* Used for CPU_OPP_MAX, if available */ | ||
45 | .index = 3, | ||
46 | .frequency = CPUFREQ_TABLE_END, | ||
47 | }, | ||
48 | [4] = { | ||
49 | .index = 4, | ||
50 | .frequency = CPUFREQ_TABLE_END, | ||
51 | }, | ||
52 | }; | ||
53 | |||
54 | static enum prcmu_cpu_opp index2opp[] = { | ||
55 | CPU_OPP_EXT_CLK, | ||
56 | CPU_OPP_50, | ||
57 | CPU_OPP_100, | ||
58 | CPU_OPP_MAX | ||
59 | }; | ||
60 | |||
61 | static int u8500_cpufreq_verify_speed(struct cpufreq_policy *policy) | ||
62 | { | ||
63 | return cpufreq_frequency_table_verify(policy, freq_table); | ||
64 | } | ||
65 | |||
66 | static int u8500_cpufreq_target(struct cpufreq_policy *policy, | ||
67 | unsigned int target_freq, | ||
68 | unsigned int relation) | ||
69 | { | ||
70 | struct cpufreq_freqs freqs; | ||
71 | unsigned int index; | ||
72 | int ret = 0; | ||
73 | |||
74 | /* | ||
75 | * Ensure desired rate is within allowed range. Some govenors | ||
76 | * (ondemand) will just pass target_freq=0 to get the minimum. | ||
77 | */ | ||
78 | if (target_freq < policy->cpuinfo.min_freq) | ||
79 | target_freq = policy->cpuinfo.min_freq; | ||
80 | if (target_freq > policy->cpuinfo.max_freq) | ||
81 | target_freq = policy->cpuinfo.max_freq; | ||
82 | |||
83 | ret = cpufreq_frequency_table_target(policy, freq_table, | ||
84 | target_freq, relation, &index); | ||
85 | if (ret < 0) { | ||
86 | dev_err(dev, "Could not look up next frequency\n"); | ||
87 | return ret; | ||
88 | } | ||
89 | |||
90 | freqs.old = policy->cur; | ||
91 | freqs.new = freq_table[index].frequency; | ||
92 | freqs.cpu = policy->cpu; | ||
93 | |||
94 | if (freqs.old == freqs.new) { | ||
95 | dev_dbg(dev, "Current and target frequencies are equal\n"); | ||
96 | return 0; | ||
97 | } | ||
98 | |||
99 | dev_dbg(dev, "transition: %u --> %u\n", freqs.old, freqs.new); | ||
100 | cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); | ||
101 | |||
102 | ret = prcmu_set_cpu_opp(index2opp[index]); | ||
103 | if (ret < 0) { | ||
104 | dev_err(dev, "Failed to set OPP level\n"); | ||
105 | return ret; | ||
106 | } | ||
107 | |||
108 | cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); | ||
109 | |||
110 | return ret; | ||
111 | } | ||
112 | |||
113 | static unsigned int u8500_cpufreq_getspeed(unsigned int cpu) | ||
114 | { | ||
115 | int i; | ||
116 | |||
117 | for (i = 0; prcmu_get_cpu_opp() != index2opp[i]; i++) | ||
118 | ; | ||
119 | return freq_table[i].frequency; | ||
120 | } | ||
121 | |||
122 | static int __cpuinit u8500_cpu_init(struct cpufreq_policy *policy) | ||
123 | { | ||
124 | int res; | ||
125 | |||
126 | BUILD_BUG_ON(ARRAY_SIZE(index2opp) + 1 != ARRAY_SIZE(freq_table)); | ||
127 | |||
128 | if (cpu_is_u8500v2()) { | ||
129 | freq_table[1].frequency = 400000; | ||
130 | freq_table[2].frequency = 800000; | ||
131 | if (prcmu_has_arm_maxopp()) | ||
132 | freq_table[3].frequency = 1000000; | ||
133 | } | ||
134 | |||
135 | /* get policy fields based on the table */ | ||
136 | res = cpufreq_frequency_table_cpuinfo(policy, freq_table); | ||
137 | if (!res) | ||
138 | cpufreq_frequency_table_get_attr(freq_table, policy->cpu); | ||
139 | else { | ||
140 | dev_err(dev, "u8500-cpufreq : Failed to read policy table\n"); | ||
141 | return res; | ||
142 | } | ||
143 | |||
144 | policy->min = policy->cpuinfo.min_freq; | ||
145 | policy->max = policy->cpuinfo.max_freq; | ||
146 | policy->cur = u8500_cpufreq_getspeed(policy->cpu); | ||
147 | policy->governor = CPUFREQ_DEFAULT_GOVERNOR; | ||
148 | |||
149 | /* | ||
150 | * FIXME : Need to take time measurement across the target() | ||
151 | * function with no/some/all drivers in the notification | ||
152 | * list. | ||
153 | */ | ||
154 | policy->cpuinfo.transition_latency = 200 * 1000; /* in ns */ | ||
155 | |||
156 | /* policy sharing between dual CPUs */ | ||
157 | cpumask_copy(policy->cpus, &cpu_present_map); | ||
158 | |||
159 | policy->shared_type = CPUFREQ_SHARED_TYPE_ALL; | ||
160 | |||
161 | return res; | ||
162 | } | ||
163 | |||
164 | static struct freq_attr *u8500_cpufreq_attr[] = { | ||
165 | &cpufreq_freq_attr_scaling_available_freqs, | ||
166 | NULL, | ||
167 | }; | ||
168 | static int u8500_cpu_exit(struct cpufreq_policy *policy) | ||
169 | { | ||
170 | cpufreq_frequency_table_put_attr(policy->cpu); | ||
171 | return 0; | ||
172 | } | ||
173 | |||
174 | static struct cpufreq_driver u8500_driver = { | ||
175 | .owner = THIS_MODULE, | ||
176 | .flags = CPUFREQ_STICKY, | ||
177 | .verify = u8500_cpufreq_verify_speed, | ||
178 | .target = u8500_cpufreq_target, | ||
179 | .get = u8500_cpufreq_getspeed, | ||
180 | .init = u8500_cpu_init, | ||
181 | .exit = u8500_cpu_exit, | ||
182 | .name = CPUFREQ_NAME, | ||
183 | .attr = u8500_cpufreq_attr, | ||
184 | }; | ||
185 | |||
186 | static int __init u8500_cpufreq_probe(struct platform_device *pdev) | ||
187 | { | ||
188 | dev = &pdev->dev; | ||
189 | return cpufreq_register_driver(&u8500_driver); | ||
190 | } | ||
191 | |||
192 | static int __exit u8500_cpufreq_remove(struct platform_device *pdev) | ||
193 | { | ||
194 | return cpufreq_unregister_driver(&u8500_driver); | ||
195 | } | ||
196 | |||
197 | static struct platform_driver u8500_cpufreq_driver = { | ||
198 | .driver = { | ||
199 | .name = DRIVER_NAME, | ||
200 | .owner = THIS_MODULE, | ||
201 | }, | ||
202 | .remove = __exit_p(u8500_cpufreq_remove), | ||
203 | }; | ||
204 | |||
205 | static int __init u8500_cpufreq_init(void) | ||
206 | { | ||
207 | return platform_driver_probe(&u8500_cpufreq_driver, | ||
208 | &u8500_cpufreq_probe); | ||
209 | } | ||
210 | |||
211 | device_initcall(u8500_cpufreq_init); | ||