diff options
-rw-r--r-- | arch/powerpc/platforms/cell/Kconfig | 10 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/Makefile | 4 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/cbe_cpufreq.c | 196 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/cbe_cpufreq.h | 24 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/cbe_cpufreq_pervasive.c | 115 | ||||
-rw-r--r-- | arch/powerpc/platforms/cell/cbe_cpufreq_pmi.c | 148 |
6 files changed, 309 insertions, 188 deletions
diff --git a/arch/powerpc/platforms/cell/Kconfig b/arch/powerpc/platforms/cell/Kconfig index 9b2b386ccf48..ac8032034fb8 100644 --- a/arch/powerpc/platforms/cell/Kconfig +++ b/arch/powerpc/platforms/cell/Kconfig | |||
@@ -73,4 +73,14 @@ config CBE_CPUFREQ | |||
73 | For details, take a look at <file:Documentation/cpu-freq/>. | 73 | For details, take a look at <file:Documentation/cpu-freq/>. |
74 | If you don't have such processor, say N | 74 | If you don't have such processor, say N |
75 | 75 | ||
76 | config CBE_CPUFREQ_PMI | ||
77 | tristate "CBE frequency scaling using PMI interface" | ||
78 | depends on CBE_CPUFREQ && PPC_PMI && EXPERIMENTAL | ||
79 | default n | ||
80 | help | ||
81 | Select this, if you want to use the PMI interface | ||
82 | to switch frequencies. Using PMI, the | ||
83 | processor will not only be able to run at lower speed, | ||
84 | but also at lower core voltage. | ||
85 | |||
76 | endmenu | 86 | endmenu |
diff --git a/arch/powerpc/platforms/cell/Makefile b/arch/powerpc/platforms/cell/Makefile index 869af89df6ff..be059718becd 100644 --- a/arch/powerpc/platforms/cell/Makefile +++ b/arch/powerpc/platforms/cell/Makefile | |||
@@ -4,7 +4,9 @@ obj-$(CONFIG_PPC_CELL_NATIVE) += interrupt.o iommu.o setup.o \ | |||
4 | obj-$(CONFIG_CBE_RAS) += ras.o | 4 | obj-$(CONFIG_CBE_RAS) += ras.o |
5 | 5 | ||
6 | obj-$(CONFIG_CBE_THERM) += cbe_thermal.o | 6 | obj-$(CONFIG_CBE_THERM) += cbe_thermal.o |
7 | obj-$(CONFIG_CBE_CPUFREQ) += cbe_cpufreq.o | 7 | obj-$(CONFIG_CBE_CPUFREQ_PMI) += cbe_cpufreq_pmi.o |
8 | obj-$(CONFIG_CBE_CPUFREQ) += cbe-cpufreq.o | ||
9 | cbe-cpufreq-y += cbe_cpufreq_pervasive.o cbe_cpufreq.o | ||
8 | 10 | ||
9 | ifeq ($(CONFIG_SMP),y) | 11 | ifeq ($(CONFIG_SMP),y) |
10 | obj-$(CONFIG_PPC_CELL_NATIVE) += smp.o | 12 | obj-$(CONFIG_PPC_CELL_NATIVE) += smp.o |
diff --git a/arch/powerpc/platforms/cell/cbe_cpufreq.c b/arch/powerpc/platforms/cell/cbe_cpufreq.c index a62562ee1461..9a9932624c0c 100644 --- a/arch/powerpc/platforms/cell/cbe_cpufreq.c +++ b/arch/powerpc/platforms/cell/cbe_cpufreq.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * cpufreq driver for the cell processor | 2 | * cpufreq driver for the cell processor |
3 | * | 3 | * |
4 | * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 | 4 | * (C) Copyright IBM Deutschland Entwicklung GmbH 2005-2007 |
5 | * | 5 | * |
6 | * Author: Christian Krafft <krafft@de.ibm.com> | 6 | * Author: Christian Krafft <krafft@de.ibm.com> |
7 | * | 7 | * |
@@ -21,18 +21,10 @@ | |||
21 | */ | 21 | */ |
22 | 22 | ||
23 | #include <linux/cpufreq.h> | 23 | #include <linux/cpufreq.h> |
24 | #include <linux/timer.h> | ||
25 | |||
26 | #include <asm/hw_irq.h> | ||
27 | #include <asm/io.h> | ||
28 | #include <asm/machdep.h> | 24 | #include <asm/machdep.h> |
29 | #include <asm/processor.h> | ||
30 | #include <asm/prom.h> | ||
31 | #include <asm/time.h> | ||
32 | #include <asm/pmi.h> | ||
33 | #include <asm/of_platform.h> | 25 | #include <asm/of_platform.h> |
34 | 26 | #include <asm/prom.h> | |
35 | #include "cbe_regs.h" | 27 | #include "cbe_cpufreq.h" |
36 | 28 | ||
37 | static DEFINE_MUTEX(cbe_switch_mutex); | 29 | static DEFINE_MUTEX(cbe_switch_mutex); |
38 | 30 | ||
@@ -50,183 +42,24 @@ static struct cpufreq_frequency_table cbe_freqs[] = { | |||
50 | {0, CPUFREQ_TABLE_END}, | 42 | {0, CPUFREQ_TABLE_END}, |
51 | }; | 43 | }; |
52 | 44 | ||
53 | /* to write to MIC register */ | ||
54 | static u64 MIC_Slow_Fast_Timer_table[] = { | ||
55 | [0 ... 7] = 0x007fc00000000000ull, | ||
56 | }; | ||
57 | |||
58 | /* more values for the MIC */ | ||
59 | static u64 MIC_Slow_Next_Timer_table[] = { | ||
60 | 0x0000240000000000ull, | ||
61 | 0x0000268000000000ull, | ||
62 | 0x000029C000000000ull, | ||
63 | 0x00002D0000000000ull, | ||
64 | 0x0000300000000000ull, | ||
65 | 0x0000334000000000ull, | ||
66 | 0x000039C000000000ull, | ||
67 | 0x00003FC000000000ull, | ||
68 | }; | ||
69 | |||
70 | static u8 pmi_slow_mode_limit[MAX_BE]; | ||
71 | |||
72 | /* | 45 | /* |
73 | * hardware specific functions | 46 | * hardware specific functions |
74 | */ | 47 | */ |
75 | 48 | ||
76 | static bool cbe_cpufreq_has_pmi; | 49 | static int set_pmode(unsigned int cpu, unsigned int slow_mode) |
77 | |||
78 | #ifdef CONFIG_PPC_PMI | ||
79 | static int set_pmode_pmi(int cpu, unsigned int pmode) | ||
80 | { | ||
81 | int ret; | ||
82 | pmi_message_t pmi_msg; | ||
83 | #ifdef DEBUG | ||
84 | long time; | ||
85 | #endif | ||
86 | |||
87 | pmi_msg.type = PMI_TYPE_FREQ_CHANGE; | ||
88 | pmi_msg.data1 = cbe_cpu_to_node(cpu); | ||
89 | pmi_msg.data2 = pmode; | ||
90 | |||
91 | #ifdef DEBUG | ||
92 | time = jiffies; | ||
93 | #endif | ||
94 | |||
95 | pmi_send_message(pmi_msg); | ||
96 | ret = pmi_msg.data2; | ||
97 | |||
98 | pr_debug("PMI returned slow mode %d\n", ret); | ||
99 | |||
100 | #ifdef DEBUG | ||
101 | time = jiffies - time; /* actual cycles (not cpu cycles!) */ | ||
102 | time = jiffies_to_msecs(time); | ||
103 | pr_debug("had to wait %lu ms for a transition using PMI.\n", time); | ||
104 | #endif | ||
105 | return ret; | ||
106 | } | ||
107 | #endif | ||
108 | |||
109 | static int get_pmode(int cpu) | ||
110 | { | ||
111 | int ret; | ||
112 | struct cbe_pmd_regs __iomem *pmd_regs; | ||
113 | |||
114 | pmd_regs = cbe_get_cpu_pmd_regs(cpu); | ||
115 | ret = in_be64(&pmd_regs->pmsr) & 0x07; | ||
116 | |||
117 | return ret; | ||
118 | } | ||
119 | |||
120 | static int set_pmode_reg(int cpu, unsigned int pmode) | ||
121 | { | ||
122 | struct cbe_pmd_regs __iomem *pmd_regs; | ||
123 | struct cbe_mic_tm_regs __iomem *mic_tm_regs; | ||
124 | u64 flags; | ||
125 | u64 value; | ||
126 | #ifdef DEBUG | ||
127 | long time; | ||
128 | #endif | ||
129 | |||
130 | local_irq_save(flags); | ||
131 | |||
132 | mic_tm_regs = cbe_get_cpu_mic_tm_regs(cpu); | ||
133 | pmd_regs = cbe_get_cpu_pmd_regs(cpu); | ||
134 | |||
135 | #ifdef DEBUG | ||
136 | time = jiffies; | ||
137 | #endif | ||
138 | out_be64(&mic_tm_regs->slow_fast_timer_0, MIC_Slow_Fast_Timer_table[pmode]); | ||
139 | out_be64(&mic_tm_regs->slow_fast_timer_1, MIC_Slow_Fast_Timer_table[pmode]); | ||
140 | |||
141 | out_be64(&mic_tm_regs->slow_next_timer_0, MIC_Slow_Next_Timer_table[pmode]); | ||
142 | out_be64(&mic_tm_regs->slow_next_timer_1, MIC_Slow_Next_Timer_table[pmode]); | ||
143 | |||
144 | value = in_be64(&pmd_regs->pmcr); | ||
145 | /* set bits to zero */ | ||
146 | value &= 0xFFFFFFFFFFFFFFF8ull; | ||
147 | /* set bits to next pmode */ | ||
148 | value |= pmode; | ||
149 | |||
150 | out_be64(&pmd_regs->pmcr, value); | ||
151 | |||
152 | #ifdef DEBUG | ||
153 | /* wait until new pmode appears in status register */ | ||
154 | value = in_be64(&pmd_regs->pmsr) & 0x07; | ||
155 | while(value != pmode) { | ||
156 | cpu_relax(); | ||
157 | value = in_be64(&pmd_regs->pmsr) & 0x07; | ||
158 | } | ||
159 | |||
160 | time = jiffies - time; | ||
161 | time = jiffies_to_msecs(time); | ||
162 | pr_debug("had to wait %lu ms for a transition using " \ | ||
163 | "the pervasive unit.\n", time); | ||
164 | #endif | ||
165 | local_irq_restore(flags); | ||
166 | |||
167 | return 0; | ||
168 | } | ||
169 | |||
170 | static int set_pmode(int cpu, unsigned int slow_mode) | ||
171 | { | 50 | { |
172 | int rc; | 51 | int rc; |
173 | #ifdef CONFIG_PPC_PMI | 52 | |
174 | if (cbe_cpufreq_has_pmi) | 53 | if (cbe_cpufreq_has_pmi) |
175 | rc = set_pmode_pmi(cpu, slow_mode); | 54 | rc = cbe_cpufreq_set_pmode_pmi(cpu, slow_mode); |
176 | else | 55 | else |
177 | #endif | 56 | rc = cbe_cpufreq_set_pmode(cpu, slow_mode); |
178 | rc = set_pmode_reg(cpu, slow_mode); | ||
179 | 57 | ||
180 | pr_debug("register contains slow mode %d\n", get_pmode(cpu)); | 58 | pr_debug("register contains slow mode %d\n", cbe_cpufreq_get_pmode(cpu)); |
181 | 59 | ||
182 | return rc; | 60 | return rc; |
183 | } | 61 | } |
184 | 62 | ||
185 | static void cbe_cpufreq_handle_pmi(pmi_message_t pmi_msg) | ||
186 | { | ||
187 | u8 node; slow_mode; | ||
188 | |||
189 | BUG_ON(pmi_msg.type != PMI_TYPE_FREQ_CHANGE); | ||
190 | |||
191 | node = pmi_msg.data1; | ||
192 | slow_mode = pmi_msg.data2; | ||
193 | |||
194 | pmi_slow_mode_limit[node] = slow_mode; | ||
195 | |||
196 | pr_debug("cbe_handle_pmi: node: %d, max slow_mode=%d\n", slow_mode); | ||
197 | } | ||
198 | |||
199 | static int pmi_notifier(struct notifier_block *nb, | ||
200 | unsigned long event, void *data) | ||
201 | { | ||
202 | struct cpufreq_policy *policy = data; | ||
203 | u8 node; | ||
204 | |||
205 | node = cbe_cpu_to_node(policy->cpu); | ||
206 | |||
207 | pr_debug("got notified, event=%lu, node=%u\n", event, node); | ||
208 | |||
209 | if (pmi_slow_mode_limit[node] != 0) { | ||
210 | pr_debug("limiting node %d to slow mode %d\n", | ||
211 | node, pmi_slow_mode_limit[node]); | ||
212 | |||
213 | cpufreq_verify_within_limits(policy, 0, | ||
214 | cbe_freqs[pmi_slow_mode_limit[node]].frequency); | ||
215 | } | ||
216 | |||
217 | return 0; | ||
218 | } | ||
219 | |||
220 | static struct notifier_block pmi_notifier_block = { | ||
221 | .notifier_call = pmi_notifier, | ||
222 | }; | ||
223 | |||
224 | static struct pmi_handler cbe_pmi_handler = { | ||
225 | .type = PMI_TYPE_FREQ_CHANGE, | ||
226 | .handle_pmi_message = cbe_cpufreq_handle_pmi, | ||
227 | }; | ||
228 | |||
229 | |||
230 | /* | 63 | /* |
231 | * cpufreq functions | 64 | * cpufreq functions |
232 | */ | 65 | */ |
@@ -270,7 +103,7 @@ static int cbe_cpufreq_cpu_init(struct cpufreq_policy *policy) | |||
270 | * of a transition */ | 103 | * of a transition */ |
271 | policy->cpuinfo.transition_latency = 25000; | 104 | policy->cpuinfo.transition_latency = 25000; |
272 | 105 | ||
273 | cur_pmode = get_pmode(policy->cpu); | 106 | cur_pmode = cbe_cpufreq_get_pmode(policy->cpu); |
274 | pr_debug("current pmode is at %d\n",cur_pmode); | 107 | pr_debug("current pmode is at %d\n",cur_pmode); |
275 | 108 | ||
276 | policy->cur = cbe_freqs[cur_pmode].frequency; | 109 | policy->cur = cbe_freqs[cur_pmode].frequency; |
@@ -297,7 +130,6 @@ static int cbe_cpufreq_verify(struct cpufreq_policy *policy) | |||
297 | return cpufreq_frequency_table_verify(policy, cbe_freqs); | 130 | return cpufreq_frequency_table_verify(policy, cbe_freqs); |
298 | } | 131 | } |
299 | 132 | ||
300 | |||
301 | static int cbe_cpufreq_target(struct cpufreq_policy *policy, | 133 | static int cbe_cpufreq_target(struct cpufreq_policy *policy, |
302 | unsigned int target_freq, | 134 | unsigned int target_freq, |
303 | unsigned int relation) | 135 | unsigned int relation) |
@@ -352,22 +184,12 @@ static int __init cbe_cpufreq_init(void) | |||
352 | if (!machine_is(cell)) | 184 | if (!machine_is(cell)) |
353 | return -ENODEV; | 185 | return -ENODEV; |
354 | 186 | ||
355 | cbe_cpufreq_has_pmi = pmi_register_handler(&cbe_pmi_handler) == 0; | ||
356 | |||
357 | if (cbe_cpufreq_has_pmi) | ||
358 | cpufreq_register_notifier(&pmi_notifier_block, CPUFREQ_POLICY_NOTIFIER); | ||
359 | |||
360 | return cpufreq_register_driver(&cbe_cpufreq_driver); | 187 | return cpufreq_register_driver(&cbe_cpufreq_driver); |
361 | } | 188 | } |
362 | 189 | ||
363 | static void __exit cbe_cpufreq_exit(void) | 190 | static void __exit cbe_cpufreq_exit(void) |
364 | { | 191 | { |
365 | cpufreq_unregister_driver(&cbe_cpufreq_driver); | 192 | cpufreq_unregister_driver(&cbe_cpufreq_driver); |
366 | |||
367 | if (cbe_cpufreq_has_pmi) { | ||
368 | cpufreq_unregister_notifier(&pmi_notifier_block, CPUFREQ_POLICY_NOTIFIER); | ||
369 | pmi_unregister_handler(&cbe_pmi_handler); | ||
370 | } | ||
371 | } | 193 | } |
372 | 194 | ||
373 | module_init(cbe_cpufreq_init); | 195 | module_init(cbe_cpufreq_init); |
diff --git a/arch/powerpc/platforms/cell/cbe_cpufreq.h b/arch/powerpc/platforms/cell/cbe_cpufreq.h new file mode 100644 index 000000000000..c1d86bfa92ff --- /dev/null +++ b/arch/powerpc/platforms/cell/cbe_cpufreq.h | |||
@@ -0,0 +1,24 @@ | |||
1 | /* | ||
2 | * cbe_cpufreq.h | ||
3 | * | ||
4 | * This file contains the definitions used by the cbe_cpufreq driver. | ||
5 | * | ||
6 | * (C) Copyright IBM Deutschland Entwicklung GmbH 2005-2007 | ||
7 | * | ||
8 | * Author: Christian Krafft <krafft@de.ibm.com> | ||
9 | * | ||
10 | */ | ||
11 | |||
12 | #include <linux/cpufreq.h> | ||
13 | #include <linux/types.h> | ||
14 | |||
15 | int cbe_cpufreq_set_pmode(int cpu, unsigned int pmode); | ||
16 | int cbe_cpufreq_get_pmode(int cpu); | ||
17 | |||
18 | int cbe_cpufreq_set_pmode_pmi(int cpu, unsigned int pmode); | ||
19 | |||
20 | #if defined(CONFIG_CBE_CPUFREQ_PMI) || defined(CONFIG_CBE_CPUFREQ_PMI_MODULE) | ||
21 | extern bool cbe_cpufreq_has_pmi; | ||
22 | #else | ||
23 | #define cbe_cpufreq_has_pmi (0) | ||
24 | #endif | ||
diff --git a/arch/powerpc/platforms/cell/cbe_cpufreq_pervasive.c b/arch/powerpc/platforms/cell/cbe_cpufreq_pervasive.c new file mode 100644 index 000000000000..163263b3e1cd --- /dev/null +++ b/arch/powerpc/platforms/cell/cbe_cpufreq_pervasive.c | |||
@@ -0,0 +1,115 @@ | |||
1 | /* | ||
2 | * pervasive backend for the cbe_cpufreq driver | ||
3 | * | ||
4 | * This driver makes use of the pervasive unit to | ||
5 | * engage the desired frequency. | ||
6 | * | ||
7 | * (C) Copyright IBM Deutschland Entwicklung GmbH 2005-2007 | ||
8 | * | ||
9 | * Author: Christian Krafft <krafft@de.ibm.com> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2, or (at your option) | ||
14 | * any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | * GNU General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License | ||
22 | * along with this program; if not, write to the Free Software | ||
23 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
24 | */ | ||
25 | |||
26 | #include <linux/io.h> | ||
27 | #include <linux/kernel.h> | ||
28 | #include <linux/time.h> | ||
29 | #include <asm/machdep.h> | ||
30 | #include <asm/hw_irq.h> | ||
31 | |||
32 | #include "cbe_regs.h" | ||
33 | #include "cbe_cpufreq.h" | ||
34 | |||
35 | /* to write to MIC register */ | ||
36 | static u64 MIC_Slow_Fast_Timer_table[] = { | ||
37 | [0 ... 7] = 0x007fc00000000000ull, | ||
38 | }; | ||
39 | |||
40 | /* more values for the MIC */ | ||
41 | static u64 MIC_Slow_Next_Timer_table[] = { | ||
42 | 0x0000240000000000ull, | ||
43 | 0x0000268000000000ull, | ||
44 | 0x000029C000000000ull, | ||
45 | 0x00002D0000000000ull, | ||
46 | 0x0000300000000000ull, | ||
47 | 0x0000334000000000ull, | ||
48 | 0x000039C000000000ull, | ||
49 | 0x00003FC000000000ull, | ||
50 | }; | ||
51 | |||
52 | |||
53 | int cbe_cpufreq_set_pmode(int cpu, unsigned int pmode) | ||
54 | { | ||
55 | struct cbe_pmd_regs __iomem *pmd_regs; | ||
56 | struct cbe_mic_tm_regs __iomem *mic_tm_regs; | ||
57 | u64 flags; | ||
58 | u64 value; | ||
59 | #ifdef DEBUG | ||
60 | long time; | ||
61 | #endif | ||
62 | |||
63 | local_irq_save(flags); | ||
64 | |||
65 | mic_tm_regs = cbe_get_cpu_mic_tm_regs(cpu); | ||
66 | pmd_regs = cbe_get_cpu_pmd_regs(cpu); | ||
67 | |||
68 | #ifdef DEBUG | ||
69 | time = jiffies; | ||
70 | #endif | ||
71 | |||
72 | out_be64(&mic_tm_regs->slow_fast_timer_0, MIC_Slow_Fast_Timer_table[pmode]); | ||
73 | out_be64(&mic_tm_regs->slow_fast_timer_1, MIC_Slow_Fast_Timer_table[pmode]); | ||
74 | |||
75 | out_be64(&mic_tm_regs->slow_next_timer_0, MIC_Slow_Next_Timer_table[pmode]); | ||
76 | out_be64(&mic_tm_regs->slow_next_timer_1, MIC_Slow_Next_Timer_table[pmode]); | ||
77 | |||
78 | value = in_be64(&pmd_regs->pmcr); | ||
79 | /* set bits to zero */ | ||
80 | value &= 0xFFFFFFFFFFFFFFF8ull; | ||
81 | /* set bits to next pmode */ | ||
82 | value |= pmode; | ||
83 | |||
84 | out_be64(&pmd_regs->pmcr, value); | ||
85 | |||
86 | #ifdef DEBUG | ||
87 | /* wait until new pmode appears in status register */ | ||
88 | value = in_be64(&pmd_regs->pmsr) & 0x07; | ||
89 | while (value != pmode) { | ||
90 | cpu_relax(); | ||
91 | value = in_be64(&pmd_regs->pmsr) & 0x07; | ||
92 | } | ||
93 | |||
94 | time = jiffies - time; | ||
95 | time = jiffies_to_msecs(time); | ||
96 | pr_debug("had to wait %lu ms for a transition using " \ | ||
97 | "pervasive unit\n", time); | ||
98 | #endif | ||
99 | local_irq_restore(flags); | ||
100 | |||
101 | return 0; | ||
102 | } | ||
103 | |||
104 | |||
105 | int cbe_cpufreq_get_pmode(int cpu) | ||
106 | { | ||
107 | int ret; | ||
108 | struct cbe_pmd_regs __iomem *pmd_regs; | ||
109 | |||
110 | pmd_regs = cbe_get_cpu_pmd_regs(cpu); | ||
111 | ret = in_be64(&pmd_regs->pmsr) & 0x07; | ||
112 | |||
113 | return ret; | ||
114 | } | ||
115 | |||
diff --git a/arch/powerpc/platforms/cell/cbe_cpufreq_pmi.c b/arch/powerpc/platforms/cell/cbe_cpufreq_pmi.c new file mode 100644 index 000000000000..fc6f38982ff4 --- /dev/null +++ b/arch/powerpc/platforms/cell/cbe_cpufreq_pmi.c | |||
@@ -0,0 +1,148 @@ | |||
1 | /* | ||
2 | * pmi backend for the cbe_cpufreq driver | ||
3 | * | ||
4 | * (C) Copyright IBM Deutschland Entwicklung GmbH 2005-2007 | ||
5 | * | ||
6 | * Author: Christian Krafft <krafft@de.ibm.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2, or (at your option) | ||
11 | * any later version. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | * | ||
18 | * You should have received a copy of the GNU General Public License | ||
19 | * along with this program; if not, write to the Free Software | ||
20 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
21 | */ | ||
22 | |||
23 | #include <linux/kernel.h> | ||
24 | #include <linux/types.h> | ||
25 | #include <linux/timer.h> | ||
26 | #include <asm/of_platform.h> | ||
27 | #include <asm/processor.h> | ||
28 | #include <asm/prom.h> | ||
29 | #include <asm/pmi.h> | ||
30 | |||
31 | #ifdef DEBUG | ||
32 | #include <asm/time.h> | ||
33 | #endif | ||
34 | |||
35 | #include "cbe_regs.h" | ||
36 | #include "cbe_cpufreq.h" | ||
37 | |||
38 | static u8 pmi_slow_mode_limit[MAX_CBE]; | ||
39 | |||
40 | bool cbe_cpufreq_has_pmi = false; | ||
41 | EXPORT_SYMBOL_GPL(cbe_cpufreq_has_pmi); | ||
42 | |||
43 | /* | ||
44 | * hardware specific functions | ||
45 | */ | ||
46 | |||
47 | int cbe_cpufreq_set_pmode_pmi(int cpu, unsigned int pmode) | ||
48 | { | ||
49 | int ret; | ||
50 | pmi_message_t pmi_msg; | ||
51 | #ifdef DEBUG | ||
52 | long time; | ||
53 | #endif | ||
54 | pmi_msg.type = PMI_TYPE_FREQ_CHANGE; | ||
55 | pmi_msg.data1 = cbe_cpu_to_node(cpu); | ||
56 | pmi_msg.data2 = pmode; | ||
57 | |||
58 | #ifdef DEBUG | ||
59 | time = jiffies; | ||
60 | #endif | ||
61 | pmi_send_message(pmi_msg); | ||
62 | |||
63 | #ifdef DEBUG | ||
64 | time = jiffies - time; | ||
65 | time = jiffies_to_msecs(time); | ||
66 | pr_debug("had to wait %lu ms for a transition using " \ | ||
67 | "PMI\n", time); | ||
68 | #endif | ||
69 | ret = pmi_msg.data2; | ||
70 | pr_debug("PMI returned slow mode %d\n", ret); | ||
71 | |||
72 | return ret; | ||
73 | } | ||
74 | EXPORT_SYMBOL_GPL(cbe_cpufreq_set_pmode_pmi); | ||
75 | |||
76 | |||
77 | static void cbe_cpufreq_handle_pmi(pmi_message_t pmi_msg) | ||
78 | { | ||
79 | u8 node, slow_mode; | ||
80 | |||
81 | BUG_ON(pmi_msg.type != PMI_TYPE_FREQ_CHANGE); | ||
82 | |||
83 | node = pmi_msg.data1; | ||
84 | slow_mode = pmi_msg.data2; | ||
85 | |||
86 | pmi_slow_mode_limit[node] = slow_mode; | ||
87 | |||
88 | pr_debug("cbe_handle_pmi: node: %d max_freq: %d\n", node, slow_mode); | ||
89 | } | ||
90 | |||
91 | static int pmi_notifier(struct notifier_block *nb, | ||
92 | unsigned long event, void *data) | ||
93 | { | ||
94 | struct cpufreq_policy *policy = data; | ||
95 | struct cpufreq_frequency_table *cbe_freqs; | ||
96 | u8 node; | ||
97 | |||
98 | cbe_freqs = cpufreq_frequency_get_table(policy->cpu); | ||
99 | node = cbe_cpu_to_node(policy->cpu); | ||
100 | |||
101 | pr_debug("got notified, event=%lu, node=%u\n", event, node); | ||
102 | |||
103 | if (pmi_slow_mode_limit[node] != 0) { | ||
104 | pr_debug("limiting node %d to slow mode %d\n", | ||
105 | node, pmi_slow_mode_limit[node]); | ||
106 | |||
107 | cpufreq_verify_within_limits(policy, 0, | ||
108 | |||
109 | cbe_freqs[pmi_slow_mode_limit[node]].frequency); | ||
110 | } | ||
111 | |||
112 | return 0; | ||
113 | } | ||
114 | |||
115 | static struct notifier_block pmi_notifier_block = { | ||
116 | .notifier_call = pmi_notifier, | ||
117 | }; | ||
118 | |||
119 | static struct pmi_handler cbe_pmi_handler = { | ||
120 | .type = PMI_TYPE_FREQ_CHANGE, | ||
121 | .handle_pmi_message = cbe_cpufreq_handle_pmi, | ||
122 | }; | ||
123 | |||
124 | |||
125 | |||
126 | static int __init cbe_cpufreq_pmi_init(void) | ||
127 | { | ||
128 | cbe_cpufreq_has_pmi = pmi_register_handler(&cbe_pmi_handler) == 0; | ||
129 | |||
130 | if (!cbe_cpufreq_has_pmi) | ||
131 | return -ENODEV; | ||
132 | |||
133 | cpufreq_register_notifier(&pmi_notifier_block, CPUFREQ_POLICY_NOTIFIER); | ||
134 | |||
135 | return 0; | ||
136 | } | ||
137 | |||
138 | static void __exit cbe_cpufreq_pmi_exit(void) | ||
139 | { | ||
140 | cpufreq_unregister_notifier(&pmi_notifier_block, CPUFREQ_POLICY_NOTIFIER); | ||
141 | pmi_unregister_handler(&cbe_pmi_handler); | ||
142 | } | ||
143 | |||
144 | module_init(cbe_cpufreq_pmi_init); | ||
145 | module_exit(cbe_cpufreq_pmi_exit); | ||
146 | |||
147 | MODULE_LICENSE("GPL"); | ||
148 | MODULE_AUTHOR("Christian Krafft <krafft@de.ibm.com>"); | ||