diff options
Diffstat (limited to 'arch/powerpc/platforms')
30 files changed, 1823 insertions, 230 deletions
diff --git a/arch/powerpc/platforms/Makefile b/arch/powerpc/platforms/Makefile index e58fa953a50b..7ad2673d0aa5 100644 --- a/arch/powerpc/platforms/Makefile +++ b/arch/powerpc/platforms/Makefile | |||
@@ -5,6 +5,7 @@ ifeq ($(CONFIG_PPC64),y) | |||
5 | obj-$(CONFIG_PPC_PMAC) += powermac/ | 5 | obj-$(CONFIG_PPC_PMAC) += powermac/ |
6 | endif | 6 | endif |
7 | endif | 7 | endif |
8 | obj-$(CONFIG_PPC_EFIKA) += efika/ | ||
8 | obj-$(CONFIG_PPC_CHRP) += chrp/ | 9 | obj-$(CONFIG_PPC_CHRP) += chrp/ |
9 | obj-$(CONFIG_4xx) += 4xx/ | 10 | obj-$(CONFIG_4xx) += 4xx/ |
10 | obj-$(CONFIG_PPC_83xx) += 83xx/ | 11 | obj-$(CONFIG_PPC_83xx) += 83xx/ |
diff --git a/arch/powerpc/platforms/cell/Kconfig b/arch/powerpc/platforms/cell/Kconfig index 3e430b489bb7..06a85b704331 100644 --- a/arch/powerpc/platforms/cell/Kconfig +++ b/arch/powerpc/platforms/cell/Kconfig | |||
@@ -20,4 +20,18 @@ config CBE_RAS | |||
20 | bool "RAS features for bare metal Cell BE" | 20 | bool "RAS features for bare metal Cell BE" |
21 | default y | 21 | default y |
22 | 22 | ||
23 | config CBE_THERM | ||
24 | tristate "CBE thermal support" | ||
25 | default m | ||
26 | depends on CBE_RAS | ||
27 | |||
28 | config CBE_CPUFREQ | ||
29 | tristate "CBE frequency scaling" | ||
30 | depends on CBE_RAS && CPU_FREQ | ||
31 | default m | ||
32 | help | ||
33 | This adds the cpufreq driver for Cell BE processors. | ||
34 | For details, take a look at <file:Documentation/cpu-freq/>. | ||
35 | If you don't have such processor, say N | ||
36 | |||
23 | endmenu | 37 | endmenu |
diff --git a/arch/powerpc/platforms/cell/Makefile b/arch/powerpc/platforms/cell/Makefile index c89cdd67383b..0f31db7a50a3 100644 --- a/arch/powerpc/platforms/cell/Makefile +++ b/arch/powerpc/platforms/cell/Makefile | |||
@@ -1,7 +1,11 @@ | |||
1 | obj-$(CONFIG_PPC_CELL_NATIVE) += interrupt.o iommu.o setup.o \ | 1 | obj-$(CONFIG_PPC_CELL_NATIVE) += interrupt.o iommu.o setup.o \ |
2 | cbe_regs.o spider-pic.o pervasive.o | 2 | cbe_regs.o spider-pic.o pervasive.o \ |
3 | pmu.o | ||
3 | obj-$(CONFIG_CBE_RAS) += ras.o | 4 | obj-$(CONFIG_CBE_RAS) += ras.o |
4 | 5 | ||
6 | obj-$(CONFIG_CBE_THERM) += cbe_thermal.o | ||
7 | obj-$(CONFIG_CBE_CPUFREQ) += cbe_cpufreq.o | ||
8 | |||
5 | ifeq ($(CONFIG_SMP),y) | 9 | ifeq ($(CONFIG_SMP),y) |
6 | obj-$(CONFIG_PPC_CELL_NATIVE) += smp.o | 10 | obj-$(CONFIG_PPC_CELL_NATIVE) += smp.o |
7 | endif | 11 | endif |
diff --git a/arch/powerpc/platforms/cell/cbe_cpufreq.c b/arch/powerpc/platforms/cell/cbe_cpufreq.c new file mode 100644 index 000000000000..a3850fd1e94c --- /dev/null +++ b/arch/powerpc/platforms/cell/cbe_cpufreq.c | |||
@@ -0,0 +1,248 @@ | |||
1 | /* | ||
2 | * cpufreq driver for the cell processor | ||
3 | * | ||
4 | * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 | ||
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/cpufreq.h> | ||
24 | #include <linux/timer.h> | ||
25 | |||
26 | #include <asm/hw_irq.h> | ||
27 | #include <asm/io.h> | ||
28 | #include <asm/processor.h> | ||
29 | #include <asm/prom.h> | ||
30 | #include <asm/time.h> | ||
31 | |||
32 | #include "cbe_regs.h" | ||
33 | |||
34 | static DEFINE_MUTEX(cbe_switch_mutex); | ||
35 | |||
36 | |||
37 | /* the CBE supports an 8 step frequency scaling */ | ||
38 | static struct cpufreq_frequency_table cbe_freqs[] = { | ||
39 | {1, 0}, | ||
40 | {2, 0}, | ||
41 | {3, 0}, | ||
42 | {4, 0}, | ||
43 | {5, 0}, | ||
44 | {6, 0}, | ||
45 | {8, 0}, | ||
46 | {10, 0}, | ||
47 | {0, CPUFREQ_TABLE_END}, | ||
48 | }; | ||
49 | |||
50 | /* to write to MIC register */ | ||
51 | static u64 MIC_Slow_Fast_Timer_table[] = { | ||
52 | [0 ... 7] = 0x007fc00000000000ull, | ||
53 | }; | ||
54 | |||
55 | /* more values for the MIC */ | ||
56 | static u64 MIC_Slow_Next_Timer_table[] = { | ||
57 | 0x0000240000000000ull, | ||
58 | 0x0000268000000000ull, | ||
59 | 0x000029C000000000ull, | ||
60 | 0x00002D0000000000ull, | ||
61 | 0x0000300000000000ull, | ||
62 | 0x0000334000000000ull, | ||
63 | 0x000039C000000000ull, | ||
64 | 0x00003FC000000000ull, | ||
65 | }; | ||
66 | |||
67 | /* | ||
68 | * hardware specific functions | ||
69 | */ | ||
70 | |||
71 | static int get_pmode(int cpu) | ||
72 | { | ||
73 | int ret; | ||
74 | struct cbe_pmd_regs __iomem *pmd_regs; | ||
75 | |||
76 | pmd_regs = cbe_get_cpu_pmd_regs(cpu); | ||
77 | ret = in_be64(&pmd_regs->pmsr) & 0x07; | ||
78 | |||
79 | return ret; | ||
80 | } | ||
81 | |||
82 | static int set_pmode(int cpu, unsigned int pmode) | ||
83 | { | ||
84 | struct cbe_pmd_regs __iomem *pmd_regs; | ||
85 | struct cbe_mic_tm_regs __iomem *mic_tm_regs; | ||
86 | u64 flags; | ||
87 | u64 value; | ||
88 | |||
89 | local_irq_save(flags); | ||
90 | |||
91 | mic_tm_regs = cbe_get_cpu_mic_tm_regs(cpu); | ||
92 | pmd_regs = cbe_get_cpu_pmd_regs(cpu); | ||
93 | |||
94 | pr_debug("pm register is mapped at %p\n", &pmd_regs->pmcr); | ||
95 | pr_debug("mic register is mapped at %p\n", &mic_tm_regs->slow_fast_timer_0); | ||
96 | |||
97 | out_be64(&mic_tm_regs->slow_fast_timer_0, MIC_Slow_Fast_Timer_table[pmode]); | ||
98 | out_be64(&mic_tm_regs->slow_fast_timer_1, MIC_Slow_Fast_Timer_table[pmode]); | ||
99 | |||
100 | out_be64(&mic_tm_regs->slow_next_timer_0, MIC_Slow_Next_Timer_table[pmode]); | ||
101 | out_be64(&mic_tm_regs->slow_next_timer_1, MIC_Slow_Next_Timer_table[pmode]); | ||
102 | |||
103 | value = in_be64(&pmd_regs->pmcr); | ||
104 | /* set bits to zero */ | ||
105 | value &= 0xFFFFFFFFFFFFFFF8ull; | ||
106 | /* set bits to next pmode */ | ||
107 | value |= pmode; | ||
108 | |||
109 | out_be64(&pmd_regs->pmcr, value); | ||
110 | |||
111 | /* wait until new pmode appears in status register */ | ||
112 | value = in_be64(&pmd_regs->pmsr) & 0x07; | ||
113 | while(value != pmode) { | ||
114 | cpu_relax(); | ||
115 | value = in_be64(&pmd_regs->pmsr) & 0x07; | ||
116 | } | ||
117 | |||
118 | local_irq_restore(flags); | ||
119 | |||
120 | return 0; | ||
121 | } | ||
122 | |||
123 | /* | ||
124 | * cpufreq functions | ||
125 | */ | ||
126 | |||
127 | static int cbe_cpufreq_cpu_init (struct cpufreq_policy *policy) | ||
128 | { | ||
129 | u32 *max_freq; | ||
130 | int i, cur_pmode; | ||
131 | struct device_node *cpu; | ||
132 | |||
133 | cpu = of_get_cpu_node(policy->cpu, NULL); | ||
134 | |||
135 | if(!cpu) | ||
136 | return -ENODEV; | ||
137 | |||
138 | pr_debug("init cpufreq on CPU %d\n", policy->cpu); | ||
139 | |||
140 | max_freq = (u32*) get_property(cpu, "clock-frequency", NULL); | ||
141 | |||
142 | if(!max_freq) | ||
143 | return -EINVAL; | ||
144 | |||
145 | // we need the freq in kHz | ||
146 | *max_freq /= 1000; | ||
147 | |||
148 | pr_debug("max clock-frequency is at %u kHz\n", *max_freq); | ||
149 | pr_debug("initializing frequency table\n"); | ||
150 | |||
151 | // initialize frequency table | ||
152 | for (i=0; cbe_freqs[i].frequency!=CPUFREQ_TABLE_END; i++) { | ||
153 | cbe_freqs[i].frequency = *max_freq / cbe_freqs[i].index; | ||
154 | pr_debug("%d: %d\n", i, cbe_freqs[i].frequency); | ||
155 | } | ||
156 | |||
157 | policy->governor = CPUFREQ_DEFAULT_GOVERNOR; | ||
158 | /* if DEBUG is enabled set_pmode() measures the correct latency of a transition */ | ||
159 | policy->cpuinfo.transition_latency = 25000; | ||
160 | |||
161 | cur_pmode = get_pmode(policy->cpu); | ||
162 | pr_debug("current pmode is at %d\n",cur_pmode); | ||
163 | |||
164 | policy->cur = cbe_freqs[cur_pmode].frequency; | ||
165 | |||
166 | #ifdef CONFIG_SMP | ||
167 | policy->cpus = cpu_sibling_map[policy->cpu]; | ||
168 | #endif | ||
169 | |||
170 | cpufreq_frequency_table_get_attr (cbe_freqs, policy->cpu); | ||
171 | |||
172 | /* this ensures that policy->cpuinfo_min and policy->cpuinfo_max are set correctly */ | ||
173 | return cpufreq_frequency_table_cpuinfo (policy, cbe_freqs); | ||
174 | } | ||
175 | |||
176 | static int cbe_cpufreq_cpu_exit(struct cpufreq_policy *policy) | ||
177 | { | ||
178 | cpufreq_frequency_table_put_attr(policy->cpu); | ||
179 | return 0; | ||
180 | } | ||
181 | |||
182 | static int cbe_cpufreq_verify(struct cpufreq_policy *policy) | ||
183 | { | ||
184 | return cpufreq_frequency_table_verify(policy, cbe_freqs); | ||
185 | } | ||
186 | |||
187 | |||
188 | static int cbe_cpufreq_target(struct cpufreq_policy *policy, unsigned int target_freq, | ||
189 | unsigned int relation) | ||
190 | { | ||
191 | int rc; | ||
192 | struct cpufreq_freqs freqs; | ||
193 | int cbe_pmode_new; | ||
194 | |||
195 | cpufreq_frequency_table_target(policy, | ||
196 | cbe_freqs, | ||
197 | target_freq, | ||
198 | relation, | ||
199 | &cbe_pmode_new); | ||
200 | |||
201 | freqs.old = policy->cur; | ||
202 | freqs.new = cbe_freqs[cbe_pmode_new].frequency; | ||
203 | freqs.cpu = policy->cpu; | ||
204 | |||
205 | mutex_lock (&cbe_switch_mutex); | ||
206 | cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); | ||
207 | |||
208 | pr_debug("setting frequency for cpu %d to %d kHz, 1/%d of max frequency\n", | ||
209 | policy->cpu, | ||
210 | cbe_freqs[cbe_pmode_new].frequency, | ||
211 | cbe_freqs[cbe_pmode_new].index); | ||
212 | |||
213 | rc = set_pmode(policy->cpu, cbe_pmode_new); | ||
214 | cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); | ||
215 | mutex_unlock(&cbe_switch_mutex); | ||
216 | |||
217 | return rc; | ||
218 | } | ||
219 | |||
220 | static struct cpufreq_driver cbe_cpufreq_driver = { | ||
221 | .verify = cbe_cpufreq_verify, | ||
222 | .target = cbe_cpufreq_target, | ||
223 | .init = cbe_cpufreq_cpu_init, | ||
224 | .exit = cbe_cpufreq_cpu_exit, | ||
225 | .name = "cbe-cpufreq", | ||
226 | .owner = THIS_MODULE, | ||
227 | .flags = CPUFREQ_CONST_LOOPS, | ||
228 | }; | ||
229 | |||
230 | /* | ||
231 | * module init and destoy | ||
232 | */ | ||
233 | |||
234 | static int __init cbe_cpufreq_init(void) | ||
235 | { | ||
236 | return cpufreq_register_driver(&cbe_cpufreq_driver); | ||
237 | } | ||
238 | |||
239 | static void __exit cbe_cpufreq_exit(void) | ||
240 | { | ||
241 | cpufreq_unregister_driver(&cbe_cpufreq_driver); | ||
242 | } | ||
243 | |||
244 | module_init(cbe_cpufreq_init); | ||
245 | module_exit(cbe_cpufreq_exit); | ||
246 | |||
247 | MODULE_LICENSE("GPL"); | ||
248 | MODULE_AUTHOR("Christian Krafft <krafft@de.ibm.com>"); | ||
diff --git a/arch/powerpc/platforms/cell/cbe_regs.c b/arch/powerpc/platforms/cell/cbe_regs.c index 2f194ba29899..5a91b75c2f01 100644 --- a/arch/powerpc/platforms/cell/cbe_regs.c +++ b/arch/powerpc/platforms/cell/cbe_regs.c | |||
@@ -8,6 +8,7 @@ | |||
8 | 8 | ||
9 | #include <linux/percpu.h> | 9 | #include <linux/percpu.h> |
10 | #include <linux/types.h> | 10 | #include <linux/types.h> |
11 | #include <linux/module.h> | ||
11 | 12 | ||
12 | #include <asm/io.h> | 13 | #include <asm/io.h> |
13 | #include <asm/pgtable.h> | 14 | #include <asm/pgtable.h> |
@@ -16,8 +17,6 @@ | |||
16 | 17 | ||
17 | #include "cbe_regs.h" | 18 | #include "cbe_regs.h" |
18 | 19 | ||
19 | #define MAX_CBE 2 | ||
20 | |||
21 | /* | 20 | /* |
22 | * Current implementation uses "cpu" nodes. We build our own mapping | 21 | * Current implementation uses "cpu" nodes. We build our own mapping |
23 | * array of cpu numbers to cpu nodes locally for now to allow interrupt | 22 | * array of cpu numbers to cpu nodes locally for now to allow interrupt |
@@ -30,6 +29,8 @@ static struct cbe_regs_map | |||
30 | struct device_node *cpu_node; | 29 | struct device_node *cpu_node; |
31 | struct cbe_pmd_regs __iomem *pmd_regs; | 30 | struct cbe_pmd_regs __iomem *pmd_regs; |
32 | struct cbe_iic_regs __iomem *iic_regs; | 31 | struct cbe_iic_regs __iomem *iic_regs; |
32 | struct cbe_mic_tm_regs __iomem *mic_tm_regs; | ||
33 | struct cbe_pmd_shadow_regs pmd_shadow_regs; | ||
33 | } cbe_regs_maps[MAX_CBE]; | 34 | } cbe_regs_maps[MAX_CBE]; |
34 | static int cbe_regs_map_count; | 35 | static int cbe_regs_map_count; |
35 | 36 | ||
@@ -42,6 +43,19 @@ static struct cbe_thread_map | |||
42 | static struct cbe_regs_map *cbe_find_map(struct device_node *np) | 43 | static struct cbe_regs_map *cbe_find_map(struct device_node *np) |
43 | { | 44 | { |
44 | int i; | 45 | int i; |
46 | struct device_node *tmp_np; | ||
47 | |||
48 | if (strcasecmp(np->type, "spe") == 0) { | ||
49 | if (np->data == NULL) { | ||
50 | /* walk up path until cpu node was found */ | ||
51 | tmp_np = np->parent; | ||
52 | while (tmp_np != NULL && strcasecmp(tmp_np->type, "cpu") != 0) | ||
53 | tmp_np = tmp_np->parent; | ||
54 | |||
55 | np->data = cbe_find_map(tmp_np); | ||
56 | } | ||
57 | return np->data; | ||
58 | } | ||
45 | 59 | ||
46 | for (i = 0; i < cbe_regs_map_count; i++) | 60 | for (i = 0; i < cbe_regs_map_count; i++) |
47 | if (cbe_regs_maps[i].cpu_node == np) | 61 | if (cbe_regs_maps[i].cpu_node == np) |
@@ -56,6 +70,7 @@ struct cbe_pmd_regs __iomem *cbe_get_pmd_regs(struct device_node *np) | |||
56 | return NULL; | 70 | return NULL; |
57 | return map->pmd_regs; | 71 | return map->pmd_regs; |
58 | } | 72 | } |
73 | EXPORT_SYMBOL_GPL(cbe_get_pmd_regs); | ||
59 | 74 | ||
60 | struct cbe_pmd_regs __iomem *cbe_get_cpu_pmd_regs(int cpu) | 75 | struct cbe_pmd_regs __iomem *cbe_get_cpu_pmd_regs(int cpu) |
61 | { | 76 | { |
@@ -64,7 +79,23 @@ struct cbe_pmd_regs __iomem *cbe_get_cpu_pmd_regs(int cpu) | |||
64 | return NULL; | 79 | return NULL; |
65 | return map->pmd_regs; | 80 | return map->pmd_regs; |
66 | } | 81 | } |
82 | EXPORT_SYMBOL_GPL(cbe_get_cpu_pmd_regs); | ||
83 | |||
84 | struct cbe_pmd_shadow_regs *cbe_get_pmd_shadow_regs(struct device_node *np) | ||
85 | { | ||
86 | struct cbe_regs_map *map = cbe_find_map(np); | ||
87 | if (map == NULL) | ||
88 | return NULL; | ||
89 | return &map->pmd_shadow_regs; | ||
90 | } | ||
67 | 91 | ||
92 | struct cbe_pmd_shadow_regs *cbe_get_cpu_pmd_shadow_regs(int cpu) | ||
93 | { | ||
94 | struct cbe_regs_map *map = cbe_thread_map[cpu].regs; | ||
95 | if (map == NULL) | ||
96 | return NULL; | ||
97 | return &map->pmd_shadow_regs; | ||
98 | } | ||
68 | 99 | ||
69 | struct cbe_iic_regs __iomem *cbe_get_iic_regs(struct device_node *np) | 100 | struct cbe_iic_regs __iomem *cbe_get_iic_regs(struct device_node *np) |
70 | { | 101 | { |
@@ -73,6 +104,7 @@ struct cbe_iic_regs __iomem *cbe_get_iic_regs(struct device_node *np) | |||
73 | return NULL; | 104 | return NULL; |
74 | return map->iic_regs; | 105 | return map->iic_regs; |
75 | } | 106 | } |
107 | |||
76 | struct cbe_iic_regs __iomem *cbe_get_cpu_iic_regs(int cpu) | 108 | struct cbe_iic_regs __iomem *cbe_get_cpu_iic_regs(int cpu) |
77 | { | 109 | { |
78 | struct cbe_regs_map *map = cbe_thread_map[cpu].regs; | 110 | struct cbe_regs_map *map = cbe_thread_map[cpu].regs; |
@@ -81,6 +113,24 @@ struct cbe_iic_regs __iomem *cbe_get_cpu_iic_regs(int cpu) | |||
81 | return map->iic_regs; | 113 | return map->iic_regs; |
82 | } | 114 | } |
83 | 115 | ||
116 | struct cbe_mic_tm_regs __iomem *cbe_get_mic_tm_regs(struct device_node *np) | ||
117 | { | ||
118 | struct cbe_regs_map *map = cbe_find_map(np); | ||
119 | if (map == NULL) | ||
120 | return NULL; | ||
121 | return map->mic_tm_regs; | ||
122 | } | ||
123 | |||
124 | struct cbe_mic_tm_regs __iomem *cbe_get_cpu_mic_tm_regs(int cpu) | ||
125 | { | ||
126 | struct cbe_regs_map *map = cbe_thread_map[cpu].regs; | ||
127 | if (map == NULL) | ||
128 | return NULL; | ||
129 | return map->mic_tm_regs; | ||
130 | } | ||
131 | EXPORT_SYMBOL_GPL(cbe_get_cpu_mic_tm_regs); | ||
132 | |||
133 | |||
84 | void __init cbe_regs_init(void) | 134 | void __init cbe_regs_init(void) |
85 | { | 135 | { |
86 | int i; | 136 | int i; |
@@ -119,6 +169,11 @@ void __init cbe_regs_init(void) | |||
119 | prop = get_property(cpu, "iic", NULL); | 169 | prop = get_property(cpu, "iic", NULL); |
120 | if (prop != NULL) | 170 | if (prop != NULL) |
121 | map->iic_regs = ioremap(prop->address, prop->len); | 171 | map->iic_regs = ioremap(prop->address, prop->len); |
172 | |||
173 | prop = (struct address_prop *)get_property(cpu, "mic-tm", | ||
174 | NULL); | ||
175 | if (prop != NULL) | ||
176 | map->mic_tm_regs = ioremap(prop->address, prop->len); | ||
122 | } | 177 | } |
123 | } | 178 | } |
124 | 179 | ||
diff --git a/arch/powerpc/platforms/cell/cbe_regs.h b/arch/powerpc/platforms/cell/cbe_regs.h index e76e4a6af5bc..91083f51a0cb 100644 --- a/arch/powerpc/platforms/cell/cbe_regs.h +++ b/arch/powerpc/platforms/cell/cbe_regs.h | |||
@@ -4,6 +4,11 @@ | |||
4 | * This file is intended to hold the various register definitions for CBE | 4 | * This file is intended to hold the various register definitions for CBE |
5 | * on-chip system devices (memory controller, IO controller, etc...) | 5 | * on-chip system devices (memory controller, IO controller, etc...) |
6 | * | 6 | * |
7 | * (C) Copyright IBM Corporation 2001,2006 | ||
8 | * | ||
9 | * Authors: Maximino Aguilar (maguilar@us.ibm.com) | ||
10 | * David J. Erb (djerb@us.ibm.com) | ||
11 | * | ||
7 | * (c) 2006 Benjamin Herrenschmidt <benh@kernel.crashing.org>, IBM Corp. | 12 | * (c) 2006 Benjamin Herrenschmidt <benh@kernel.crashing.org>, IBM Corp. |
8 | */ | 13 | */ |
9 | 14 | ||
@@ -22,6 +27,7 @@ | |||
22 | #define HID0_CBE_THERM_INT_EN 0x0000000400000000ul | 27 | #define HID0_CBE_THERM_INT_EN 0x0000000400000000ul |
23 | #define HID0_CBE_SYSERR_INT_EN 0x0000000200000000ul | 28 | #define HID0_CBE_SYSERR_INT_EN 0x0000000200000000ul |
24 | 29 | ||
30 | #define MAX_CBE 2 | ||
25 | 31 | ||
26 | /* | 32 | /* |
27 | * | 33 | * |
@@ -29,51 +35,132 @@ | |||
29 | * | 35 | * |
30 | */ | 36 | */ |
31 | 37 | ||
38 | /* Macros for the pm_control register. */ | ||
39 | #define CBE_PM_16BIT_CTR(ctr) (1 << (24 - ((ctr) & (NR_PHYS_CTRS - 1)))) | ||
40 | #define CBE_PM_ENABLE_PERF_MON 0x80000000 | ||
41 | |||
42 | |||
43 | union spe_reg { | ||
44 | u64 val; | ||
45 | u8 spe[8]; | ||
46 | }; | ||
47 | |||
48 | union ppe_spe_reg { | ||
49 | u64 val; | ||
50 | struct { | ||
51 | u32 ppe; | ||
52 | u32 spe; | ||
53 | }; | ||
54 | }; | ||
55 | |||
56 | |||
32 | struct cbe_pmd_regs { | 57 | struct cbe_pmd_regs { |
33 | u8 pad_0x0000_0x0800[0x0800 - 0x0000]; /* 0x0000 */ | 58 | /* Debug Bus Control */ |
59 | u64 pad_0x0000; /* 0x0000 */ | ||
60 | |||
61 | u64 group_control; /* 0x0008 */ | ||
62 | |||
63 | u8 pad_0x0010_0x00a8 [0x00a8 - 0x0010]; /* 0x0010 */ | ||
64 | |||
65 | u64 debug_bus_control; /* 0x00a8 */ | ||
66 | |||
67 | u8 pad_0x00b0_0x0100 [0x0100 - 0x00b0]; /* 0x00b0 */ | ||
68 | |||
69 | u64 trace_aux_data; /* 0x0100 */ | ||
70 | u64 trace_buffer_0_63; /* 0x0108 */ | ||
71 | u64 trace_buffer_64_127; /* 0x0110 */ | ||
72 | u64 trace_address; /* 0x0118 */ | ||
73 | u64 ext_tr_timer; /* 0x0120 */ | ||
74 | |||
75 | u8 pad_0x0128_0x0400 [0x0400 - 0x0128]; /* 0x0128 */ | ||
76 | |||
77 | /* Performance Monitor */ | ||
78 | u64 pm_status; /* 0x0400 */ | ||
79 | u64 pm_control; /* 0x0408 */ | ||
80 | u64 pm_interval; /* 0x0410 */ | ||
81 | u64 pm_ctr[4]; /* 0x0418 */ | ||
82 | u64 pm_start_stop; /* 0x0438 */ | ||
83 | u64 pm07_control[8]; /* 0x0440 */ | ||
84 | |||
85 | u8 pad_0x0480_0x0800 [0x0800 - 0x0480]; /* 0x0480 */ | ||
34 | 86 | ||
35 | /* Thermal Sensor Registers */ | 87 | /* Thermal Sensor Registers */ |
36 | u64 ts_ctsr1; /* 0x0800 */ | 88 | union spe_reg ts_ctsr1; /* 0x0800 */ |
37 | u64 ts_ctsr2; /* 0x0808 */ | 89 | u64 ts_ctsr2; /* 0x0808 */ |
38 | u64 ts_mtsr1; /* 0x0810 */ | 90 | union spe_reg ts_mtsr1; /* 0x0810 */ |
39 | u64 ts_mtsr2; /* 0x0818 */ | 91 | u64 ts_mtsr2; /* 0x0818 */ |
40 | u64 ts_itr1; /* 0x0820 */ | 92 | union spe_reg ts_itr1; /* 0x0820 */ |
41 | u64 ts_itr2; /* 0x0828 */ | 93 | u64 ts_itr2; /* 0x0828 */ |
42 | u64 ts_gitr; /* 0x0830 */ | 94 | u64 ts_gitr; /* 0x0830 */ |
43 | u64 ts_isr; /* 0x0838 */ | 95 | u64 ts_isr; /* 0x0838 */ |
44 | u64 ts_imr; /* 0x0840 */ | 96 | u64 ts_imr; /* 0x0840 */ |
45 | u64 tm_cr1; /* 0x0848 */ | 97 | union spe_reg tm_cr1; /* 0x0848 */ |
46 | u64 tm_cr2; /* 0x0850 */ | 98 | u64 tm_cr2; /* 0x0850 */ |
47 | u64 tm_simr; /* 0x0858 */ | 99 | u64 tm_simr; /* 0x0858 */ |
48 | u64 tm_tpr; /* 0x0860 */ | 100 | union ppe_spe_reg tm_tpr; /* 0x0860 */ |
49 | u64 tm_str1; /* 0x0868 */ | 101 | union spe_reg tm_str1; /* 0x0868 */ |
50 | u64 tm_str2; /* 0x0870 */ | 102 | u64 tm_str2; /* 0x0870 */ |
51 | u64 tm_tsr; /* 0x0878 */ | 103 | union ppe_spe_reg tm_tsr; /* 0x0878 */ |
52 | 104 | ||
53 | /* Power Management */ | 105 | /* Power Management */ |
54 | u64 pm_control; /* 0x0880 */ | 106 | u64 pmcr; /* 0x0880 */ |
55 | #define CBE_PMD_PAUSE_ZERO_CONTROL 0x10000 | 107 | #define CBE_PMD_PAUSE_ZERO_CONTROL 0x10000 |
56 | u64 pm_status; /* 0x0888 */ | 108 | u64 pmsr; /* 0x0888 */ |
57 | 109 | ||
58 | /* Time Base Register */ | 110 | /* Time Base Register */ |
59 | u64 tbr; /* 0x0890 */ | 111 | u64 tbr; /* 0x0890 */ |
60 | 112 | ||
61 | u8 pad_0x0898_0x0c00 [0x0c00 - 0x0898]; /* 0x0898 */ | 113 | u8 pad_0x0898_0x0c00 [0x0c00 - 0x0898]; /* 0x0898 */ |
62 | 114 | ||
63 | /* Fault Isolation Registers */ | 115 | /* Fault Isolation Registers */ |
64 | u64 checkstop_fir; /* 0x0c00 */ | 116 | u64 checkstop_fir; /* 0x0c00 */ |
65 | u64 recoverable_fir; | 117 | u64 recoverable_fir; /* 0x0c08 */ |
66 | u64 spec_att_mchk_fir; | 118 | u64 spec_att_mchk_fir; /* 0x0c10 */ |
67 | u64 fir_mode_reg; | 119 | u64 fir_mode_reg; /* 0x0c18 */ |
68 | u64 fir_enable_mask; | 120 | u64 fir_enable_mask; /* 0x0c20 */ |
69 | 121 | ||
70 | u8 pad_0x0c28_0x1000 [0x1000 - 0x0c28]; /* 0x0c28 */ | 122 | u8 pad_0x0c28_0x1000 [0x1000 - 0x0c28]; /* 0x0c28 */ |
71 | }; | 123 | }; |
72 | 124 | ||
73 | extern struct cbe_pmd_regs __iomem *cbe_get_pmd_regs(struct device_node *np); | 125 | extern struct cbe_pmd_regs __iomem *cbe_get_pmd_regs(struct device_node *np); |
74 | extern struct cbe_pmd_regs __iomem *cbe_get_cpu_pmd_regs(int cpu); | 126 | extern struct cbe_pmd_regs __iomem *cbe_get_cpu_pmd_regs(int cpu); |
75 | 127 | ||
76 | /* | 128 | /* |
129 | * PMU shadow registers | ||
130 | * | ||
131 | * Many of the registers in the performance monitoring unit are write-only, | ||
132 | * so we need to save a copy of what we write to those registers. | ||
133 | * | ||
134 | * The actual data counters are read/write. However, writing to the counters | ||
135 | * only takes effect if the PMU is enabled. Otherwise the value is stored in | ||
136 | * a hardware latch until the next time the PMU is enabled. So we save a copy | ||
137 | * of the counter values if we need to read them back while the PMU is | ||
138 | * disabled. The counter_value_in_latch field is a bitmap indicating which | ||
139 | * counters currently have a value waiting to be written. | ||
140 | */ | ||
141 | |||
142 | #define NR_PHYS_CTRS 4 | ||
143 | #define NR_CTRS (NR_PHYS_CTRS * 2) | ||
144 | |||
145 | struct cbe_pmd_shadow_regs { | ||
146 | u32 group_control; | ||
147 | u32 debug_bus_control; | ||
148 | u32 trace_address; | ||
149 | u32 ext_tr_timer; | ||
150 | u32 pm_status; | ||
151 | u32 pm_control; | ||
152 | u32 pm_interval; | ||
153 | u32 pm_start_stop; | ||
154 | u32 pm07_control[NR_CTRS]; | ||
155 | |||
156 | u32 pm_ctr[NR_PHYS_CTRS]; | ||
157 | u32 counter_value_in_latch; | ||
158 | }; | ||
159 | |||
160 | extern struct cbe_pmd_shadow_regs *cbe_get_pmd_shadow_regs(struct device_node *np); | ||
161 | extern struct cbe_pmd_shadow_regs *cbe_get_cpu_pmd_shadow_regs(int cpu); | ||
162 | |||
163 | /* | ||
77 | * | 164 | * |
78 | * IIC unit register definitions | 165 | * IIC unit register definitions |
79 | * | 166 | * |
@@ -102,18 +189,20 @@ struct cbe_iic_regs { | |||
102 | 189 | ||
103 | /* IIC interrupt registers */ | 190 | /* IIC interrupt registers */ |
104 | struct cbe_iic_thread_regs thread[2]; /* 0x0400 */ | 191 | struct cbe_iic_thread_regs thread[2]; /* 0x0400 */ |
105 | u64 iic_ir; /* 0x0440 */ | 192 | |
106 | u64 iic_is; /* 0x0448 */ | 193 | u64 iic_ir; /* 0x0440 */ |
194 | u64 iic_is; /* 0x0448 */ | ||
195 | #define CBE_IIC_IS_PMI 0x2 | ||
107 | 196 | ||
108 | u8 pad_0x0450_0x0500[0x0500 - 0x0450]; /* 0x0450 */ | 197 | u8 pad_0x0450_0x0500[0x0500 - 0x0450]; /* 0x0450 */ |
109 | 198 | ||
110 | /* IOC FIR */ | 199 | /* IOC FIR */ |
111 | u64 ioc_fir_reset; /* 0x0500 */ | 200 | u64 ioc_fir_reset; /* 0x0500 */ |
112 | u64 ioc_fir_set; | 201 | u64 ioc_fir_set; /* 0x0508 */ |
113 | u64 ioc_checkstop_enable; | 202 | u64 ioc_checkstop_enable; /* 0x0510 */ |
114 | u64 ioc_fir_error_mask; | 203 | u64 ioc_fir_error_mask; /* 0x0518 */ |
115 | u64 ioc_syserr_enable; | 204 | u64 ioc_syserr_enable; /* 0x0520 */ |
116 | u64 ioc_fir; | 205 | u64 ioc_fir; /* 0x0528 */ |
117 | 206 | ||
118 | u8 pad_0x0530_0x1000[0x1000 - 0x0530]; /* 0x0530 */ | 207 | u8 pad_0x0530_0x1000[0x1000 - 0x0530]; /* 0x0530 */ |
119 | }; | 208 | }; |
@@ -122,6 +211,48 @@ extern struct cbe_iic_regs __iomem *cbe_get_iic_regs(struct device_node *np); | |||
122 | extern struct cbe_iic_regs __iomem *cbe_get_cpu_iic_regs(int cpu); | 211 | extern struct cbe_iic_regs __iomem *cbe_get_cpu_iic_regs(int cpu); |
123 | 212 | ||
124 | 213 | ||
214 | struct cbe_mic_tm_regs { | ||
215 | u8 pad_0x0000_0x0040[0x0040 - 0x0000]; /* 0x0000 */ | ||
216 | |||
217 | u64 mic_ctl_cnfg2; /* 0x0040 */ | ||
218 | #define CBE_MIC_ENABLE_AUX_TRC 0x8000000000000000LL | ||
219 | #define CBE_MIC_DISABLE_PWR_SAV_2 0x0200000000000000LL | ||
220 | #define CBE_MIC_DISABLE_AUX_TRC_WRAP 0x0100000000000000LL | ||
221 | #define CBE_MIC_ENABLE_AUX_TRC_INT 0x0080000000000000LL | ||
222 | |||
223 | u64 pad_0x0048; /* 0x0048 */ | ||
224 | |||
225 | u64 mic_aux_trc_base; /* 0x0050 */ | ||
226 | u64 mic_aux_trc_max_addr; /* 0x0058 */ | ||
227 | u64 mic_aux_trc_cur_addr; /* 0x0060 */ | ||
228 | u64 mic_aux_trc_grf_addr; /* 0x0068 */ | ||
229 | u64 mic_aux_trc_grf_data; /* 0x0070 */ | ||
230 | |||
231 | u64 pad_0x0078; /* 0x0078 */ | ||
232 | |||
233 | u64 mic_ctl_cnfg_0; /* 0x0080 */ | ||
234 | #define CBE_MIC_DISABLE_PWR_SAV_0 0x8000000000000000LL | ||
235 | |||
236 | u64 pad_0x0088; /* 0x0088 */ | ||
237 | |||
238 | u64 slow_fast_timer_0; /* 0x0090 */ | ||
239 | u64 slow_next_timer_0; /* 0x0098 */ | ||
240 | |||
241 | u8 pad_0x00a0_0x01c0[0x01c0 - 0x0a0]; /* 0x00a0 */ | ||
242 | |||
243 | u64 mic_ctl_cnfg_1; /* 0x01c0 */ | ||
244 | #define CBE_MIC_DISABLE_PWR_SAV_1 0x8000000000000000LL | ||
245 | u64 pad_0x01c8; /* 0x01c8 */ | ||
246 | |||
247 | u64 slow_fast_timer_1; /* 0x01d0 */ | ||
248 | u64 slow_next_timer_1; /* 0x01d8 */ | ||
249 | |||
250 | u8 pad_0x01e0_0x1000[0x1000 - 0x01e0]; /* 0x01e0 */ | ||
251 | }; | ||
252 | |||
253 | extern struct cbe_mic_tm_regs __iomem *cbe_get_mic_tm_regs(struct device_node *np); | ||
254 | extern struct cbe_mic_tm_regs __iomem *cbe_get_cpu_mic_tm_regs(int cpu); | ||
255 | |||
125 | /* Init this module early */ | 256 | /* Init this module early */ |
126 | extern void cbe_regs_init(void); | 257 | extern void cbe_regs_init(void); |
127 | 258 | ||
diff --git a/arch/powerpc/platforms/cell/cbe_thermal.c b/arch/powerpc/platforms/cell/cbe_thermal.c new file mode 100644 index 000000000000..17831a92d91e --- /dev/null +++ b/arch/powerpc/platforms/cell/cbe_thermal.c | |||
@@ -0,0 +1,225 @@ | |||
1 | /* | ||
2 | * thermal support for the cell processor | ||
3 | * | ||
4 | * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 | ||
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/module.h> | ||
24 | #include <linux/sysdev.h> | ||
25 | #include <linux/kernel.h> | ||
26 | #include <linux/cpu.h> | ||
27 | #include <asm/spu.h> | ||
28 | #include <asm/io.h> | ||
29 | #include <asm/prom.h> | ||
30 | |||
31 | #include "cbe_regs.h" | ||
32 | |||
33 | static struct cbe_pmd_regs __iomem *get_pmd_regs(struct sys_device *sysdev) | ||
34 | { | ||
35 | struct spu *spu; | ||
36 | |||
37 | spu = container_of(sysdev, struct spu, sysdev); | ||
38 | |||
39 | return cbe_get_pmd_regs(spu->devnode); | ||
40 | } | ||
41 | |||
42 | /* returns the value for a given spu in a given register */ | ||
43 | static u8 spu_read_register_value(struct sys_device *sysdev, union spe_reg __iomem *reg) | ||
44 | { | ||
45 | unsigned int *id; | ||
46 | union spe_reg value; | ||
47 | struct spu *spu; | ||
48 | |||
49 | /* getting the id from the reg attribute will not work on future device-tree layouts | ||
50 | * in future we should store the id to the spu struct and use it here */ | ||
51 | spu = container_of(sysdev, struct spu, sysdev); | ||
52 | id = (unsigned int *)get_property(spu->devnode, "reg", NULL); | ||
53 | value.val = in_be64(®->val); | ||
54 | |||
55 | return value.spe[*id]; | ||
56 | } | ||
57 | |||
58 | static ssize_t spu_show_temp(struct sys_device *sysdev, char *buf) | ||
59 | { | ||
60 | int value; | ||
61 | struct cbe_pmd_regs __iomem *pmd_regs; | ||
62 | |||
63 | pmd_regs = get_pmd_regs(sysdev); | ||
64 | |||
65 | value = spu_read_register_value(sysdev, &pmd_regs->ts_ctsr1); | ||
66 | /* clear all other bits */ | ||
67 | value &= 0x3F; | ||
68 | /* temp is stored in steps of 2 degrees */ | ||
69 | value *= 2; | ||
70 | /* base temp is 65 degrees */ | ||
71 | value += 65; | ||
72 | |||
73 | return sprintf(buf, "%d\n", (int) value); | ||
74 | } | ||
75 | |||
76 | static ssize_t ppe_show_temp(struct sys_device *sysdev, char *buf, int pos) | ||
77 | { | ||
78 | struct cbe_pmd_regs __iomem *pmd_regs; | ||
79 | u64 value; | ||
80 | |||
81 | pmd_regs = cbe_get_cpu_pmd_regs(sysdev->id); | ||
82 | value = in_be64(&pmd_regs->ts_ctsr2); | ||
83 | |||
84 | /* access the corresponding byte */ | ||
85 | value >>= pos; | ||
86 | /* clear all other bits */ | ||
87 | value &= 0x3F; | ||
88 | /* temp is stored in steps of 2 degrees */ | ||
89 | value *= 2; | ||
90 | /* base temp is 65 degrees */ | ||
91 | value += 65; | ||
92 | |||
93 | return sprintf(buf, "%d\n", (int) value); | ||
94 | } | ||
95 | |||
96 | |||
97 | /* shows the temperature of the DTS on the PPE, | ||
98 | * located near the linear thermal sensor */ | ||
99 | static ssize_t ppe_show_temp0(struct sys_device *sysdev, char *buf) | ||
100 | { | ||
101 | return ppe_show_temp(sysdev, buf, 32); | ||
102 | } | ||
103 | |||
104 | /* shows the temperature of the second DTS on the PPE */ | ||
105 | static ssize_t ppe_show_temp1(struct sys_device *sysdev, char *buf) | ||
106 | { | ||
107 | return ppe_show_temp(sysdev, buf, 0); | ||
108 | } | ||
109 | |||
110 | static struct sysdev_attribute attr_spu_temperature = { | ||
111 | .attr = {.name = "temperature", .mode = 0400 }, | ||
112 | .show = spu_show_temp, | ||
113 | }; | ||
114 | |||
115 | static struct attribute *spu_attributes[] = { | ||
116 | &attr_spu_temperature.attr, | ||
117 | }; | ||
118 | |||
119 | static struct attribute_group spu_attribute_group = { | ||
120 | .name = "thermal", | ||
121 | .attrs = spu_attributes, | ||
122 | }; | ||
123 | |||
124 | static struct sysdev_attribute attr_ppe_temperature0 = { | ||
125 | .attr = {.name = "temperature0", .mode = 0400 }, | ||
126 | .show = ppe_show_temp0, | ||
127 | }; | ||
128 | |||
129 | static struct sysdev_attribute attr_ppe_temperature1 = { | ||
130 | .attr = {.name = "temperature1", .mode = 0400 }, | ||
131 | .show = ppe_show_temp1, | ||
132 | }; | ||
133 | |||
134 | static struct attribute *ppe_attributes[] = { | ||
135 | &attr_ppe_temperature0.attr, | ||
136 | &attr_ppe_temperature1.attr, | ||
137 | }; | ||
138 | |||
139 | static struct attribute_group ppe_attribute_group = { | ||
140 | .name = "thermal", | ||
141 | .attrs = ppe_attributes, | ||
142 | }; | ||
143 | |||
144 | /* | ||
145 | * initialize throttling with default values | ||
146 | */ | ||
147 | static void __init init_default_values(void) | ||
148 | { | ||
149 | int cpu; | ||
150 | struct cbe_pmd_regs __iomem *pmd_regs; | ||
151 | struct sys_device *sysdev; | ||
152 | union ppe_spe_reg tpr; | ||
153 | union spe_reg str1; | ||
154 | u64 str2; | ||
155 | union spe_reg cr1; | ||
156 | u64 cr2; | ||
157 | |||
158 | /* TPR defaults */ | ||
159 | /* ppe | ||
160 | * 1F - no full stop | ||
161 | * 08 - dynamic throttling starts if over 80 degrees | ||
162 | * 03 - dynamic throttling ceases if below 70 degrees */ | ||
163 | tpr.ppe = 0x1F0803; | ||
164 | /* spe | ||
165 | * 10 - full stopped when over 96 degrees | ||
166 | * 08 - dynamic throttling starts if over 80 degrees | ||
167 | * 03 - dynamic throttling ceases if below 70 degrees | ||
168 | */ | ||
169 | tpr.spe = 0x100803; | ||
170 | |||
171 | /* STR defaults */ | ||
172 | /* str1 | ||
173 | * 10 - stop 16 of 32 cycles | ||
174 | */ | ||
175 | str1.val = 0x1010101010101010ull; | ||
176 | /* str2 | ||
177 | * 10 - stop 16 of 32 cycles | ||
178 | */ | ||
179 | str2 = 0x10; | ||
180 | |||
181 | /* CR defaults */ | ||
182 | /* cr1 | ||
183 | * 4 - normal operation | ||
184 | */ | ||
185 | cr1.val = 0x0404040404040404ull; | ||
186 | /* cr2 | ||
187 | * 4 - normal operation | ||
188 | */ | ||
189 | cr2 = 0x04; | ||
190 | |||
191 | for_each_possible_cpu (cpu) { | ||
192 | pr_debug("processing cpu %d\n", cpu); | ||
193 | sysdev = get_cpu_sysdev(cpu); | ||
194 | pmd_regs = cbe_get_cpu_pmd_regs(sysdev->id); | ||
195 | |||
196 | out_be64(&pmd_regs->tm_str2, str2); | ||
197 | out_be64(&pmd_regs->tm_str1.val, str1.val); | ||
198 | out_be64(&pmd_regs->tm_tpr.val, tpr.val); | ||
199 | out_be64(&pmd_regs->tm_cr1.val, cr1.val); | ||
200 | out_be64(&pmd_regs->tm_cr2, cr2); | ||
201 | } | ||
202 | } | ||
203 | |||
204 | |||
205 | static int __init thermal_init(void) | ||
206 | { | ||
207 | init_default_values(); | ||
208 | |||
209 | spu_add_sysdev_attr_group(&spu_attribute_group); | ||
210 | cpu_add_sysdev_attr_group(&ppe_attribute_group); | ||
211 | |||
212 | return 0; | ||
213 | } | ||
214 | module_init(thermal_init); | ||
215 | |||
216 | static void __exit thermal_exit(void) | ||
217 | { | ||
218 | spu_remove_sysdev_attr_group(&spu_attribute_group); | ||
219 | cpu_remove_sysdev_attr_group(&ppe_attribute_group); | ||
220 | } | ||
221 | module_exit(thermal_exit); | ||
222 | |||
223 | MODULE_LICENSE("GPL"); | ||
224 | MODULE_AUTHOR("Christian Krafft <krafft@de.ibm.com>"); | ||
225 | |||
diff --git a/arch/powerpc/platforms/cell/pervasive.c b/arch/powerpc/platforms/cell/pervasive.c index 9f2e4ed20a57..c68fabdc7874 100644 --- a/arch/powerpc/platforms/cell/pervasive.c +++ b/arch/powerpc/platforms/cell/pervasive.c | |||
@@ -38,32 +38,16 @@ | |||
38 | #include "pervasive.h" | 38 | #include "pervasive.h" |
39 | #include "cbe_regs.h" | 39 | #include "cbe_regs.h" |
40 | 40 | ||
41 | static DEFINE_SPINLOCK(cbe_pervasive_lock); | 41 | static void cbe_power_save(void) |
42 | |||
43 | static void __init cbe_enable_pause_zero(void) | ||
44 | { | 42 | { |
45 | unsigned long thread_switch_control; | 43 | unsigned long ctrl, thread_switch_control; |
46 | unsigned long temp_register; | 44 | ctrl = mfspr(SPRN_CTRLF); |
47 | struct cbe_pmd_regs __iomem *pregs; | ||
48 | |||
49 | spin_lock_irq(&cbe_pervasive_lock); | ||
50 | pregs = cbe_get_cpu_pmd_regs(smp_processor_id()); | ||
51 | if (pregs == NULL) | ||
52 | goto out; | ||
53 | |||
54 | pr_debug("Power Management: CPU %d\n", smp_processor_id()); | ||
55 | |||
56 | /* Enable Pause(0) control bit */ | ||
57 | temp_register = in_be64(&pregs->pm_control); | ||
58 | |||
59 | out_be64(&pregs->pm_control, | ||
60 | temp_register | CBE_PMD_PAUSE_ZERO_CONTROL); | ||
61 | 45 | ||
62 | /* Enable DEC and EE interrupt request */ | 46 | /* Enable DEC and EE interrupt request */ |
63 | thread_switch_control = mfspr(SPRN_TSC_CELL); | 47 | thread_switch_control = mfspr(SPRN_TSC_CELL); |
64 | thread_switch_control |= TSC_CELL_EE_ENABLE | TSC_CELL_EE_BOOST; | 48 | thread_switch_control |= TSC_CELL_EE_ENABLE | TSC_CELL_EE_BOOST; |
65 | 49 | ||
66 | switch ((mfspr(SPRN_CTRLF) & CTRL_CT)) { | 50 | switch (ctrl & CTRL_CT) { |
67 | case CTRL_CT0: | 51 | case CTRL_CT0: |
68 | thread_switch_control |= TSC_CELL_DEC_ENABLE_0; | 52 | thread_switch_control |= TSC_CELL_DEC_ENABLE_0; |
69 | break; | 53 | break; |
@@ -75,58 +59,21 @@ static void __init cbe_enable_pause_zero(void) | |||
75 | __FUNCTION__); | 59 | __FUNCTION__); |
76 | break; | 60 | break; |
77 | } | 61 | } |
78 | |||
79 | mtspr(SPRN_TSC_CELL, thread_switch_control); | 62 | mtspr(SPRN_TSC_CELL, thread_switch_control); |
80 | 63 | ||
81 | out: | 64 | /* |
82 | spin_unlock_irq(&cbe_pervasive_lock); | 65 | * go into low thread priority, medium priority will be |
83 | } | 66 | * restored for us after wake-up. |
84 | |||
85 | static void cbe_idle(void) | ||
86 | { | ||
87 | unsigned long ctrl; | ||
88 | |||
89 | /* Why do we do that on every idle ? Couldn't that be done once for | ||
90 | * all or do we lose the state some way ? Also, the pm_control | ||
91 | * register setting, that can't be set once at boot ? We really want | ||
92 | * to move that away in order to implement a simple powersave | ||
93 | */ | 67 | */ |
94 | cbe_enable_pause_zero(); | 68 | HMT_low(); |
95 | |||
96 | while (1) { | ||
97 | if (!need_resched()) { | ||
98 | local_irq_disable(); | ||
99 | while (!need_resched()) { | ||
100 | /* go into low thread priority */ | ||
101 | HMT_low(); | ||
102 | |||
103 | /* | ||
104 | * atomically disable thread execution | ||
105 | * and runlatch. | ||
106 | * External and Decrementer exceptions | ||
107 | * are still handled when the thread | ||
108 | * is disabled but now enter in | ||
109 | * cbe_system_reset_exception() | ||
110 | */ | ||
111 | ctrl = mfspr(SPRN_CTRLF); | ||
112 | ctrl &= ~(CTRL_RUNLATCH | CTRL_TE); | ||
113 | mtspr(SPRN_CTRLT, ctrl); | ||
114 | } | ||
115 | /* restore thread prio */ | ||
116 | HMT_medium(); | ||
117 | local_irq_enable(); | ||
118 | } | ||
119 | 69 | ||
120 | /* | 70 | /* |
121 | * turn runlatch on again before scheduling the | 71 | * atomically disable thread execution and runlatch. |
122 | * process we just woke up | 72 | * External and Decrementer exceptions are still handled when the |
123 | */ | 73 | * thread is disabled but now enter in cbe_system_reset_exception() |
124 | ppc64_runlatch_on(); | 74 | */ |
125 | 75 | ctrl &= ~(CTRL_RUNLATCH | CTRL_TE); | |
126 | preempt_enable_no_resched(); | 76 | mtspr(SPRN_CTRLT, ctrl); |
127 | schedule(); | ||
128 | preempt_disable(); | ||
129 | } | ||
130 | } | 77 | } |
131 | 78 | ||
132 | static int cbe_system_reset_exception(struct pt_regs *regs) | 79 | static int cbe_system_reset_exception(struct pt_regs *regs) |
@@ -158,9 +105,20 @@ static int cbe_system_reset_exception(struct pt_regs *regs) | |||
158 | 105 | ||
159 | void __init cbe_pervasive_init(void) | 106 | void __init cbe_pervasive_init(void) |
160 | { | 107 | { |
108 | int cpu; | ||
161 | if (!cpu_has_feature(CPU_FTR_PAUSE_ZERO)) | 109 | if (!cpu_has_feature(CPU_FTR_PAUSE_ZERO)) |
162 | return; | 110 | return; |
163 | 111 | ||
164 | ppc_md.idle_loop = cbe_idle; | 112 | for_each_possible_cpu(cpu) { |
113 | struct cbe_pmd_regs __iomem *regs = cbe_get_cpu_pmd_regs(cpu); | ||
114 | if (!regs) | ||
115 | continue; | ||
116 | |||
117 | /* Enable Pause(0) control bit */ | ||
118 | out_be64(®s->pmcr, in_be64(®s->pmcr) | | ||
119 | CBE_PMD_PAUSE_ZERO_CONTROL); | ||
120 | } | ||
121 | |||
122 | ppc_md.power_save = cbe_power_save; | ||
165 | ppc_md.system_reset_exception = cbe_system_reset_exception; | 123 | ppc_md.system_reset_exception = cbe_system_reset_exception; |
166 | } | 124 | } |
diff --git a/arch/powerpc/platforms/cell/pmu.c b/arch/powerpc/platforms/cell/pmu.c new file mode 100644 index 000000000000..30d17ce236a7 --- /dev/null +++ b/arch/powerpc/platforms/cell/pmu.c | |||
@@ -0,0 +1,328 @@ | |||
1 | /* | ||
2 | * Cell Broadband Engine Performance Monitor | ||
3 | * | ||
4 | * (C) Copyright IBM Corporation 2001,2006 | ||
5 | * | ||
6 | * Author: | ||
7 | * David Erb (djerb@us.ibm.com) | ||
8 | * Kevin Corry (kevcorry@us.ibm.com) | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2, or (at your option) | ||
13 | * any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
23 | */ | ||
24 | |||
25 | #include <linux/types.h> | ||
26 | #include <asm/io.h> | ||
27 | #include <asm/machdep.h> | ||
28 | #include <asm/reg.h> | ||
29 | #include <asm/spu.h> | ||
30 | |||
31 | #include "cbe_regs.h" | ||
32 | #include "interrupt.h" | ||
33 | #include "pmu.h" | ||
34 | |||
35 | /* | ||
36 | * When writing to write-only mmio addresses, save a shadow copy. All of the | ||
37 | * registers are 32-bit, but stored in the upper-half of a 64-bit field in | ||
38 | * pmd_regs. | ||
39 | */ | ||
40 | |||
41 | #define WRITE_WO_MMIO(reg, x) \ | ||
42 | do { \ | ||
43 | u32 _x = (x); \ | ||
44 | struct cbe_pmd_regs __iomem *pmd_regs; \ | ||
45 | struct cbe_pmd_shadow_regs *shadow_regs; \ | ||
46 | pmd_regs = cbe_get_cpu_pmd_regs(cpu); \ | ||
47 | shadow_regs = cbe_get_cpu_pmd_shadow_regs(cpu); \ | ||
48 | out_be64(&(pmd_regs->reg), (((u64)_x) << 32)); \ | ||
49 | shadow_regs->reg = _x; \ | ||
50 | } while (0) | ||
51 | |||
52 | #define READ_SHADOW_REG(val, reg) \ | ||
53 | do { \ | ||
54 | struct cbe_pmd_shadow_regs *shadow_regs; \ | ||
55 | shadow_regs = cbe_get_cpu_pmd_shadow_regs(cpu); \ | ||
56 | (val) = shadow_regs->reg; \ | ||
57 | } while (0) | ||
58 | |||
59 | #define READ_MMIO_UPPER32(val, reg) \ | ||
60 | do { \ | ||
61 | struct cbe_pmd_regs __iomem *pmd_regs; \ | ||
62 | pmd_regs = cbe_get_cpu_pmd_regs(cpu); \ | ||
63 | (val) = (u32)(in_be64(&pmd_regs->reg) >> 32); \ | ||
64 | } while (0) | ||
65 | |||
66 | /* | ||
67 | * Physical counter registers. | ||
68 | * Each physical counter can act as one 32-bit counter or two 16-bit counters. | ||
69 | */ | ||
70 | |||
71 | u32 cbe_read_phys_ctr(u32 cpu, u32 phys_ctr) | ||
72 | { | ||
73 | u32 val_in_latch, val = 0; | ||
74 | |||
75 | if (phys_ctr < NR_PHYS_CTRS) { | ||
76 | READ_SHADOW_REG(val_in_latch, counter_value_in_latch); | ||
77 | |||
78 | /* Read the latch or the actual counter, whichever is newer. */ | ||
79 | if (val_in_latch & (1 << phys_ctr)) { | ||
80 | READ_SHADOW_REG(val, pm_ctr[phys_ctr]); | ||
81 | } else { | ||
82 | READ_MMIO_UPPER32(val, pm_ctr[phys_ctr]); | ||
83 | } | ||
84 | } | ||
85 | |||
86 | return val; | ||
87 | } | ||
88 | |||
89 | void cbe_write_phys_ctr(u32 cpu, u32 phys_ctr, u32 val) | ||
90 | { | ||
91 | struct cbe_pmd_shadow_regs *shadow_regs; | ||
92 | u32 pm_ctrl; | ||
93 | |||
94 | if (phys_ctr < NR_PHYS_CTRS) { | ||
95 | /* Writing to a counter only writes to a hardware latch. | ||
96 | * The new value is not propagated to the actual counter | ||
97 | * until the performance monitor is enabled. | ||
98 | */ | ||
99 | WRITE_WO_MMIO(pm_ctr[phys_ctr], val); | ||
100 | |||
101 | pm_ctrl = cbe_read_pm(cpu, pm_control); | ||
102 | if (pm_ctrl & CBE_PM_ENABLE_PERF_MON) { | ||
103 | /* The counters are already active, so we need to | ||
104 | * rewrite the pm_control register to "re-enable" | ||
105 | * the PMU. | ||
106 | */ | ||
107 | cbe_write_pm(cpu, pm_control, pm_ctrl); | ||
108 | } else { | ||
109 | shadow_regs = cbe_get_cpu_pmd_shadow_regs(cpu); | ||
110 | shadow_regs->counter_value_in_latch |= (1 << phys_ctr); | ||
111 | } | ||
112 | } | ||
113 | } | ||
114 | |||
115 | /* | ||
116 | * "Logical" counter registers. | ||
117 | * These will read/write 16-bits or 32-bits depending on the | ||
118 | * current size of the counter. Counters 4 - 7 are always 16-bit. | ||
119 | */ | ||
120 | |||
121 | u32 cbe_read_ctr(u32 cpu, u32 ctr) | ||
122 | { | ||
123 | u32 val; | ||
124 | u32 phys_ctr = ctr & (NR_PHYS_CTRS - 1); | ||
125 | |||
126 | val = cbe_read_phys_ctr(cpu, phys_ctr); | ||
127 | |||
128 | if (cbe_get_ctr_size(cpu, phys_ctr) == 16) | ||
129 | val = (ctr < NR_PHYS_CTRS) ? (val >> 16) : (val & 0xffff); | ||
130 | |||
131 | return val; | ||
132 | } | ||
133 | |||
134 | void cbe_write_ctr(u32 cpu, u32 ctr, u32 val) | ||
135 | { | ||
136 | u32 phys_ctr; | ||
137 | u32 phys_val; | ||
138 | |||
139 | phys_ctr = ctr & (NR_PHYS_CTRS - 1); | ||
140 | |||
141 | if (cbe_get_ctr_size(cpu, phys_ctr) == 16) { | ||
142 | phys_val = cbe_read_phys_ctr(cpu, phys_ctr); | ||
143 | |||
144 | if (ctr < NR_PHYS_CTRS) | ||
145 | val = (val << 16) | (phys_val & 0xffff); | ||
146 | else | ||
147 | val = (val & 0xffff) | (phys_val & 0xffff0000); | ||
148 | } | ||
149 | |||
150 | cbe_write_phys_ctr(cpu, phys_ctr, val); | ||
151 | } | ||
152 | |||
153 | /* | ||
154 | * Counter-control registers. | ||
155 | * Each "logical" counter has a corresponding control register. | ||
156 | */ | ||
157 | |||
158 | u32 cbe_read_pm07_control(u32 cpu, u32 ctr) | ||
159 | { | ||
160 | u32 pm07_control = 0; | ||
161 | |||
162 | if (ctr < NR_CTRS) | ||
163 | READ_SHADOW_REG(pm07_control, pm07_control[ctr]); | ||
164 | |||
165 | return pm07_control; | ||
166 | } | ||
167 | |||
168 | void cbe_write_pm07_control(u32 cpu, u32 ctr, u32 val) | ||
169 | { | ||
170 | if (ctr < NR_CTRS) | ||
171 | WRITE_WO_MMIO(pm07_control[ctr], val); | ||
172 | } | ||
173 | |||
174 | /* | ||
175 | * Other PMU control registers. Most of these are write-only. | ||
176 | */ | ||
177 | |||
178 | u32 cbe_read_pm(u32 cpu, enum pm_reg_name reg) | ||
179 | { | ||
180 | u32 val = 0; | ||
181 | |||
182 | switch (reg) { | ||
183 | case group_control: | ||
184 | READ_SHADOW_REG(val, group_control); | ||
185 | break; | ||
186 | |||
187 | case debug_bus_control: | ||
188 | READ_SHADOW_REG(val, debug_bus_control); | ||
189 | break; | ||
190 | |||
191 | case trace_address: | ||
192 | READ_MMIO_UPPER32(val, trace_address); | ||
193 | break; | ||
194 | |||
195 | case ext_tr_timer: | ||
196 | READ_SHADOW_REG(val, ext_tr_timer); | ||
197 | break; | ||
198 | |||
199 | case pm_status: | ||
200 | READ_MMIO_UPPER32(val, pm_status); | ||
201 | break; | ||
202 | |||
203 | case pm_control: | ||
204 | READ_SHADOW_REG(val, pm_control); | ||
205 | break; | ||
206 | |||
207 | case pm_interval: | ||
208 | READ_SHADOW_REG(val, pm_interval); | ||
209 | break; | ||
210 | |||
211 | case pm_start_stop: | ||
212 | READ_SHADOW_REG(val, pm_start_stop); | ||
213 | break; | ||
214 | } | ||
215 | |||
216 | return val; | ||
217 | } | ||
218 | |||
219 | void cbe_write_pm(u32 cpu, enum pm_reg_name reg, u32 val) | ||
220 | { | ||
221 | switch (reg) { | ||
222 | case group_control: | ||
223 | WRITE_WO_MMIO(group_control, val); | ||
224 | break; | ||
225 | |||
226 | case debug_bus_control: | ||
227 | WRITE_WO_MMIO(debug_bus_control, val); | ||
228 | break; | ||
229 | |||
230 | case trace_address: | ||
231 | WRITE_WO_MMIO(trace_address, val); | ||
232 | break; | ||
233 | |||
234 | case ext_tr_timer: | ||
235 | WRITE_WO_MMIO(ext_tr_timer, val); | ||
236 | break; | ||
237 | |||
238 | case pm_status: | ||
239 | WRITE_WO_MMIO(pm_status, val); | ||
240 | break; | ||
241 | |||
242 | case pm_control: | ||
243 | WRITE_WO_MMIO(pm_control, val); | ||
244 | break; | ||
245 | |||
246 | case pm_interval: | ||
247 | WRITE_WO_MMIO(pm_interval, val); | ||
248 | break; | ||
249 | |||
250 | case pm_start_stop: | ||
251 | WRITE_WO_MMIO(pm_start_stop, val); | ||
252 | break; | ||
253 | } | ||
254 | } | ||
255 | |||
256 | /* | ||
257 | * Get/set the size of a physical counter to either 16 or 32 bits. | ||
258 | */ | ||
259 | |||
260 | u32 cbe_get_ctr_size(u32 cpu, u32 phys_ctr) | ||
261 | { | ||
262 | u32 pm_ctrl, size = 0; | ||
263 | |||
264 | if (phys_ctr < NR_PHYS_CTRS) { | ||
265 | pm_ctrl = cbe_read_pm(cpu, pm_control); | ||
266 | size = (pm_ctrl & CBE_PM_16BIT_CTR(phys_ctr)) ? 16 : 32; | ||
267 | } | ||
268 | |||
269 | return size; | ||
270 | } | ||
271 | |||
272 | void cbe_set_ctr_size(u32 cpu, u32 phys_ctr, u32 ctr_size) | ||
273 | { | ||
274 | u32 pm_ctrl; | ||
275 | |||
276 | if (phys_ctr < NR_PHYS_CTRS) { | ||
277 | pm_ctrl = cbe_read_pm(cpu, pm_control); | ||
278 | switch (ctr_size) { | ||
279 | case 16: | ||
280 | pm_ctrl |= CBE_PM_16BIT_CTR(phys_ctr); | ||
281 | break; | ||
282 | |||
283 | case 32: | ||
284 | pm_ctrl &= ~CBE_PM_16BIT_CTR(phys_ctr); | ||
285 | break; | ||
286 | } | ||
287 | cbe_write_pm(cpu, pm_control, pm_ctrl); | ||
288 | } | ||
289 | } | ||
290 | |||
291 | /* | ||
292 | * Enable/disable the entire performance monitoring unit. | ||
293 | * When we enable the PMU, all pending writes to counters get committed. | ||
294 | */ | ||
295 | |||
296 | void cbe_enable_pm(u32 cpu) | ||
297 | { | ||
298 | struct cbe_pmd_shadow_regs *shadow_regs; | ||
299 | u32 pm_ctrl; | ||
300 | |||
301 | shadow_regs = cbe_get_cpu_pmd_shadow_regs(cpu); | ||
302 | shadow_regs->counter_value_in_latch = 0; | ||
303 | |||
304 | pm_ctrl = cbe_read_pm(cpu, pm_control) | CBE_PM_ENABLE_PERF_MON; | ||
305 | cbe_write_pm(cpu, pm_control, pm_ctrl); | ||
306 | } | ||
307 | |||
308 | void cbe_disable_pm(u32 cpu) | ||
309 | { | ||
310 | u32 pm_ctrl; | ||
311 | pm_ctrl = cbe_read_pm(cpu, pm_control) & ~CBE_PM_ENABLE_PERF_MON; | ||
312 | cbe_write_pm(cpu, pm_control, pm_ctrl); | ||
313 | } | ||
314 | |||
315 | /* | ||
316 | * Reading from the trace_buffer. | ||
317 | * The trace buffer is two 64-bit registers. Reading from | ||
318 | * the second half automatically increments the trace_address. | ||
319 | */ | ||
320 | |||
321 | void cbe_read_trace_buffer(u32 cpu, u64 *buf) | ||
322 | { | ||
323 | struct cbe_pmd_regs __iomem *pmd_regs = cbe_get_cpu_pmd_regs(cpu); | ||
324 | |||
325 | *buf++ = in_be64(&pmd_regs->trace_buffer_0_63); | ||
326 | *buf++ = in_be64(&pmd_regs->trace_buffer_64_127); | ||
327 | } | ||
328 | |||
diff --git a/arch/powerpc/platforms/cell/pmu.h b/arch/powerpc/platforms/cell/pmu.h new file mode 100644 index 000000000000..eb1e8e0af910 --- /dev/null +++ b/arch/powerpc/platforms/cell/pmu.h | |||
@@ -0,0 +1,57 @@ | |||
1 | /* | ||
2 | * Cell Broadband Engine Performance Monitor | ||
3 | * | ||
4 | * (C) Copyright IBM Corporation 2001,2006 | ||
5 | * | ||
6 | * Author: | ||
7 | * David Erb (djerb@us.ibm.com) | ||
8 | * Kevin Corry (kevcorry@us.ibm.com) | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2, or (at your option) | ||
13 | * any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
23 | */ | ||
24 | |||
25 | #ifndef __PERFMON_H__ | ||
26 | #define __PERFMON_H__ | ||
27 | |||
28 | enum pm_reg_name { | ||
29 | group_control, | ||
30 | debug_bus_control, | ||
31 | trace_address, | ||
32 | ext_tr_timer, | ||
33 | pm_status, | ||
34 | pm_control, | ||
35 | pm_interval, | ||
36 | pm_start_stop, | ||
37 | }; | ||
38 | |||
39 | extern u32 cbe_read_phys_ctr(u32 cpu, u32 phys_ctr); | ||
40 | extern void cbe_write_phys_ctr(u32 cpu, u32 phys_ctr, u32 val); | ||
41 | extern u32 cbe_read_ctr(u32 cpu, u32 ctr); | ||
42 | extern void cbe_write_ctr(u32 cpu, u32 ctr, u32 val); | ||
43 | |||
44 | extern u32 cbe_read_pm07_control(u32 cpu, u32 ctr); | ||
45 | extern void cbe_write_pm07_control(u32 cpu, u32 ctr, u32 val); | ||
46 | extern u32 cbe_read_pm (u32 cpu, enum pm_reg_name reg); | ||
47 | extern void cbe_write_pm (u32 cpu, enum pm_reg_name reg, u32 val); | ||
48 | |||
49 | extern u32 cbe_get_ctr_size(u32 cpu, u32 phys_ctr); | ||
50 | extern void cbe_set_ctr_size(u32 cpu, u32 phys_ctr, u32 ctr_size); | ||
51 | |||
52 | extern void cbe_enable_pm(u32 cpu); | ||
53 | extern void cbe_disable_pm(u32 cpu); | ||
54 | |||
55 | extern void cbe_read_trace_buffer(u32 cpu, u64 *buf); | ||
56 | |||
57 | #endif | ||
diff --git a/arch/powerpc/platforms/cell/spu_base.c b/arch/powerpc/platforms/cell/spu_base.c index 7aa809d5a244..d5aeb3c6dd45 100644 --- a/arch/powerpc/platforms/cell/spu_base.c +++ b/arch/powerpc/platforms/cell/spu_base.c | |||
@@ -38,6 +38,7 @@ | |||
38 | #include <asm/spu.h> | 38 | #include <asm/spu.h> |
39 | #include <asm/spu_priv1.h> | 39 | #include <asm/spu_priv1.h> |
40 | #include <asm/mmu_context.h> | 40 | #include <asm/mmu_context.h> |
41 | #include <asm/xmon.h> | ||
41 | 42 | ||
42 | #include "interrupt.h" | 43 | #include "interrupt.h" |
43 | 44 | ||
@@ -89,7 +90,30 @@ static int __spu_trap_data_seg(struct spu *spu, unsigned long ea) | |||
89 | printk("%s: invalid access during switch!\n", __func__); | 90 | printk("%s: invalid access during switch!\n", __func__); |
90 | return 1; | 91 | return 1; |
91 | } | 92 | } |
92 | if (!mm || (REGION_ID(ea) != USER_REGION_ID)) { | 93 | esid = (ea & ESID_MASK) | SLB_ESID_V; |
94 | |||
95 | switch(REGION_ID(ea)) { | ||
96 | case USER_REGION_ID: | ||
97 | #ifdef CONFIG_HUGETLB_PAGE | ||
98 | if (in_hugepage_area(mm->context, ea)) | ||
99 | llp = mmu_psize_defs[mmu_huge_psize].sllp; | ||
100 | else | ||
101 | #endif | ||
102 | llp = mmu_psize_defs[mmu_virtual_psize].sllp; | ||
103 | vsid = (get_vsid(mm->context.id, ea) << SLB_VSID_SHIFT) | | ||
104 | SLB_VSID_USER | llp; | ||
105 | break; | ||
106 | case VMALLOC_REGION_ID: | ||
107 | llp = mmu_psize_defs[mmu_virtual_psize].sllp; | ||
108 | vsid = (get_kernel_vsid(ea) << SLB_VSID_SHIFT) | | ||
109 | SLB_VSID_KERNEL | llp; | ||
110 | break; | ||
111 | case KERNEL_REGION_ID: | ||
112 | llp = mmu_psize_defs[mmu_linear_psize].sllp; | ||
113 | vsid = (get_kernel_vsid(ea) << SLB_VSID_SHIFT) | | ||
114 | SLB_VSID_KERNEL | llp; | ||
115 | break; | ||
116 | default: | ||
93 | /* Future: support kernel segments so that drivers | 117 | /* Future: support kernel segments so that drivers |
94 | * can use SPUs. | 118 | * can use SPUs. |
95 | */ | 119 | */ |
@@ -97,16 +121,6 @@ static int __spu_trap_data_seg(struct spu *spu, unsigned long ea) | |||
97 | return 1; | 121 | return 1; |
98 | } | 122 | } |
99 | 123 | ||
100 | esid = (ea & ESID_MASK) | SLB_ESID_V; | ||
101 | #ifdef CONFIG_HUGETLB_PAGE | ||
102 | if (in_hugepage_area(mm->context, ea)) | ||
103 | llp = mmu_psize_defs[mmu_huge_psize].sllp; | ||
104 | else | ||
105 | #endif | ||
106 | llp = mmu_psize_defs[mmu_virtual_psize].sllp; | ||
107 | vsid = (get_vsid(mm->context.id, ea) << SLB_VSID_SHIFT) | | ||
108 | SLB_VSID_USER | llp; | ||
109 | |||
110 | out_be64(&priv2->slb_index_W, spu->slb_replace); | 124 | out_be64(&priv2->slb_index_W, spu->slb_replace); |
111 | out_be64(&priv2->slb_vsid_RW, vsid); | 125 | out_be64(&priv2->slb_vsid_RW, vsid); |
112 | out_be64(&priv2->slb_esid_RW, esid); | 126 | out_be64(&priv2->slb_esid_RW, esid); |
@@ -320,6 +334,7 @@ static void spu_free_irqs(struct spu *spu) | |||
320 | } | 334 | } |
321 | 335 | ||
322 | static struct list_head spu_list[MAX_NUMNODES]; | 336 | static struct list_head spu_list[MAX_NUMNODES]; |
337 | static LIST_HEAD(spu_full_list); | ||
323 | static DEFINE_MUTEX(spu_mutex); | 338 | static DEFINE_MUTEX(spu_mutex); |
324 | 339 | ||
325 | static void spu_init_channels(struct spu *spu) | 340 | static void spu_init_channels(struct spu *spu) |
@@ -364,8 +379,7 @@ struct spu *spu_alloc_node(int node) | |||
364 | if (!list_empty(&spu_list[node])) { | 379 | if (!list_empty(&spu_list[node])) { |
365 | spu = list_entry(spu_list[node].next, struct spu, list); | 380 | spu = list_entry(spu_list[node].next, struct spu, list); |
366 | list_del_init(&spu->list); | 381 | list_del_init(&spu->list); |
367 | pr_debug("Got SPU %x %d %d\n", | 382 | pr_debug("Got SPU %d %d\n", spu->number, spu->node); |
368 | spu->isrc, spu->number, spu->node); | ||
369 | spu_init_channels(spu); | 383 | spu_init_channels(spu); |
370 | } | 384 | } |
371 | mutex_unlock(&spu_mutex); | 385 | mutex_unlock(&spu_mutex); |
@@ -591,7 +605,6 @@ static int __init spu_map_interrupts_old(struct spu *spu, struct device_node *np | |||
591 | 605 | ||
592 | /* Add the node number */ | 606 | /* Add the node number */ |
593 | isrc |= spu->node << IIC_IRQ_NODE_SHIFT; | 607 | isrc |= spu->node << IIC_IRQ_NODE_SHIFT; |
594 | spu->isrc = isrc; | ||
595 | 608 | ||
596 | /* Now map interrupts of all 3 classes */ | 609 | /* Now map interrupts of all 3 classes */ |
597 | spu->irqs[0] = irq_create_mapping(NULL, IIC_IRQ_CLASS_0 | isrc); | 610 | spu->irqs[0] = irq_create_mapping(NULL, IIC_IRQ_CLASS_0 | isrc); |
@@ -758,15 +771,56 @@ struct sysdev_class spu_sysdev_class = { | |||
758 | set_kset_name("spu") | 771 | set_kset_name("spu") |
759 | }; | 772 | }; |
760 | 773 | ||
761 | static ssize_t spu_show_isrc(struct sys_device *sysdev, char *buf) | 774 | int spu_add_sysdev_attr(struct sysdev_attribute *attr) |
762 | { | 775 | { |
763 | struct spu *spu = container_of(sysdev, struct spu, sysdev); | 776 | struct spu *spu; |
764 | return sprintf(buf, "%d\n", spu->isrc); | 777 | mutex_lock(&spu_mutex); |
778 | |||
779 | list_for_each_entry(spu, &spu_full_list, full_list) | ||
780 | sysdev_create_file(&spu->sysdev, attr); | ||
765 | 781 | ||
782 | mutex_unlock(&spu_mutex); | ||
783 | return 0; | ||
766 | } | 784 | } |
767 | static SYSDEV_ATTR(isrc, 0400, spu_show_isrc, NULL); | 785 | EXPORT_SYMBOL_GPL(spu_add_sysdev_attr); |
786 | |||
787 | int spu_add_sysdev_attr_group(struct attribute_group *attrs) | ||
788 | { | ||
789 | struct spu *spu; | ||
790 | mutex_lock(&spu_mutex); | ||
768 | 791 | ||
769 | extern int attach_sysdev_to_node(struct sys_device *dev, int nid); | 792 | list_for_each_entry(spu, &spu_full_list, full_list) |
793 | sysfs_create_group(&spu->sysdev.kobj, attrs); | ||
794 | |||
795 | mutex_unlock(&spu_mutex); | ||
796 | return 0; | ||
797 | } | ||
798 | EXPORT_SYMBOL_GPL(spu_add_sysdev_attr_group); | ||
799 | |||
800 | |||
801 | void spu_remove_sysdev_attr(struct sysdev_attribute *attr) | ||
802 | { | ||
803 | struct spu *spu; | ||
804 | mutex_lock(&spu_mutex); | ||
805 | |||
806 | list_for_each_entry(spu, &spu_full_list, full_list) | ||
807 | sysdev_remove_file(&spu->sysdev, attr); | ||
808 | |||
809 | mutex_unlock(&spu_mutex); | ||
810 | } | ||
811 | EXPORT_SYMBOL_GPL(spu_remove_sysdev_attr); | ||
812 | |||
813 | void spu_remove_sysdev_attr_group(struct attribute_group *attrs) | ||
814 | { | ||
815 | struct spu *spu; | ||
816 | mutex_lock(&spu_mutex); | ||
817 | |||
818 | list_for_each_entry(spu, &spu_full_list, full_list) | ||
819 | sysfs_remove_group(&spu->sysdev.kobj, attrs); | ||
820 | |||
821 | mutex_unlock(&spu_mutex); | ||
822 | } | ||
823 | EXPORT_SYMBOL_GPL(spu_remove_sysdev_attr_group); | ||
770 | 824 | ||
771 | static int spu_create_sysdev(struct spu *spu) | 825 | static int spu_create_sysdev(struct spu *spu) |
772 | { | 826 | { |
@@ -781,8 +835,6 @@ static int spu_create_sysdev(struct spu *spu) | |||
781 | return ret; | 835 | return ret; |
782 | } | 836 | } |
783 | 837 | ||
784 | if (spu->isrc != 0) | ||
785 | sysdev_create_file(&spu->sysdev, &attr_isrc); | ||
786 | sysfs_add_device_to_node(&spu->sysdev, spu->nid); | 838 | sysfs_add_device_to_node(&spu->sysdev, spu->nid); |
787 | 839 | ||
788 | return 0; | 840 | return 0; |
@@ -790,7 +842,6 @@ static int spu_create_sysdev(struct spu *spu) | |||
790 | 842 | ||
791 | static void spu_destroy_sysdev(struct spu *spu) | 843 | static void spu_destroy_sysdev(struct spu *spu) |
792 | { | 844 | { |
793 | sysdev_remove_file(&spu->sysdev, &attr_isrc); | ||
794 | sysfs_remove_device_from_node(&spu->sysdev, spu->nid); | 845 | sysfs_remove_device_from_node(&spu->sysdev, spu->nid); |
795 | sysdev_unregister(&spu->sysdev); | 846 | sysdev_unregister(&spu->sysdev); |
796 | } | 847 | } |
@@ -830,7 +881,7 @@ static int __init create_spu(struct device_node *spe) | |||
830 | if (ret) | 881 | if (ret) |
831 | goto out_unmap; | 882 | goto out_unmap; |
832 | spin_lock_init(&spu->register_lock); | 883 | spin_lock_init(&spu->register_lock); |
833 | spu_mfc_sdr_set(spu, mfspr(SPRN_SDR1)); | 884 | spu_mfc_sdr_setup(spu); |
834 | spu_mfc_sr1_set(spu, 0x33); | 885 | spu_mfc_sr1_set(spu, 0x33); |
835 | mutex_lock(&spu_mutex); | 886 | mutex_lock(&spu_mutex); |
836 | 887 | ||
@@ -844,10 +895,13 @@ static int __init create_spu(struct device_node *spe) | |||
844 | goto out_free_irqs; | 895 | goto out_free_irqs; |
845 | 896 | ||
846 | list_add(&spu->list, &spu_list[spu->node]); | 897 | list_add(&spu->list, &spu_list[spu->node]); |
898 | list_add(&spu->full_list, &spu_full_list); | ||
899 | spu->devnode = of_node_get(spe); | ||
900 | |||
847 | mutex_unlock(&spu_mutex); | 901 | mutex_unlock(&spu_mutex); |
848 | 902 | ||
849 | pr_debug(KERN_DEBUG "Using SPE %s %02x %p %p %p %p %d\n", | 903 | pr_debug(KERN_DEBUG "Using SPE %s %p %p %p %p %d\n", |
850 | spu->name, spu->isrc, spu->local_store, | 904 | spu->name, spu->local_store, |
851 | spu->problem, spu->priv1, spu->priv2, spu->number); | 905 | spu->problem, spu->priv1, spu->priv2, spu->number); |
852 | goto out; | 906 | goto out; |
853 | 907 | ||
@@ -866,6 +920,9 @@ out: | |||
866 | static void destroy_spu(struct spu *spu) | 920 | static void destroy_spu(struct spu *spu) |
867 | { | 921 | { |
868 | list_del_init(&spu->list); | 922 | list_del_init(&spu->list); |
923 | list_del_init(&spu->full_list); | ||
924 | |||
925 | of_node_put(spu->devnode); | ||
869 | 926 | ||
870 | spu_destroy_sysdev(spu); | 927 | spu_destroy_sysdev(spu); |
871 | spu_free_irqs(spu); | 928 | spu_free_irqs(spu); |
@@ -912,6 +969,9 @@ static int __init init_spu_base(void) | |||
912 | break; | 969 | break; |
913 | } | 970 | } |
914 | } | 971 | } |
972 | |||
973 | xmon_register_spus(&spu_full_list); | ||
974 | |||
915 | return ret; | 975 | return ret; |
916 | } | 976 | } |
917 | module_init(init_spu_base); | 977 | module_init(init_spu_base); |
diff --git a/arch/powerpc/platforms/cell/spu_priv1_mmio.c b/arch/powerpc/platforms/cell/spu_priv1_mmio.c index 71b69f0a1a48..90011f9aab3f 100644 --- a/arch/powerpc/platforms/cell/spu_priv1_mmio.c +++ b/arch/powerpc/platforms/cell/spu_priv1_mmio.c | |||
@@ -84,9 +84,9 @@ static void mfc_dsisr_set(struct spu *spu, u64 dsisr) | |||
84 | out_be64(&spu->priv1->mfc_dsisr_RW, dsisr); | 84 | out_be64(&spu->priv1->mfc_dsisr_RW, dsisr); |
85 | } | 85 | } |
86 | 86 | ||
87 | static void mfc_sdr_set(struct spu *spu, u64 sdr) | 87 | static void mfc_sdr_setup(struct spu *spu) |
88 | { | 88 | { |
89 | out_be64(&spu->priv1->mfc_sdr_RW, sdr); | 89 | out_be64(&spu->priv1->mfc_sdr_RW, mfspr(SPRN_SDR1)); |
90 | } | 90 | } |
91 | 91 | ||
92 | static void mfc_sr1_set(struct spu *spu, u64 sr1) | 92 | static void mfc_sr1_set(struct spu *spu, u64 sr1) |
@@ -146,7 +146,7 @@ const struct spu_priv1_ops spu_priv1_mmio_ops = | |||
146 | .mfc_dar_get = mfc_dar_get, | 146 | .mfc_dar_get = mfc_dar_get, |
147 | .mfc_dsisr_get = mfc_dsisr_get, | 147 | .mfc_dsisr_get = mfc_dsisr_get, |
148 | .mfc_dsisr_set = mfc_dsisr_set, | 148 | .mfc_dsisr_set = mfc_dsisr_set, |
149 | .mfc_sdr_set = mfc_sdr_set, | 149 | .mfc_sdr_setup = mfc_sdr_setup, |
150 | .mfc_sr1_set = mfc_sr1_set, | 150 | .mfc_sr1_set = mfc_sr1_set, |
151 | .mfc_sr1_get = mfc_sr1_get, | 151 | .mfc_sr1_get = mfc_sr1_get, |
152 | .mfc_tclass_id_set = mfc_tclass_id_set, | 152 | .mfc_tclass_id_set = mfc_tclass_id_set, |
diff --git a/arch/powerpc/platforms/cell/spufs/context.c b/arch/powerpc/platforms/cell/spufs/context.c index 034cf6af53a2..48eb050bcf4b 100644 --- a/arch/powerpc/platforms/cell/spufs/context.c +++ b/arch/powerpc/platforms/cell/spufs/context.c | |||
@@ -120,6 +120,33 @@ void spu_unmap_mappings(struct spu_context *ctx) | |||
120 | unmap_mapping_range(ctx->signal2, 0, 0x4000, 1); | 120 | unmap_mapping_range(ctx->signal2, 0, 0x4000, 1); |
121 | } | 121 | } |
122 | 122 | ||
123 | int spu_acquire_exclusive(struct spu_context *ctx) | ||
124 | { | ||
125 | int ret = 0; | ||
126 | |||
127 | down_write(&ctx->state_sema); | ||
128 | /* ctx is about to be freed, can't acquire any more */ | ||
129 | if (!ctx->owner) { | ||
130 | ret = -EINVAL; | ||
131 | goto out; | ||
132 | } | ||
133 | |||
134 | if (ctx->state == SPU_STATE_SAVED) { | ||
135 | ret = spu_activate(ctx, 0); | ||
136 | if (ret) | ||
137 | goto out; | ||
138 | ctx->state = SPU_STATE_RUNNABLE; | ||
139 | } else { | ||
140 | /* We need to exclude userspace access to the context. */ | ||
141 | spu_unmap_mappings(ctx); | ||
142 | } | ||
143 | |||
144 | out: | ||
145 | if (ret) | ||
146 | up_write(&ctx->state_sema); | ||
147 | return ret; | ||
148 | } | ||
149 | |||
123 | int spu_acquire_runnable(struct spu_context *ctx) | 150 | int spu_acquire_runnable(struct spu_context *ctx) |
124 | { | 151 | { |
125 | int ret = 0; | 152 | int ret = 0; |
diff --git a/arch/powerpc/platforms/cell/spufs/file.c b/arch/powerpc/platforms/cell/spufs/file.c index 533e2723e184..0ea2361865a2 100644 --- a/arch/powerpc/platforms/cell/spufs/file.c +++ b/arch/powerpc/platforms/cell/spufs/file.c | |||
@@ -1343,6 +1343,37 @@ static struct file_operations spufs_mfc_fops = { | |||
1343 | .mmap = spufs_mfc_mmap, | 1343 | .mmap = spufs_mfc_mmap, |
1344 | }; | 1344 | }; |
1345 | 1345 | ||
1346 | |||
1347 | static int spufs_recycle_open(struct inode *inode, struct file *file) | ||
1348 | { | ||
1349 | file->private_data = SPUFS_I(inode)->i_ctx; | ||
1350 | return nonseekable_open(inode, file); | ||
1351 | } | ||
1352 | |||
1353 | static ssize_t spufs_recycle_write(struct file *file, | ||
1354 | const char __user *buffer, size_t size, loff_t *pos) | ||
1355 | { | ||
1356 | struct spu_context *ctx = file->private_data; | ||
1357 | int ret; | ||
1358 | |||
1359 | if (!(ctx->flags & SPU_CREATE_ISOLATE)) | ||
1360 | return -EINVAL; | ||
1361 | |||
1362 | if (size < 1) | ||
1363 | return -EINVAL; | ||
1364 | |||
1365 | ret = spu_recycle_isolated(ctx); | ||
1366 | |||
1367 | if (ret) | ||
1368 | return ret; | ||
1369 | return size; | ||
1370 | } | ||
1371 | |||
1372 | static struct file_operations spufs_recycle_fops = { | ||
1373 | .open = spufs_recycle_open, | ||
1374 | .write = spufs_recycle_write, | ||
1375 | }; | ||
1376 | |||
1346 | static void spufs_npc_set(void *data, u64 val) | 1377 | static void spufs_npc_set(void *data, u64 val) |
1347 | { | 1378 | { |
1348 | struct spu_context *ctx = data; | 1379 | struct spu_context *ctx = data; |
@@ -1531,3 +1562,26 @@ struct tree_descr spufs_dir_contents[] = { | |||
1531 | { "object-id", &spufs_object_id_ops, 0666, }, | 1562 | { "object-id", &spufs_object_id_ops, 0666, }, |
1532 | {}, | 1563 | {}, |
1533 | }; | 1564 | }; |
1565 | |||
1566 | struct tree_descr spufs_dir_nosched_contents[] = { | ||
1567 | { "mem", &spufs_mem_fops, 0666, }, | ||
1568 | { "mbox", &spufs_mbox_fops, 0444, }, | ||
1569 | { "ibox", &spufs_ibox_fops, 0444, }, | ||
1570 | { "wbox", &spufs_wbox_fops, 0222, }, | ||
1571 | { "mbox_stat", &spufs_mbox_stat_fops, 0444, }, | ||
1572 | { "ibox_stat", &spufs_ibox_stat_fops, 0444, }, | ||
1573 | { "wbox_stat", &spufs_wbox_stat_fops, 0444, }, | ||
1574 | { "signal1", &spufs_signal1_fops, 0666, }, | ||
1575 | { "signal2", &spufs_signal2_fops, 0666, }, | ||
1576 | { "signal1_type", &spufs_signal1_type, 0666, }, | ||
1577 | { "signal2_type", &spufs_signal2_type, 0666, }, | ||
1578 | { "mss", &spufs_mss_fops, 0666, }, | ||
1579 | { "mfc", &spufs_mfc_fops, 0666, }, | ||
1580 | { "cntl", &spufs_cntl_fops, 0666, }, | ||
1581 | { "npc", &spufs_npc_ops, 0666, }, | ||
1582 | { "psmap", &spufs_psmap_fops, 0666, }, | ||
1583 | { "phys-id", &spufs_id_ops, 0666, }, | ||
1584 | { "object-id", &spufs_object_id_ops, 0666, }, | ||
1585 | { "recycle", &spufs_recycle_fops, 0222, }, | ||
1586 | {}, | ||
1587 | }; | ||
diff --git a/arch/powerpc/platforms/cell/spufs/hw_ops.c b/arch/powerpc/platforms/cell/spufs/hw_ops.c index d805ffed892d..59c87f12da5a 100644 --- a/arch/powerpc/platforms/cell/spufs/hw_ops.c +++ b/arch/powerpc/platforms/cell/spufs/hw_ops.c | |||
@@ -219,8 +219,11 @@ static char *spu_hw_get_ls(struct spu_context *ctx) | |||
219 | 219 | ||
220 | static void spu_hw_runcntl_write(struct spu_context *ctx, u32 val) | 220 | static void spu_hw_runcntl_write(struct spu_context *ctx, u32 val) |
221 | { | 221 | { |
222 | eieio(); | 222 | spin_lock_irq(&ctx->spu->register_lock); |
223 | if (val & SPU_RUNCNTL_ISOLATE) | ||
224 | out_be64(&ctx->spu->priv2->spu_privcntl_RW, 4LL); | ||
223 | out_be32(&ctx->spu->problem->spu_runcntl_RW, val); | 225 | out_be32(&ctx->spu->problem->spu_runcntl_RW, val); |
226 | spin_unlock_irq(&ctx->spu->register_lock); | ||
224 | } | 227 | } |
225 | 228 | ||
226 | static void spu_hw_runcntl_stop(struct spu_context *ctx) | 229 | static void spu_hw_runcntl_stop(struct spu_context *ctx) |
diff --git a/arch/powerpc/platforms/cell/spufs/inode.c b/arch/powerpc/platforms/cell/spufs/inode.c index 427d00a4f6a0..9e457be140ef 100644 --- a/arch/powerpc/platforms/cell/spufs/inode.c +++ b/arch/powerpc/platforms/cell/spufs/inode.c | |||
@@ -33,6 +33,8 @@ | |||
33 | #include <linux/slab.h> | 33 | #include <linux/slab.h> |
34 | #include <linux/parser.h> | 34 | #include <linux/parser.h> |
35 | 35 | ||
36 | #include <asm/prom.h> | ||
37 | #include <asm/spu_priv1.h> | ||
36 | #include <asm/io.h> | 38 | #include <asm/io.h> |
37 | #include <asm/semaphore.h> | 39 | #include <asm/semaphore.h> |
38 | #include <asm/spu.h> | 40 | #include <asm/spu.h> |
@@ -41,6 +43,7 @@ | |||
41 | #include "spufs.h" | 43 | #include "spufs.h" |
42 | 44 | ||
43 | static kmem_cache_t *spufs_inode_cache; | 45 | static kmem_cache_t *spufs_inode_cache; |
46 | static char *isolated_loader; | ||
44 | 47 | ||
45 | static struct inode * | 48 | static struct inode * |
46 | spufs_alloc_inode(struct super_block *sb) | 49 | spufs_alloc_inode(struct super_block *sb) |
@@ -232,6 +235,95 @@ struct file_operations spufs_context_fops = { | |||
232 | .fsync = simple_sync_file, | 235 | .fsync = simple_sync_file, |
233 | }; | 236 | }; |
234 | 237 | ||
238 | static int spu_setup_isolated(struct spu_context *ctx) | ||
239 | { | ||
240 | int ret; | ||
241 | u64 __iomem *mfc_cntl; | ||
242 | u64 sr1; | ||
243 | u32 status; | ||
244 | unsigned long timeout; | ||
245 | const u32 status_loading = SPU_STATUS_RUNNING | ||
246 | | SPU_STATUS_ISOLATED_STATE | SPU_STATUS_ISOLATED_LOAD_STATUS; | ||
247 | |||
248 | if (!isolated_loader) | ||
249 | return -ENODEV; | ||
250 | |||
251 | if ((ret = spu_acquire_exclusive(ctx)) != 0) | ||
252 | return ret; | ||
253 | |||
254 | mfc_cntl = &ctx->spu->priv2->mfc_control_RW; | ||
255 | |||
256 | /* purge the MFC DMA queue to ensure no spurious accesses before we | ||
257 | * enter kernel mode */ | ||
258 | timeout = jiffies + HZ; | ||
259 | out_be64(mfc_cntl, MFC_CNTL_PURGE_DMA_REQUEST); | ||
260 | while ((in_be64(mfc_cntl) & MFC_CNTL_PURGE_DMA_STATUS_MASK) | ||
261 | != MFC_CNTL_PURGE_DMA_COMPLETE) { | ||
262 | if (time_after(jiffies, timeout)) { | ||
263 | printk(KERN_ERR "%s: timeout flushing MFC DMA queue\n", | ||
264 | __FUNCTION__); | ||
265 | ret = -EIO; | ||
266 | goto out_unlock; | ||
267 | } | ||
268 | cond_resched(); | ||
269 | } | ||
270 | |||
271 | /* put the SPE in kernel mode to allow access to the loader */ | ||
272 | sr1 = spu_mfc_sr1_get(ctx->spu); | ||
273 | sr1 &= ~MFC_STATE1_PROBLEM_STATE_MASK; | ||
274 | spu_mfc_sr1_set(ctx->spu, sr1); | ||
275 | |||
276 | /* start the loader */ | ||
277 | ctx->ops->signal1_write(ctx, (unsigned long)isolated_loader >> 32); | ||
278 | ctx->ops->signal2_write(ctx, | ||
279 | (unsigned long)isolated_loader & 0xffffffff); | ||
280 | |||
281 | ctx->ops->runcntl_write(ctx, | ||
282 | SPU_RUNCNTL_RUNNABLE | SPU_RUNCNTL_ISOLATE); | ||
283 | |||
284 | ret = 0; | ||
285 | timeout = jiffies + HZ; | ||
286 | while (((status = ctx->ops->status_read(ctx)) & status_loading) == | ||
287 | status_loading) { | ||
288 | if (time_after(jiffies, timeout)) { | ||
289 | printk(KERN_ERR "%s: timeout waiting for loader\n", | ||
290 | __FUNCTION__); | ||
291 | ret = -EIO; | ||
292 | goto out_drop_priv; | ||
293 | } | ||
294 | cond_resched(); | ||
295 | } | ||
296 | |||
297 | if (!(status & SPU_STATUS_RUNNING)) { | ||
298 | /* If isolated LOAD has failed: run SPU, we will get a stop-and | ||
299 | * signal later. */ | ||
300 | pr_debug("%s: isolated LOAD failed\n", __FUNCTION__); | ||
301 | ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE); | ||
302 | ret = -EACCES; | ||
303 | |||
304 | } else if (!(status & SPU_STATUS_ISOLATED_STATE)) { | ||
305 | /* This isn't allowed by the CBEA, but check anyway */ | ||
306 | pr_debug("%s: SPU fell out of isolated mode?\n", __FUNCTION__); | ||
307 | ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_STOP); | ||
308 | ret = -EINVAL; | ||
309 | } | ||
310 | |||
311 | out_drop_priv: | ||
312 | /* Finished accessing the loader. Drop kernel mode */ | ||
313 | sr1 |= MFC_STATE1_PROBLEM_STATE_MASK; | ||
314 | spu_mfc_sr1_set(ctx->spu, sr1); | ||
315 | |||
316 | out_unlock: | ||
317 | spu_release_exclusive(ctx); | ||
318 | return ret; | ||
319 | } | ||
320 | |||
321 | int spu_recycle_isolated(struct spu_context *ctx) | ||
322 | { | ||
323 | ctx->ops->runcntl_stop(ctx); | ||
324 | return spu_setup_isolated(ctx); | ||
325 | } | ||
326 | |||
235 | static int | 327 | static int |
236 | spufs_mkdir(struct inode *dir, struct dentry *dentry, unsigned int flags, | 328 | spufs_mkdir(struct inode *dir, struct dentry *dentry, unsigned int flags, |
237 | int mode) | 329 | int mode) |
@@ -255,10 +347,14 @@ spufs_mkdir(struct inode *dir, struct dentry *dentry, unsigned int flags, | |||
255 | goto out_iput; | 347 | goto out_iput; |
256 | 348 | ||
257 | ctx->flags = flags; | 349 | ctx->flags = flags; |
258 | |||
259 | inode->i_op = &spufs_dir_inode_operations; | 350 | inode->i_op = &spufs_dir_inode_operations; |
260 | inode->i_fop = &simple_dir_operations; | 351 | inode->i_fop = &simple_dir_operations; |
261 | ret = spufs_fill_dir(dentry, spufs_dir_contents, mode, ctx); | 352 | if (flags & SPU_CREATE_NOSCHED) |
353 | ret = spufs_fill_dir(dentry, spufs_dir_nosched_contents, | ||
354 | mode, ctx); | ||
355 | else | ||
356 | ret = spufs_fill_dir(dentry, spufs_dir_contents, mode, ctx); | ||
357 | |||
262 | if (ret) | 358 | if (ret) |
263 | goto out_free_ctx; | 359 | goto out_free_ctx; |
264 | 360 | ||
@@ -307,6 +403,16 @@ static int spufs_create_context(struct inode *inode, | |||
307 | { | 403 | { |
308 | int ret; | 404 | int ret; |
309 | 405 | ||
406 | ret = -EPERM; | ||
407 | if ((flags & SPU_CREATE_NOSCHED) && | ||
408 | !capable(CAP_SYS_NICE)) | ||
409 | goto out_unlock; | ||
410 | |||
411 | ret = -EINVAL; | ||
412 | if ((flags & (SPU_CREATE_NOSCHED | SPU_CREATE_ISOLATE)) | ||
413 | == SPU_CREATE_ISOLATE) | ||
414 | goto out_unlock; | ||
415 | |||
310 | ret = spufs_mkdir(inode, dentry, flags, mode & S_IRWXUGO); | 416 | ret = spufs_mkdir(inode, dentry, flags, mode & S_IRWXUGO); |
311 | if (ret) | 417 | if (ret) |
312 | goto out_unlock; | 418 | goto out_unlock; |
@@ -326,6 +432,13 @@ static int spufs_create_context(struct inode *inode, | |||
326 | out_unlock: | 432 | out_unlock: |
327 | mutex_unlock(&inode->i_mutex); | 433 | mutex_unlock(&inode->i_mutex); |
328 | out: | 434 | out: |
435 | if (ret >= 0 && (flags & SPU_CREATE_ISOLATE)) { | ||
436 | int setup_err = spu_setup_isolated( | ||
437 | SPUFS_I(dentry->d_inode)->i_ctx); | ||
438 | if (setup_err) | ||
439 | ret = setup_err; | ||
440 | } | ||
441 | |||
329 | dput(dentry); | 442 | dput(dentry); |
330 | return ret; | 443 | return ret; |
331 | } | 444 | } |
@@ -540,6 +653,30 @@ spufs_parse_options(char *options, struct inode *root) | |||
540 | return 1; | 653 | return 1; |
541 | } | 654 | } |
542 | 655 | ||
656 | static void | ||
657 | spufs_init_isolated_loader(void) | ||
658 | { | ||
659 | struct device_node *dn; | ||
660 | const char *loader; | ||
661 | int size; | ||
662 | |||
663 | dn = of_find_node_by_path("/spu-isolation"); | ||
664 | if (!dn) | ||
665 | return; | ||
666 | |||
667 | loader = get_property(dn, "loader", &size); | ||
668 | if (!loader) | ||
669 | return; | ||
670 | |||
671 | /* kmalloc should align on a 16 byte boundary..* */ | ||
672 | isolated_loader = kmalloc(size, GFP_KERNEL); | ||
673 | if (!isolated_loader) | ||
674 | return; | ||
675 | |||
676 | memcpy(isolated_loader, loader, size); | ||
677 | printk(KERN_INFO "spufs: SPU isolation mode enabled\n"); | ||
678 | } | ||
679 | |||
543 | static int | 680 | static int |
544 | spufs_create_root(struct super_block *sb, void *data) | 681 | spufs_create_root(struct super_block *sb, void *data) |
545 | { | 682 | { |
@@ -625,6 +762,8 @@ static int __init spufs_init(void) | |||
625 | ret = register_spu_syscalls(&spufs_calls); | 762 | ret = register_spu_syscalls(&spufs_calls); |
626 | if (ret) | 763 | if (ret) |
627 | goto out_fs; | 764 | goto out_fs; |
765 | |||
766 | spufs_init_isolated_loader(); | ||
628 | return 0; | 767 | return 0; |
629 | out_fs: | 768 | out_fs: |
630 | unregister_filesystem(&spufs_type); | 769 | unregister_filesystem(&spufs_type); |
diff --git a/arch/powerpc/platforms/cell/spufs/run.c b/arch/powerpc/platforms/cell/spufs/run.c index 63df8cf4ba16..a4a0080c2233 100644 --- a/arch/powerpc/platforms/cell/spufs/run.c +++ b/arch/powerpc/platforms/cell/spufs/run.c | |||
@@ -1,3 +1,5 @@ | |||
1 | #define DEBUG | ||
2 | |||
1 | #include <linux/wait.h> | 3 | #include <linux/wait.h> |
2 | #include <linux/ptrace.h> | 4 | #include <linux/ptrace.h> |
3 | 5 | ||
@@ -51,11 +53,17 @@ static inline int spu_stopped(struct spu_context *ctx, u32 * stat) | |||
51 | static inline int spu_run_init(struct spu_context *ctx, u32 * npc) | 53 | static inline int spu_run_init(struct spu_context *ctx, u32 * npc) |
52 | { | 54 | { |
53 | int ret; | 55 | int ret; |
56 | unsigned long runcntl = SPU_RUNCNTL_RUNNABLE; | ||
54 | 57 | ||
55 | if ((ret = spu_acquire_runnable(ctx)) != 0) | 58 | if ((ret = spu_acquire_runnable(ctx)) != 0) |
56 | return ret; | 59 | return ret; |
57 | ctx->ops->npc_write(ctx, *npc); | 60 | |
58 | ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE); | 61 | /* if we're in isolated mode, we would have started the SPU |
62 | * earlier, so don't do it again now. */ | ||
63 | if (!(ctx->flags & SPU_CREATE_ISOLATE)) { | ||
64 | ctx->ops->npc_write(ctx, *npc); | ||
65 | ctx->ops->runcntl_write(ctx, runcntl); | ||
66 | } | ||
59 | return 0; | 67 | return 0; |
60 | } | 68 | } |
61 | 69 | ||
diff --git a/arch/powerpc/platforms/cell/spufs/spufs.h b/arch/powerpc/platforms/cell/spufs/spufs.h index a0f55ca2d488..f438f0b8525d 100644 --- a/arch/powerpc/platforms/cell/spufs/spufs.h +++ b/arch/powerpc/platforms/cell/spufs/spufs.h | |||
@@ -135,6 +135,7 @@ struct spufs_inode_info { | |||
135 | container_of(inode, struct spufs_inode_info, vfs_inode) | 135 | container_of(inode, struct spufs_inode_info, vfs_inode) |
136 | 136 | ||
137 | extern struct tree_descr spufs_dir_contents[]; | 137 | extern struct tree_descr spufs_dir_contents[]; |
138 | extern struct tree_descr spufs_dir_nosched_contents[]; | ||
138 | 139 | ||
139 | /* system call implementation */ | 140 | /* system call implementation */ |
140 | long spufs_run_spu(struct file *file, | 141 | long spufs_run_spu(struct file *file, |
@@ -162,6 +163,12 @@ void spu_acquire(struct spu_context *ctx); | |||
162 | void spu_release(struct spu_context *ctx); | 163 | void spu_release(struct spu_context *ctx); |
163 | int spu_acquire_runnable(struct spu_context *ctx); | 164 | int spu_acquire_runnable(struct spu_context *ctx); |
164 | void spu_acquire_saved(struct spu_context *ctx); | 165 | void spu_acquire_saved(struct spu_context *ctx); |
166 | int spu_acquire_exclusive(struct spu_context *ctx); | ||
167 | |||
168 | static inline void spu_release_exclusive(struct spu_context *ctx) | ||
169 | { | ||
170 | up_write(&ctx->state_sema); | ||
171 | } | ||
165 | 172 | ||
166 | int spu_activate(struct spu_context *ctx, u64 flags); | 173 | int spu_activate(struct spu_context *ctx, u64 flags); |
167 | void spu_deactivate(struct spu_context *ctx); | 174 | void spu_deactivate(struct spu_context *ctx); |
@@ -169,6 +176,7 @@ void spu_yield(struct spu_context *ctx); | |||
169 | int __init spu_sched_init(void); | 176 | int __init spu_sched_init(void); |
170 | void __exit spu_sched_exit(void); | 177 | void __exit spu_sched_exit(void); |
171 | 178 | ||
179 | int spu_recycle_isolated(struct spu_context *ctx); | ||
172 | /* | 180 | /* |
173 | * spufs_wait | 181 | * spufs_wait |
174 | * Same as wait_event_interruptible(), except that here | 182 | * Same as wait_event_interruptible(), except that here |
diff --git a/arch/powerpc/platforms/cell/spufs/switch.c b/arch/powerpc/platforms/cell/spufs/switch.c index 0f782ca662ba..c08981ff7fc6 100644 --- a/arch/powerpc/platforms/cell/spufs/switch.c +++ b/arch/powerpc/platforms/cell/spufs/switch.c | |||
@@ -102,7 +102,7 @@ static inline int check_spu_isolate(struct spu_state *csa, struct spu *spu) | |||
102 | * saved at this time. | 102 | * saved at this time. |
103 | */ | 103 | */ |
104 | isolate_state = SPU_STATUS_ISOLATED_STATE | | 104 | isolate_state = SPU_STATUS_ISOLATED_STATE | |
105 | SPU_STATUS_ISOLATED_LOAD_STAUTUS | SPU_STATUS_ISOLATED_EXIT_STAUTUS; | 105 | SPU_STATUS_ISOLATED_LOAD_STATUS | SPU_STATUS_ISOLATED_EXIT_STATUS; |
106 | return (in_be32(&prob->spu_status_R) & isolate_state) ? 1 : 0; | 106 | return (in_be32(&prob->spu_status_R) & isolate_state) ? 1 : 0; |
107 | } | 107 | } |
108 | 108 | ||
@@ -1046,12 +1046,12 @@ static inline int suspend_spe(struct spu_state *csa, struct spu *spu) | |||
1046 | */ | 1046 | */ |
1047 | if (in_be32(&prob->spu_status_R) & SPU_STATUS_RUNNING) { | 1047 | if (in_be32(&prob->spu_status_R) & SPU_STATUS_RUNNING) { |
1048 | if (in_be32(&prob->spu_status_R) & | 1048 | if (in_be32(&prob->spu_status_R) & |
1049 | SPU_STATUS_ISOLATED_EXIT_STAUTUS) { | 1049 | SPU_STATUS_ISOLATED_EXIT_STATUS) { |
1050 | POLL_WHILE_TRUE(in_be32(&prob->spu_status_R) & | 1050 | POLL_WHILE_TRUE(in_be32(&prob->spu_status_R) & |
1051 | SPU_STATUS_RUNNING); | 1051 | SPU_STATUS_RUNNING); |
1052 | } | 1052 | } |
1053 | if ((in_be32(&prob->spu_status_R) & | 1053 | if ((in_be32(&prob->spu_status_R) & |
1054 | SPU_STATUS_ISOLATED_LOAD_STAUTUS) | 1054 | SPU_STATUS_ISOLATED_LOAD_STATUS) |
1055 | || (in_be32(&prob->spu_status_R) & | 1055 | || (in_be32(&prob->spu_status_R) & |
1056 | SPU_STATUS_ISOLATED_STATE)) { | 1056 | SPU_STATUS_ISOLATED_STATE)) { |
1057 | out_be32(&prob->spu_runcntl_RW, SPU_RUNCNTL_STOP); | 1057 | out_be32(&prob->spu_runcntl_RW, SPU_RUNCNTL_STOP); |
@@ -1085,7 +1085,7 @@ static inline void clear_spu_status(struct spu_state *csa, struct spu *spu) | |||
1085 | */ | 1085 | */ |
1086 | if (!(in_be32(&prob->spu_status_R) & SPU_STATUS_RUNNING)) { | 1086 | if (!(in_be32(&prob->spu_status_R) & SPU_STATUS_RUNNING)) { |
1087 | if (in_be32(&prob->spu_status_R) & | 1087 | if (in_be32(&prob->spu_status_R) & |
1088 | SPU_STATUS_ISOLATED_EXIT_STAUTUS) { | 1088 | SPU_STATUS_ISOLATED_EXIT_STATUS) { |
1089 | spu_mfc_sr1_set(spu, | 1089 | spu_mfc_sr1_set(spu, |
1090 | MFC_STATE1_MASTER_RUN_CONTROL_MASK); | 1090 | MFC_STATE1_MASTER_RUN_CONTROL_MASK); |
1091 | eieio(); | 1091 | eieio(); |
@@ -1095,7 +1095,7 @@ static inline void clear_spu_status(struct spu_state *csa, struct spu *spu) | |||
1095 | SPU_STATUS_RUNNING); | 1095 | SPU_STATUS_RUNNING); |
1096 | } | 1096 | } |
1097 | if ((in_be32(&prob->spu_status_R) & | 1097 | if ((in_be32(&prob->spu_status_R) & |
1098 | SPU_STATUS_ISOLATED_LOAD_STAUTUS) | 1098 | SPU_STATUS_ISOLATED_LOAD_STATUS) |
1099 | || (in_be32(&prob->spu_status_R) & | 1099 | || (in_be32(&prob->spu_status_R) & |
1100 | SPU_STATUS_ISOLATED_STATE)) { | 1100 | SPU_STATUS_ISOLATED_STATE)) { |
1101 | spu_mfc_sr1_set(spu, | 1101 | spu_mfc_sr1_set(spu, |
@@ -1916,6 +1916,51 @@ static void save_lscsa(struct spu_state *prev, struct spu *spu) | |||
1916 | wait_spu_stopped(prev, spu); /* Step 57. */ | 1916 | wait_spu_stopped(prev, spu); /* Step 57. */ |
1917 | } | 1917 | } |
1918 | 1918 | ||
1919 | static void force_spu_isolate_exit(struct spu *spu) | ||
1920 | { | ||
1921 | struct spu_problem __iomem *prob = spu->problem; | ||
1922 | struct spu_priv2 __iomem *priv2 = spu->priv2; | ||
1923 | |||
1924 | /* Stop SPE execution and wait for completion. */ | ||
1925 | out_be32(&prob->spu_runcntl_RW, SPU_RUNCNTL_STOP); | ||
1926 | iobarrier_rw(); | ||
1927 | POLL_WHILE_TRUE(in_be32(&prob->spu_status_R) & SPU_STATUS_RUNNING); | ||
1928 | |||
1929 | /* Restart SPE master runcntl. */ | ||
1930 | spu_mfc_sr1_set(spu, MFC_STATE1_MASTER_RUN_CONTROL_MASK); | ||
1931 | iobarrier_w(); | ||
1932 | |||
1933 | /* Initiate isolate exit request and wait for completion. */ | ||
1934 | out_be64(&priv2->spu_privcntl_RW, 4LL); | ||
1935 | iobarrier_w(); | ||
1936 | out_be32(&prob->spu_runcntl_RW, 2); | ||
1937 | iobarrier_rw(); | ||
1938 | POLL_WHILE_FALSE((in_be32(&prob->spu_status_R) | ||
1939 | & SPU_STATUS_STOPPED_BY_STOP)); | ||
1940 | |||
1941 | /* Reset load request to normal. */ | ||
1942 | out_be64(&priv2->spu_privcntl_RW, SPU_PRIVCNT_LOAD_REQUEST_NORMAL); | ||
1943 | iobarrier_w(); | ||
1944 | } | ||
1945 | |||
1946 | /** | ||
1947 | * stop_spu_isolate | ||
1948 | * Check SPU run-control state and force isolated | ||
1949 | * exit function as necessary. | ||
1950 | */ | ||
1951 | static void stop_spu_isolate(struct spu *spu) | ||
1952 | { | ||
1953 | struct spu_problem __iomem *prob = spu->problem; | ||
1954 | |||
1955 | if (in_be32(&prob->spu_status_R) & SPU_STATUS_ISOLATED_STATE) { | ||
1956 | /* The SPU is in isolated state; the only way | ||
1957 | * to get it out is to perform an isolated | ||
1958 | * exit (clean) operation. | ||
1959 | */ | ||
1960 | force_spu_isolate_exit(spu); | ||
1961 | } | ||
1962 | } | ||
1963 | |||
1919 | static void harvest(struct spu_state *prev, struct spu *spu) | 1964 | static void harvest(struct spu_state *prev, struct spu *spu) |
1920 | { | 1965 | { |
1921 | /* | 1966 | /* |
@@ -1928,6 +1973,7 @@ static void harvest(struct spu_state *prev, struct spu *spu) | |||
1928 | inhibit_user_access(prev, spu); /* Step 3. */ | 1973 | inhibit_user_access(prev, spu); /* Step 3. */ |
1929 | terminate_spu_app(prev, spu); /* Step 4. */ | 1974 | terminate_spu_app(prev, spu); /* Step 4. */ |
1930 | set_switch_pending(prev, spu); /* Step 5. */ | 1975 | set_switch_pending(prev, spu); /* Step 5. */ |
1976 | stop_spu_isolate(spu); /* NEW. */ | ||
1931 | remove_other_spu_access(prev, spu); /* Step 6. */ | 1977 | remove_other_spu_access(prev, spu); /* Step 6. */ |
1932 | suspend_mfc(prev, spu); /* Step 7. */ | 1978 | suspend_mfc(prev, spu); /* Step 7. */ |
1933 | wait_suspend_mfc_complete(prev, spu); /* Step 8. */ | 1979 | wait_suspend_mfc_complete(prev, spu); /* Step 8. */ |
@@ -2096,11 +2142,11 @@ int spu_save(struct spu_state *prev, struct spu *spu) | |||
2096 | acquire_spu_lock(spu); /* Step 1. */ | 2142 | acquire_spu_lock(spu); /* Step 1. */ |
2097 | rc = __do_spu_save(prev, spu); /* Steps 2-53. */ | 2143 | rc = __do_spu_save(prev, spu); /* Steps 2-53. */ |
2098 | release_spu_lock(spu); | 2144 | release_spu_lock(spu); |
2099 | if (rc) { | 2145 | if (rc != 0 && rc != 2 && rc != 6) { |
2100 | panic("%s failed on SPU[%d], rc=%d.\n", | 2146 | panic("%s failed on SPU[%d], rc=%d.\n", |
2101 | __func__, spu->number, rc); | 2147 | __func__, spu->number, rc); |
2102 | } | 2148 | } |
2103 | return rc; | 2149 | return 0; |
2104 | } | 2150 | } |
2105 | EXPORT_SYMBOL_GPL(spu_save); | 2151 | EXPORT_SYMBOL_GPL(spu_save); |
2106 | 2152 | ||
@@ -2165,9 +2211,6 @@ static void init_priv1(struct spu_state *csa) | |||
2165 | MFC_STATE1_PROBLEM_STATE_MASK | | 2211 | MFC_STATE1_PROBLEM_STATE_MASK | |
2166 | MFC_STATE1_RELOCATE_MASK | MFC_STATE1_BUS_TLBIE_MASK; | 2212 | MFC_STATE1_RELOCATE_MASK | MFC_STATE1_BUS_TLBIE_MASK; |
2167 | 2213 | ||
2168 | /* Set storage description. */ | ||
2169 | csa->priv1.mfc_sdr_RW = mfspr(SPRN_SDR1); | ||
2170 | |||
2171 | /* Enable OS-specific set of interrupts. */ | 2214 | /* Enable OS-specific set of interrupts. */ |
2172 | csa->priv1.int_mask_class0_RW = CLASS0_ENABLE_DMA_ALIGNMENT_INTR | | 2215 | csa->priv1.int_mask_class0_RW = CLASS0_ENABLE_DMA_ALIGNMENT_INTR | |
2173 | CLASS0_ENABLE_INVALID_DMA_COMMAND_INTR | | 2216 | CLASS0_ENABLE_INVALID_DMA_COMMAND_INTR | |
diff --git a/arch/powerpc/platforms/efika/Makefile b/arch/powerpc/platforms/efika/Makefile new file mode 100644 index 000000000000..17b2a781fbae --- /dev/null +++ b/arch/powerpc/platforms/efika/Makefile | |||
@@ -0,0 +1 @@ | |||
obj-y += setup.o mpc52xx.o pci.o | |||
diff --git a/arch/powerpc/platforms/efika/efika.h b/arch/powerpc/platforms/efika/efika.h new file mode 100644 index 000000000000..2f060fd097d7 --- /dev/null +++ b/arch/powerpc/platforms/efika/efika.h | |||
@@ -0,0 +1,19 @@ | |||
1 | /* | ||
2 | * Efika 5K2 platform setup - Header file | ||
3 | * | ||
4 | * Copyright (C) 2006 bplan GmbH | ||
5 | * | ||
6 | * This file is licensed under the terms of the GNU General Public License | ||
7 | * version 2. This program is licensed "as is" without any warranty of any | ||
8 | * kind, whether express or implied. | ||
9 | * | ||
10 | */ | ||
11 | |||
12 | #ifndef __ARCH_POWERPC_EFIKA__ | ||
13 | #define __ARCH_POWERPC_EFIKA__ | ||
14 | |||
15 | #define EFIKA_PLATFORM_NAME "Efika" | ||
16 | |||
17 | extern void __init efika_pcisetup(void); | ||
18 | |||
19 | #endif | ||
diff --git a/arch/powerpc/platforms/efika/pci.c b/arch/powerpc/platforms/efika/pci.c new file mode 100644 index 000000000000..62e05b2a9227 --- /dev/null +++ b/arch/powerpc/platforms/efika/pci.c | |||
@@ -0,0 +1,119 @@ | |||
1 | |||
2 | #include <linux/kernel.h> | ||
3 | #include <linux/pci.h> | ||
4 | #include <linux/string.h> | ||
5 | #include <linux/init.h> | ||
6 | |||
7 | #include <asm/io.h> | ||
8 | #include <asm/irq.h> | ||
9 | #include <asm/prom.h> | ||
10 | #include <asm/machdep.h> | ||
11 | #include <asm/sections.h> | ||
12 | #include <asm/pci-bridge.h> | ||
13 | #include <asm/rtas.h> | ||
14 | |||
15 | #include "efika.h" | ||
16 | |||
17 | #ifdef CONFIG_PCI | ||
18 | /* | ||
19 | * Access functions for PCI config space using RTAS calls. | ||
20 | */ | ||
21 | static int rtas_read_config(struct pci_bus *bus, unsigned int devfn, int offset, | ||
22 | int len, u32 * val) | ||
23 | { | ||
24 | struct pci_controller *hose = bus->sysdata; | ||
25 | unsigned long addr = (offset & 0xff) | ((devfn & 0xff) << 8) | ||
26 | | (((bus->number - hose->first_busno) & 0xff) << 16) | ||
27 | | (hose->index << 24); | ||
28 | int ret = -1; | ||
29 | int rval; | ||
30 | |||
31 | rval = rtas_call(rtas_token("read-pci-config"), 2, 2, &ret, addr, len); | ||
32 | *val = ret; | ||
33 | return rval ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL; | ||
34 | } | ||
35 | |||
36 | static int rtas_write_config(struct pci_bus *bus, unsigned int devfn, | ||
37 | int offset, int len, u32 val) | ||
38 | { | ||
39 | struct pci_controller *hose = bus->sysdata; | ||
40 | unsigned long addr = (offset & 0xff) | ((devfn & 0xff) << 8) | ||
41 | | (((bus->number - hose->first_busno) & 0xff) << 16) | ||
42 | | (hose->index << 24); | ||
43 | int rval; | ||
44 | |||
45 | rval = rtas_call(rtas_token("write-pci-config"), 3, 1, NULL, | ||
46 | addr, len, val); | ||
47 | return rval ? PCIBIOS_DEVICE_NOT_FOUND : PCIBIOS_SUCCESSFUL; | ||
48 | } | ||
49 | |||
50 | static struct pci_ops rtas_pci_ops = { | ||
51 | rtas_read_config, | ||
52 | rtas_write_config | ||
53 | }; | ||
54 | |||
55 | void __init efika_pcisetup(void) | ||
56 | { | ||
57 | const int *bus_range; | ||
58 | int len; | ||
59 | struct pci_controller *hose; | ||
60 | struct device_node *root; | ||
61 | struct device_node *pcictrl; | ||
62 | |||
63 | root = of_find_node_by_path("/"); | ||
64 | if (root == NULL) { | ||
65 | printk(KERN_WARNING EFIKA_PLATFORM_NAME | ||
66 | ": Unable to find the root node\n"); | ||
67 | return; | ||
68 | } | ||
69 | |||
70 | for (pcictrl = NULL;;) { | ||
71 | pcictrl = of_get_next_child(root, pcictrl); | ||
72 | if ((pcictrl == NULL) || (strcmp(pcictrl->name, "pci") == 0)) | ||
73 | break; | ||
74 | } | ||
75 | |||
76 | of_node_put(root); | ||
77 | |||
78 | if (pcictrl == NULL) { | ||
79 | printk(KERN_WARNING EFIKA_PLATFORM_NAME | ||
80 | ": Unable to find the PCI bridge node\n"); | ||
81 | return; | ||
82 | } | ||
83 | |||
84 | bus_range = get_property(pcictrl, "bus-range", &len); | ||
85 | if (bus_range == NULL || len < 2 * sizeof(int)) { | ||
86 | printk(KERN_WARNING EFIKA_PLATFORM_NAME | ||
87 | ": Can't get bus-range for %s\n", pcictrl->full_name); | ||
88 | return; | ||
89 | } | ||
90 | |||
91 | if (bus_range[1] == bus_range[0]) | ||
92 | printk(KERN_INFO EFIKA_PLATFORM_NAME ": PCI bus %d", | ||
93 | bus_range[0]); | ||
94 | else | ||
95 | printk(KERN_INFO EFIKA_PLATFORM_NAME ": PCI buses %d..%d", | ||
96 | bus_range[0], bus_range[1]); | ||
97 | printk(" controlled by %s\n", pcictrl->full_name); | ||
98 | printk("\n"); | ||
99 | |||
100 | hose = pcibios_alloc_controller(); | ||
101 | if (!hose) { | ||
102 | printk(KERN_WARNING EFIKA_PLATFORM_NAME | ||
103 | ": Can't allocate PCI controller structure for %s\n", | ||
104 | pcictrl->full_name); | ||
105 | return; | ||
106 | } | ||
107 | |||
108 | hose->arch_data = of_node_get(pcictrl); | ||
109 | hose->first_busno = bus_range[0]; | ||
110 | hose->last_busno = bus_range[1]; | ||
111 | hose->ops = &rtas_pci_ops; | ||
112 | |||
113 | pci_process_bridge_OF_ranges(hose, pcictrl, 0); | ||
114 | } | ||
115 | |||
116 | #else | ||
117 | void __init efika_pcisetup(void) | ||
118 | {} | ||
119 | #endif | ||
diff --git a/arch/powerpc/platforms/efika/setup.c b/arch/powerpc/platforms/efika/setup.c new file mode 100644 index 000000000000..3bc1b5fe0cea --- /dev/null +++ b/arch/powerpc/platforms/efika/setup.c | |||
@@ -0,0 +1,149 @@ | |||
1 | /* | ||
2 | * | ||
3 | * Efika 5K2 platform setup | ||
4 | * Some code really inspired from the lite5200b platform. | ||
5 | * | ||
6 | * Copyright (C) 2006 bplan GmbH | ||
7 | * | ||
8 | * This file is licensed under the terms of the GNU General Public License | ||
9 | * version 2. This program is licensed "as is" without any warranty of any | ||
10 | * kind, whether express or implied. | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #include <linux/errno.h> | ||
15 | #include <linux/kernel.h> | ||
16 | #include <linux/slab.h> | ||
17 | #include <linux/reboot.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <linux/utsrelease.h> | ||
20 | #include <linux/seq_file.h> | ||
21 | #include <linux/root_dev.h> | ||
22 | #include <linux/initrd.h> | ||
23 | #include <linux/timer.h> | ||
24 | #include <linux/pci.h> | ||
25 | |||
26 | #include <asm/pgtable.h> | ||
27 | #include <asm/prom.h> | ||
28 | #include <asm/time.h> | ||
29 | #include <asm/machdep.h> | ||
30 | #include <asm/rtas.h> | ||
31 | #include <asm/of_device.h> | ||
32 | #include <asm/mpc52xx.h> | ||
33 | |||
34 | #include "efika.h" | ||
35 | |||
36 | static void efika_show_cpuinfo(struct seq_file *m) | ||
37 | { | ||
38 | struct device_node *root; | ||
39 | const char *revision = NULL; | ||
40 | const char *codegendescription = NULL; | ||
41 | const char *codegenvendor = NULL; | ||
42 | |||
43 | root = of_find_node_by_path("/"); | ||
44 | if (root) { | ||
45 | revision = get_property(root, "revision", NULL); | ||
46 | codegendescription = | ||
47 | get_property(root, "CODEGEN,description", NULL); | ||
48 | codegenvendor = get_property(root, "CODEGEN,vendor", NULL); | ||
49 | |||
50 | of_node_put(root); | ||
51 | } | ||
52 | |||
53 | if (codegendescription) | ||
54 | seq_printf(m, "machine\t\t: %s\n", codegendescription); | ||
55 | else | ||
56 | seq_printf(m, "machine\t\t: Efika\n"); | ||
57 | |||
58 | if (revision) | ||
59 | seq_printf(m, "revision\t: %s\n", revision); | ||
60 | |||
61 | if (codegenvendor) | ||
62 | seq_printf(m, "vendor\t\t: %s\n", codegenvendor); | ||
63 | |||
64 | of_node_put(root); | ||
65 | } | ||
66 | |||
67 | static void __init efika_setup_arch(void) | ||
68 | { | ||
69 | rtas_initialize(); | ||
70 | |||
71 | #ifdef CONFIG_BLK_DEV_INITRD | ||
72 | initrd_below_start_ok = 1; | ||
73 | |||
74 | if (initrd_start) | ||
75 | ROOT_DEV = Root_RAM0; | ||
76 | else | ||
77 | #endif | ||
78 | ROOT_DEV = Root_SDA2; /* sda2 (sda1 is for the kernel) */ | ||
79 | |||
80 | efika_pcisetup(); | ||
81 | |||
82 | if (ppc_md.progress) | ||
83 | ppc_md.progress("Linux/PPC " UTS_RELEASE " runnung on Efika ;-)\n", 0x0); | ||
84 | } | ||
85 | |||
86 | static void __init efika_init(void) | ||
87 | { | ||
88 | struct device_node *np; | ||
89 | struct device_node *cnp = NULL; | ||
90 | const u32 *base; | ||
91 | |||
92 | /* Find every child of the SOC node and add it to of_platform */ | ||
93 | np = of_find_node_by_name(NULL, "builtin"); | ||
94 | if (np) { | ||
95 | char name[BUS_ID_SIZE]; | ||
96 | while ((cnp = of_get_next_child(np, cnp))) { | ||
97 | strcpy(name, cnp->name); | ||
98 | |||
99 | base = get_property(cnp, "reg", NULL); | ||
100 | if (base == NULL) | ||
101 | continue; | ||
102 | |||
103 | snprintf(name+strlen(name), BUS_ID_SIZE, "@%x", *base); | ||
104 | of_platform_device_create(cnp, name, NULL); | ||
105 | |||
106 | printk(KERN_INFO EFIKA_PLATFORM_NAME" : Added %s (type '%s' at '%s') to the known devices\n", name, cnp->type, cnp->full_name); | ||
107 | } | ||
108 | } | ||
109 | |||
110 | if (ppc_md.progress) | ||
111 | ppc_md.progress(" Have fun with your Efika! ", 0x7777); | ||
112 | } | ||
113 | |||
114 | static int __init efika_probe(void) | ||
115 | { | ||
116 | char *model = of_get_flat_dt_prop(of_get_flat_dt_root(), | ||
117 | "model", NULL); | ||
118 | |||
119 | if (model == NULL) | ||
120 | return 0; | ||
121 | if (strcmp(model, "EFIKA5K2")) | ||
122 | return 0; | ||
123 | |||
124 | ISA_DMA_THRESHOLD = ~0L; | ||
125 | DMA_MODE_READ = 0x44; | ||
126 | DMA_MODE_WRITE = 0x48; | ||
127 | |||
128 | return 1; | ||
129 | } | ||
130 | |||
131 | define_machine(efika) | ||
132 | { | ||
133 | .name = EFIKA_PLATFORM_NAME, | ||
134 | .probe = efika_probe, | ||
135 | .setup_arch = efika_setup_arch, | ||
136 | .init = efika_init, | ||
137 | .show_cpuinfo = efika_show_cpuinfo, | ||
138 | .init_IRQ = mpc52xx_init_irq, | ||
139 | .get_irq = mpc52xx_get_irq, | ||
140 | .restart = rtas_restart, | ||
141 | .power_off = rtas_power_off, | ||
142 | .halt = rtas_halt, | ||
143 | .set_rtc_time = rtas_set_rtc_time, | ||
144 | .get_rtc_time = rtas_get_rtc_time, | ||
145 | .progress = rtas_progress, | ||
146 | .get_boot_time = rtas_get_boot_time, | ||
147 | .calibrate_decr = generic_calibrate_decr, | ||
148 | .phys_mem_access_prot = pci_phys_mem_access_prot, | ||
149 | }; | ||
diff --git a/arch/powerpc/platforms/iseries/dt.c b/arch/powerpc/platforms/iseries/dt.c index e305deee7f44..9e8a334a518a 100644 --- a/arch/powerpc/platforms/iseries/dt.c +++ b/arch/powerpc/platforms/iseries/dt.c | |||
@@ -41,6 +41,7 @@ | |||
41 | #include "call_pci.h" | 41 | #include "call_pci.h" |
42 | #include "pci.h" | 42 | #include "pci.h" |
43 | #include "it_exp_vpd_panel.h" | 43 | #include "it_exp_vpd_panel.h" |
44 | #include "naca.h" | ||
44 | 45 | ||
45 | #ifdef DEBUG | 46 | #ifdef DEBUG |
46 | #define DBG(fmt...) udbg_printf(fmt) | 47 | #define DBG(fmt...) udbg_printf(fmt) |
@@ -205,13 +206,11 @@ static void __init dt_prop_u32(struct iseries_flat_dt *dt, const char *name, | |||
205 | dt_prop(dt, name, &data, sizeof(u32)); | 206 | dt_prop(dt, name, &data, sizeof(u32)); |
206 | } | 207 | } |
207 | 208 | ||
208 | #ifdef notyet | ||
209 | static void __init dt_prop_u64(struct iseries_flat_dt *dt, const char *name, | 209 | static void __init dt_prop_u64(struct iseries_flat_dt *dt, const char *name, |
210 | u64 data) | 210 | u64 data) |
211 | { | 211 | { |
212 | dt_prop(dt, name, &data, sizeof(u64)); | 212 | dt_prop(dt, name, &data, sizeof(u64)); |
213 | } | 213 | } |
214 | #endif | ||
215 | 214 | ||
216 | static void __init dt_prop_u64_list(struct iseries_flat_dt *dt, | 215 | static void __init dt_prop_u64_list(struct iseries_flat_dt *dt, |
217 | const char *name, u64 *data, int n) | 216 | const char *name, u64 *data, int n) |
@@ -306,6 +305,17 @@ static void __init dt_model(struct iseries_flat_dt *dt) | |||
306 | dt_prop_u32(dt, "ibm,partition-no", HvLpConfig_getLpIndex()); | 305 | dt_prop_u32(dt, "ibm,partition-no", HvLpConfig_getLpIndex()); |
307 | } | 306 | } |
308 | 307 | ||
308 | static void __init dt_initrd(struct iseries_flat_dt *dt) | ||
309 | { | ||
310 | #ifdef CONFIG_BLK_DEV_INITRD | ||
311 | if (naca.xRamDisk) { | ||
312 | dt_prop_u64(dt, "linux,initrd-start", (u64)naca.xRamDisk); | ||
313 | dt_prop_u64(dt, "linux,initrd-end", | ||
314 | (u64)naca.xRamDisk + naca.xRamDiskSize * HW_PAGE_SIZE); | ||
315 | } | ||
316 | #endif | ||
317 | } | ||
318 | |||
309 | static void __init dt_do_vdevice(struct iseries_flat_dt *dt, | 319 | static void __init dt_do_vdevice(struct iseries_flat_dt *dt, |
310 | const char *name, u32 reg, int unit, | 320 | const char *name, u32 reg, int unit, |
311 | const char *type, const char *compat, int end) | 321 | const char *type, const char *compat, int end) |
@@ -641,6 +651,7 @@ void * __init build_flat_dt(unsigned long phys_mem_size) | |||
641 | /* /chosen */ | 651 | /* /chosen */ |
642 | dt_start_node(iseries_dt, "chosen"); | 652 | dt_start_node(iseries_dt, "chosen"); |
643 | dt_prop_str(iseries_dt, "bootargs", cmd_line); | 653 | dt_prop_str(iseries_dt, "bootargs", cmd_line); |
654 | dt_initrd(iseries_dt); | ||
644 | dt_end_node(iseries_dt); | 655 | dt_end_node(iseries_dt); |
645 | 656 | ||
646 | dt_cpus(iseries_dt); | 657 | dt_cpus(iseries_dt); |
diff --git a/arch/powerpc/platforms/iseries/ksyms.c b/arch/powerpc/platforms/iseries/ksyms.c index a2200842f4e5..2430848b98e7 100644 --- a/arch/powerpc/platforms/iseries/ksyms.c +++ b/arch/powerpc/platforms/iseries/ksyms.c | |||
@@ -19,9 +19,3 @@ EXPORT_SYMBOL(HvCall4); | |||
19 | EXPORT_SYMBOL(HvCall5); | 19 | EXPORT_SYMBOL(HvCall5); |
20 | EXPORT_SYMBOL(HvCall6); | 20 | EXPORT_SYMBOL(HvCall6); |
21 | EXPORT_SYMBOL(HvCall7); | 21 | EXPORT_SYMBOL(HvCall7); |
22 | |||
23 | #ifdef CONFIG_SMP | ||
24 | EXPORT_SYMBOL(local_get_flags); | ||
25 | EXPORT_SYMBOL(local_irq_disable); | ||
26 | EXPORT_SYMBOL(local_irq_restore); | ||
27 | #endif | ||
diff --git a/arch/powerpc/platforms/iseries/misc.S b/arch/powerpc/platforms/iseries/misc.S index 7641fc7e550a..2c6ff0fdac98 100644 --- a/arch/powerpc/platforms/iseries/misc.S +++ b/arch/powerpc/platforms/iseries/misc.S | |||
@@ -19,39 +19,8 @@ | |||
19 | 19 | ||
20 | .text | 20 | .text |
21 | 21 | ||
22 | /* unsigned long local_save_flags(void) */ | 22 | /* Handle pending interrupts in interrupt context */ |
23 | _GLOBAL(local_get_flags) | 23 | _GLOBAL(iseries_handle_interrupts) |
24 | lbz r3,PACAPROCENABLED(r13) | ||
25 | blr | ||
26 | |||
27 | /* unsigned long local_irq_disable(void) */ | ||
28 | _GLOBAL(local_irq_disable) | ||
29 | lbz r3,PACAPROCENABLED(r13) | ||
30 | li r4,0 | ||
31 | stb r4,PACAPROCENABLED(r13) | ||
32 | blr /* Done */ | ||
33 | |||
34 | /* void local_irq_restore(unsigned long flags) */ | ||
35 | _GLOBAL(local_irq_restore) | ||
36 | lbz r5,PACAPROCENABLED(r13) | ||
37 | /* Check if things are setup the way we want _already_. */ | ||
38 | cmpw 0,r3,r5 | ||
39 | beqlr | ||
40 | /* are we enabling interrupts? */ | ||
41 | cmpdi 0,r3,0 | ||
42 | stb r3,PACAPROCENABLED(r13) | ||
43 | beqlr | ||
44 | /* Check pending interrupts */ | ||
45 | /* A decrementer, IPI or PMC interrupt may have occurred | ||
46 | * while we were in the hypervisor (which enables) */ | ||
47 | ld r4,PACALPPACAPTR(r13) | ||
48 | ld r4,LPPACAANYINT(r4) | ||
49 | cmpdi r4,0 | ||
50 | beqlr | ||
51 | |||
52 | /* | ||
53 | * Handle pending interrupts in interrupt context | ||
54 | */ | ||
55 | li r0,0x5555 | 24 | li r0,0x5555 |
56 | sc | 25 | sc |
57 | blr | 26 | blr |
diff --git a/arch/powerpc/platforms/iseries/setup.c b/arch/powerpc/platforms/iseries/setup.c index 6f73469fd3b0..cd8965ec9ddb 100644 --- a/arch/powerpc/platforms/iseries/setup.c +++ b/arch/powerpc/platforms/iseries/setup.c | |||
@@ -21,7 +21,6 @@ | |||
21 | #include <linux/smp.h> | 21 | #include <linux/smp.h> |
22 | #include <linux/param.h> | 22 | #include <linux/param.h> |
23 | #include <linux/string.h> | 23 | #include <linux/string.h> |
24 | #include <linux/initrd.h> | ||
25 | #include <linux/seq_file.h> | 24 | #include <linux/seq_file.h> |
26 | #include <linux/kdev_t.h> | 25 | #include <linux/kdev_t.h> |
27 | #include <linux/major.h> | 26 | #include <linux/major.h> |
@@ -80,8 +79,6 @@ extern void iSeries_pci_final_fixup(void); | |||
80 | static void iSeries_pci_final_fixup(void) { } | 79 | static void iSeries_pci_final_fixup(void) { } |
81 | #endif | 80 | #endif |
82 | 81 | ||
83 | extern int rd_size; /* Defined in drivers/block/rd.c */ | ||
84 | |||
85 | extern unsigned long iSeries_recal_tb; | 82 | extern unsigned long iSeries_recal_tb; |
86 | extern unsigned long iSeries_recal_titan; | 83 | extern unsigned long iSeries_recal_titan; |
87 | 84 | ||
@@ -295,24 +292,6 @@ static void __init iSeries_init_early(void) | |||
295 | { | 292 | { |
296 | DBG(" -> iSeries_init_early()\n"); | 293 | DBG(" -> iSeries_init_early()\n"); |
297 | 294 | ||
298 | #if defined(CONFIG_BLK_DEV_INITRD) | ||
299 | /* | ||
300 | * If the init RAM disk has been configured and there is | ||
301 | * a non-zero starting address for it, set it up | ||
302 | */ | ||
303 | if (naca.xRamDisk) { | ||
304 | initrd_start = (unsigned long)__va(naca.xRamDisk); | ||
305 | initrd_end = initrd_start + naca.xRamDiskSize * HW_PAGE_SIZE; | ||
306 | initrd_below_start_ok = 1; // ramdisk in kernel space | ||
307 | ROOT_DEV = Root_RAM0; | ||
308 | if (((rd_size * 1024) / HW_PAGE_SIZE) < naca.xRamDiskSize) | ||
309 | rd_size = (naca.xRamDiskSize * HW_PAGE_SIZE) / 1024; | ||
310 | } else | ||
311 | #endif /* CONFIG_BLK_DEV_INITRD */ | ||
312 | { | ||
313 | /* ROOT_DEV = MKDEV(VIODASD_MAJOR, 1); */ | ||
314 | } | ||
315 | |||
316 | iSeries_recal_tb = get_tb(); | 295 | iSeries_recal_tb = get_tb(); |
317 | iSeries_recal_titan = HvCallXm_loadTod(); | 296 | iSeries_recal_titan = HvCallXm_loadTod(); |
318 | 297 | ||
@@ -331,17 +310,6 @@ static void __init iSeries_init_early(void) | |||
331 | 310 | ||
332 | mf_init(); | 311 | mf_init(); |
333 | 312 | ||
334 | /* If we were passed an initrd, set the ROOT_DEV properly if the values | ||
335 | * look sensible. If not, clear initrd reference. | ||
336 | */ | ||
337 | #ifdef CONFIG_BLK_DEV_INITRD | ||
338 | if (initrd_start >= KERNELBASE && initrd_end >= KERNELBASE && | ||
339 | initrd_end > initrd_start) | ||
340 | ROOT_DEV = Root_RAM0; | ||
341 | else | ||
342 | initrd_start = initrd_end = 0; | ||
343 | #endif /* CONFIG_BLK_DEV_INITRD */ | ||
344 | |||
345 | DBG(" <- iSeries_init_early()\n"); | 313 | DBG(" <- iSeries_init_early()\n"); |
346 | } | 314 | } |
347 | 315 | ||
diff --git a/arch/powerpc/platforms/powermac/feature.c b/arch/powerpc/platforms/powermac/feature.c index e49621be6640..c29a6a064d22 100644 --- a/arch/powerpc/platforms/powermac/feature.c +++ b/arch/powerpc/platforms/powermac/feature.c | |||
@@ -486,10 +486,6 @@ static long heathrow_sound_enable(struct device_node *node, long param, | |||
486 | 486 | ||
487 | static u32 save_fcr[6]; | 487 | static u32 save_fcr[6]; |
488 | static u32 save_mbcr; | 488 | static u32 save_mbcr; |
489 | static u32 save_gpio_levels[2]; | ||
490 | static u8 save_gpio_extint[KEYLARGO_GPIO_EXTINT_CNT]; | ||
491 | static u8 save_gpio_normal[KEYLARGO_GPIO_CNT]; | ||
492 | static u32 save_unin_clock_ctl; | ||
493 | static struct dbdma_regs save_dbdma[13]; | 489 | static struct dbdma_regs save_dbdma[13]; |
494 | static struct dbdma_regs save_alt_dbdma[13]; | 490 | static struct dbdma_regs save_alt_dbdma[13]; |
495 | 491 | ||
@@ -1548,6 +1544,10 @@ void g5_phy_disable_cpu1(void) | |||
1548 | 1544 | ||
1549 | 1545 | ||
1550 | #ifdef CONFIG_PM | 1546 | #ifdef CONFIG_PM |
1547 | static u32 save_gpio_levels[2]; | ||
1548 | static u8 save_gpio_extint[KEYLARGO_GPIO_EXTINT_CNT]; | ||
1549 | static u8 save_gpio_normal[KEYLARGO_GPIO_CNT]; | ||
1550 | static u32 save_unin_clock_ctl; | ||
1551 | 1551 | ||
1552 | static void keylargo_shutdown(struct macio_chip *macio, int sleep_mode) | 1552 | static void keylargo_shutdown(struct macio_chip *macio, int sleep_mode) |
1553 | { | 1553 | { |
diff --git a/arch/powerpc/platforms/powermac/setup.c b/arch/powerpc/platforms/powermac/setup.c index 824a618396ab..cb1c342061e2 100644 --- a/arch/powerpc/platforms/powermac/setup.c +++ b/arch/powerpc/platforms/powermac/setup.c | |||
@@ -361,7 +361,7 @@ char *bootdevice; | |||
361 | void *boot_host; | 361 | void *boot_host; |
362 | int boot_target; | 362 | int boot_target; |
363 | int boot_part; | 363 | int boot_part; |
364 | extern dev_t boot_dev; | 364 | static dev_t boot_dev; |
365 | 365 | ||
366 | #ifdef CONFIG_SCSI | 366 | #ifdef CONFIG_SCSI |
367 | void __init note_scsi_host(struct device_node *node, void *host) | 367 | void __init note_scsi_host(struct device_node *node, void *host) |
diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c index 1820a0b0a8c6..721436db3ef0 100644 --- a/arch/powerpc/platforms/pseries/lpar.c +++ b/arch/powerpc/platforms/pseries/lpar.c | |||
@@ -282,7 +282,7 @@ void vpa_init(int cpu) | |||
282 | } | 282 | } |
283 | } | 283 | } |
284 | 284 | ||
285 | long pSeries_lpar_hpte_insert(unsigned long hpte_group, | 285 | static long pSeries_lpar_hpte_insert(unsigned long hpte_group, |
286 | unsigned long va, unsigned long pa, | 286 | unsigned long va, unsigned long pa, |
287 | unsigned long rflags, unsigned long vflags, | 287 | unsigned long rflags, unsigned long vflags, |
288 | int psize) | 288 | int psize) |
@@ -506,7 +506,7 @@ static void pSeries_lpar_hpte_invalidate(unsigned long slot, unsigned long va, | |||
506 | * Take a spinlock around flushes to avoid bouncing the hypervisor tlbie | 506 | * Take a spinlock around flushes to avoid bouncing the hypervisor tlbie |
507 | * lock. | 507 | * lock. |
508 | */ | 508 | */ |
509 | void pSeries_lpar_flush_hash_range(unsigned long number, int local) | 509 | static void pSeries_lpar_flush_hash_range(unsigned long number, int local) |
510 | { | 510 | { |
511 | int i; | 511 | int i; |
512 | unsigned long flags = 0; | 512 | unsigned long flags = 0; |