diff options
Diffstat (limited to 'arch/powerpc/platforms')
73 files changed, 7299 insertions, 968 deletions
diff --git a/arch/powerpc/platforms/52xx/Makefile b/arch/powerpc/platforms/52xx/Makefile index a46184a0c750..795b713ec9ee 100644 --- a/arch/powerpc/platforms/52xx/Makefile +++ b/arch/powerpc/platforms/52xx/Makefile | |||
@@ -3,6 +3,7 @@ | |||
3 | # | 3 | # |
4 | ifeq ($(CONFIG_PPC_MERGE),y) | 4 | ifeq ($(CONFIG_PPC_MERGE),y) |
5 | obj-y += mpc52xx_pic.o mpc52xx_common.o | 5 | obj-y += mpc52xx_pic.o mpc52xx_common.o |
6 | obj-$(CONFIG_PCI) += mpc52xx_pci.o | ||
6 | endif | 7 | endif |
7 | 8 | ||
8 | obj-$(CONFIG_PPC_EFIKA) += efika-setup.o efika-pci.o | 9 | obj-$(CONFIG_PPC_EFIKA) += efika-setup.o efika-pci.o |
diff --git a/arch/powerpc/platforms/52xx/lite5200.c b/arch/powerpc/platforms/52xx/lite5200.c index 0f21bab33f6c..cdb16bfa6ca6 100644 --- a/arch/powerpc/platforms/52xx/lite5200.c +++ b/arch/powerpc/platforms/52xx/lite5200.c | |||
@@ -107,6 +107,12 @@ static void __init lite52xx_setup_arch(void) | |||
107 | mpc52xx_setup_cpu(); /* Generic */ | 107 | mpc52xx_setup_cpu(); /* Generic */ |
108 | lite52xx_setup_cpu(); /* Platorm specific */ | 108 | lite52xx_setup_cpu(); /* Platorm specific */ |
109 | 109 | ||
110 | #ifdef CONFIG_PCI | ||
111 | np = of_find_node_by_type(np, "pci"); | ||
112 | if (np) | ||
113 | mpc52xx_add_bridge(np); | ||
114 | #endif | ||
115 | |||
110 | #ifdef CONFIG_BLK_DEV_INITRD | 116 | #ifdef CONFIG_BLK_DEV_INITRD |
111 | if (initrd_start) | 117 | if (initrd_start) |
112 | ROOT_DEV = Root_RAM0; | 118 | ROOT_DEV = Root_RAM0; |
diff --git a/arch/powerpc/platforms/52xx/mpc52xx_pci.c b/arch/powerpc/platforms/52xx/mpc52xx_pci.c new file mode 100644 index 000000000000..faf161bdbc1c --- /dev/null +++ b/arch/powerpc/platforms/52xx/mpc52xx_pci.c | |||
@@ -0,0 +1,412 @@ | |||
1 | /* | ||
2 | * PCI code for the Freescale MPC52xx embedded CPU. | ||
3 | * | ||
4 | * Copyright (C) 2006 Secret Lab Technologies Ltd. | ||
5 | * Grant Likely <grant.likely@secretlab.ca> | ||
6 | * Copyright (C) 2004 Sylvain Munaut <tnt@246tNt.com> | ||
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 | #undef DEBUG | ||
14 | |||
15 | #include <asm/pci.h> | ||
16 | #include <asm/mpc52xx.h> | ||
17 | #include <asm/delay.h> | ||
18 | #include <asm/machdep.h> | ||
19 | #include <linux/kernel.h> | ||
20 | |||
21 | |||
22 | /* ======================================================================== */ | ||
23 | /* PCI windows config */ | ||
24 | /* ======================================================================== */ | ||
25 | |||
26 | #define MPC52xx_PCI_TARGET_IO 0xf0000000 | ||
27 | #define MPC52xx_PCI_TARGET_MEM 0x00000000 | ||
28 | |||
29 | |||
30 | /* ======================================================================== */ | ||
31 | /* Structures mapping & Defines for PCI Unit */ | ||
32 | /* ======================================================================== */ | ||
33 | |||
34 | #define MPC52xx_PCI_GSCR_BM 0x40000000 | ||
35 | #define MPC52xx_PCI_GSCR_PE 0x20000000 | ||
36 | #define MPC52xx_PCI_GSCR_SE 0x10000000 | ||
37 | #define MPC52xx_PCI_GSCR_XLB2PCI_MASK 0x07000000 | ||
38 | #define MPC52xx_PCI_GSCR_XLB2PCI_SHIFT 24 | ||
39 | #define MPC52xx_PCI_GSCR_IPG2PCI_MASK 0x00070000 | ||
40 | #define MPC52xx_PCI_GSCR_IPG2PCI_SHIFT 16 | ||
41 | #define MPC52xx_PCI_GSCR_BME 0x00004000 | ||
42 | #define MPC52xx_PCI_GSCR_PEE 0x00002000 | ||
43 | #define MPC52xx_PCI_GSCR_SEE 0x00001000 | ||
44 | #define MPC52xx_PCI_GSCR_PR 0x00000001 | ||
45 | |||
46 | |||
47 | #define MPC52xx_PCI_IWBTAR_TRANSLATION(proc_ad,pci_ad,size) \ | ||
48 | ( ( (proc_ad) & 0xff000000 ) | \ | ||
49 | ( (((size) - 1) >> 8) & 0x00ff0000 ) | \ | ||
50 | ( ((pci_ad) >> 16) & 0x0000ff00 ) ) | ||
51 | |||
52 | #define MPC52xx_PCI_IWCR_PACK(win0,win1,win2) (((win0) << 24) | \ | ||
53 | ((win1) << 16) | \ | ||
54 | ((win2) << 8)) | ||
55 | |||
56 | #define MPC52xx_PCI_IWCR_DISABLE 0x0 | ||
57 | #define MPC52xx_PCI_IWCR_ENABLE 0x1 | ||
58 | #define MPC52xx_PCI_IWCR_READ 0x0 | ||
59 | #define MPC52xx_PCI_IWCR_READ_LINE 0x2 | ||
60 | #define MPC52xx_PCI_IWCR_READ_MULTI 0x4 | ||
61 | #define MPC52xx_PCI_IWCR_MEM 0x0 | ||
62 | #define MPC52xx_PCI_IWCR_IO 0x8 | ||
63 | |||
64 | #define MPC52xx_PCI_TCR_P 0x01000000 | ||
65 | #define MPC52xx_PCI_TCR_LD 0x00010000 | ||
66 | |||
67 | #define MPC52xx_PCI_TBATR_DISABLE 0x0 | ||
68 | #define MPC52xx_PCI_TBATR_ENABLE 0x1 | ||
69 | |||
70 | struct mpc52xx_pci { | ||
71 | u32 idr; /* PCI + 0x00 */ | ||
72 | u32 scr; /* PCI + 0x04 */ | ||
73 | u32 ccrir; /* PCI + 0x08 */ | ||
74 | u32 cr1; /* PCI + 0x0C */ | ||
75 | u32 bar0; /* PCI + 0x10 */ | ||
76 | u32 bar1; /* PCI + 0x14 */ | ||
77 | u8 reserved1[16]; /* PCI + 0x18 */ | ||
78 | u32 ccpr; /* PCI + 0x28 */ | ||
79 | u32 sid; /* PCI + 0x2C */ | ||
80 | u32 erbar; /* PCI + 0x30 */ | ||
81 | u32 cpr; /* PCI + 0x34 */ | ||
82 | u8 reserved2[4]; /* PCI + 0x38 */ | ||
83 | u32 cr2; /* PCI + 0x3C */ | ||
84 | u8 reserved3[32]; /* PCI + 0x40 */ | ||
85 | u32 gscr; /* PCI + 0x60 */ | ||
86 | u32 tbatr0; /* PCI + 0x64 */ | ||
87 | u32 tbatr1; /* PCI + 0x68 */ | ||
88 | u32 tcr; /* PCI + 0x6C */ | ||
89 | u32 iw0btar; /* PCI + 0x70 */ | ||
90 | u32 iw1btar; /* PCI + 0x74 */ | ||
91 | u32 iw2btar; /* PCI + 0x78 */ | ||
92 | u8 reserved4[4]; /* PCI + 0x7C */ | ||
93 | u32 iwcr; /* PCI + 0x80 */ | ||
94 | u32 icr; /* PCI + 0x84 */ | ||
95 | u32 isr; /* PCI + 0x88 */ | ||
96 | u32 arb; /* PCI + 0x8C */ | ||
97 | u8 reserved5[104]; /* PCI + 0x90 */ | ||
98 | u32 car; /* PCI + 0xF8 */ | ||
99 | u8 reserved6[4]; /* PCI + 0xFC */ | ||
100 | }; | ||
101 | |||
102 | |||
103 | /* ======================================================================== */ | ||
104 | /* PCI configuration acess */ | ||
105 | /* ======================================================================== */ | ||
106 | |||
107 | static int | ||
108 | mpc52xx_pci_read_config(struct pci_bus *bus, unsigned int devfn, | ||
109 | int offset, int len, u32 *val) | ||
110 | { | ||
111 | struct pci_controller *hose = bus->sysdata; | ||
112 | u32 value; | ||
113 | |||
114 | if (ppc_md.pci_exclude_device) | ||
115 | if (ppc_md.pci_exclude_device(bus->number, devfn)) | ||
116 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
117 | |||
118 | out_be32(hose->cfg_addr, | ||
119 | (1 << 31) | | ||
120 | ((bus->number - hose->bus_offset) << 16) | | ||
121 | (devfn << 8) | | ||
122 | (offset & 0xfc)); | ||
123 | mb(); | ||
124 | |||
125 | #if defined(CONFIG_PPC_MPC5200_BUGFIX) | ||
126 | if (bus->number != hose->bus_offset) { | ||
127 | /* workaround for the bug 435 of the MPC5200 (L25R); | ||
128 | * Don't do 32 bits config access during type-1 cycles */ | ||
129 | switch (len) { | ||
130 | case 1: | ||
131 | value = in_8(((u8 __iomem *)hose->cfg_data) + | ||
132 | (offset & 3)); | ||
133 | break; | ||
134 | case 2: | ||
135 | value = in_le16(((u16 __iomem *)hose->cfg_data) + | ||
136 | ((offset>>1) & 1)); | ||
137 | break; | ||
138 | |||
139 | default: | ||
140 | value = in_le16((u16 __iomem *)hose->cfg_data) | | ||
141 | (in_le16(((u16 __iomem *)hose->cfg_data) + 1) << 16); | ||
142 | break; | ||
143 | } | ||
144 | } | ||
145 | else | ||
146 | #endif | ||
147 | { | ||
148 | value = in_le32(hose->cfg_data); | ||
149 | |||
150 | if (len != 4) { | ||
151 | value >>= ((offset & 0x3) << 3); | ||
152 | value &= 0xffffffff >> (32 - (len << 3)); | ||
153 | } | ||
154 | } | ||
155 | |||
156 | *val = value; | ||
157 | |||
158 | out_be32(hose->cfg_addr, 0); | ||
159 | mb(); | ||
160 | |||
161 | return PCIBIOS_SUCCESSFUL; | ||
162 | } | ||
163 | |||
164 | static int | ||
165 | mpc52xx_pci_write_config(struct pci_bus *bus, unsigned int devfn, | ||
166 | int offset, int len, u32 val) | ||
167 | { | ||
168 | struct pci_controller *hose = bus->sysdata; | ||
169 | u32 value, mask; | ||
170 | |||
171 | if (ppc_md.pci_exclude_device) | ||
172 | if (ppc_md.pci_exclude_device(bus->number, devfn)) | ||
173 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
174 | |||
175 | out_be32(hose->cfg_addr, | ||
176 | (1 << 31) | | ||
177 | ((bus->number - hose->bus_offset) << 16) | | ||
178 | (devfn << 8) | | ||
179 | (offset & 0xfc)); | ||
180 | mb(); | ||
181 | |||
182 | #if defined(CONFIG_PPC_MPC5200_BUGFIX) | ||
183 | if (bus->number != hose->bus_offset) { | ||
184 | /* workaround for the bug 435 of the MPC5200 (L25R); | ||
185 | * Don't do 32 bits config access during type-1 cycles */ | ||
186 | switch (len) { | ||
187 | case 1: | ||
188 | out_8(((u8 __iomem *)hose->cfg_data) + | ||
189 | (offset & 3), val); | ||
190 | break; | ||
191 | case 2: | ||
192 | out_le16(((u16 __iomem *)hose->cfg_data) + | ||
193 | ((offset>>1) & 1), val); | ||
194 | break; | ||
195 | |||
196 | default: | ||
197 | out_le16((u16 __iomem *)hose->cfg_data, | ||
198 | (u16)val); | ||
199 | out_le16(((u16 __iomem *)hose->cfg_data) + 1, | ||
200 | (u16)(val>>16)); | ||
201 | break; | ||
202 | } | ||
203 | } | ||
204 | else | ||
205 | #endif | ||
206 | { | ||
207 | if (len != 4) { | ||
208 | value = in_le32(hose->cfg_data); | ||
209 | |||
210 | offset = (offset & 0x3) << 3; | ||
211 | mask = (0xffffffff >> (32 - (len << 3))); | ||
212 | mask <<= offset; | ||
213 | |||
214 | value &= ~mask; | ||
215 | val = value | ((val << offset) & mask); | ||
216 | } | ||
217 | |||
218 | out_le32(hose->cfg_data, val); | ||
219 | } | ||
220 | mb(); | ||
221 | |||
222 | out_be32(hose->cfg_addr, 0); | ||
223 | mb(); | ||
224 | |||
225 | return PCIBIOS_SUCCESSFUL; | ||
226 | } | ||
227 | |||
228 | static struct pci_ops mpc52xx_pci_ops = { | ||
229 | .read = mpc52xx_pci_read_config, | ||
230 | .write = mpc52xx_pci_write_config | ||
231 | }; | ||
232 | |||
233 | |||
234 | /* ======================================================================== */ | ||
235 | /* PCI setup */ | ||
236 | /* ======================================================================== */ | ||
237 | |||
238 | static void __init | ||
239 | mpc52xx_pci_setup(struct pci_controller *hose, | ||
240 | struct mpc52xx_pci __iomem *pci_regs) | ||
241 | { | ||
242 | struct resource *res; | ||
243 | u32 tmp; | ||
244 | int iwcr0 = 0, iwcr1 = 0, iwcr2 = 0; | ||
245 | |||
246 | pr_debug("mpc52xx_pci_setup(hose=%p, pci_regs=%p)\n", hose, pci_regs); | ||
247 | |||
248 | /* pci_process_bridge_OF_ranges() found all our addresses for us; | ||
249 | * now store them in the right places */ | ||
250 | hose->cfg_addr = &pci_regs->car; | ||
251 | hose->cfg_data = hose->io_base_virt; | ||
252 | |||
253 | /* Control regs */ | ||
254 | tmp = in_be32(&pci_regs->scr); | ||
255 | tmp |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY; | ||
256 | out_be32(&pci_regs->scr, tmp); | ||
257 | |||
258 | /* Memory windows */ | ||
259 | res = &hose->mem_resources[0]; | ||
260 | if (res->flags) { | ||
261 | pr_debug("mem_resource[0] = {.start=%x, .end=%x, .flags=%lx}\n", | ||
262 | res->start, res->end, res->flags); | ||
263 | out_be32(&pci_regs->iw0btar, | ||
264 | MPC52xx_PCI_IWBTAR_TRANSLATION(res->start, res->start, | ||
265 | res->end - res->start + 1)); | ||
266 | iwcr0 = MPC52xx_PCI_IWCR_ENABLE | MPC52xx_PCI_IWCR_MEM; | ||
267 | if (res->flags & IORESOURCE_PREFETCH) | ||
268 | iwcr0 |= MPC52xx_PCI_IWCR_READ_MULTI; | ||
269 | else | ||
270 | iwcr0 |= MPC52xx_PCI_IWCR_READ; | ||
271 | } | ||
272 | |||
273 | res = &hose->mem_resources[1]; | ||
274 | if (res->flags) { | ||
275 | pr_debug("mem_resource[1] = {.start=%x, .end=%x, .flags=%lx}\n", | ||
276 | res->start, res->end, res->flags); | ||
277 | out_be32(&pci_regs->iw1btar, | ||
278 | MPC52xx_PCI_IWBTAR_TRANSLATION(res->start, res->start, | ||
279 | res->end - res->start + 1)); | ||
280 | iwcr1 = MPC52xx_PCI_IWCR_ENABLE | MPC52xx_PCI_IWCR_MEM; | ||
281 | if (res->flags & IORESOURCE_PREFETCH) | ||
282 | iwcr1 |= MPC52xx_PCI_IWCR_READ_MULTI; | ||
283 | else | ||
284 | iwcr1 |= MPC52xx_PCI_IWCR_READ; | ||
285 | } | ||
286 | |||
287 | /* IO resources */ | ||
288 | res = &hose->io_resource; | ||
289 | if (!res) { | ||
290 | printk(KERN_ERR "%s: Didn't find IO resources\n", __FILE__); | ||
291 | return; | ||
292 | } | ||
293 | pr_debug(".io_resource={.start=%x,.end=%x,.flags=%lx} " | ||
294 | ".io_base_phys=0x%p\n", | ||
295 | res->start, res->end, res->flags, (void*)hose->io_base_phys); | ||
296 | out_be32(&pci_regs->iw2btar, | ||
297 | MPC52xx_PCI_IWBTAR_TRANSLATION(hose->io_base_phys, | ||
298 | res->start, | ||
299 | res->end - res->start + 1)); | ||
300 | iwcr2 = MPC52xx_PCI_IWCR_ENABLE | MPC52xx_PCI_IWCR_IO; | ||
301 | |||
302 | /* Set all the IWCR fields at once; they're in the same reg */ | ||
303 | out_be32(&pci_regs->iwcr, MPC52xx_PCI_IWCR_PACK(iwcr0, iwcr1, iwcr2)); | ||
304 | |||
305 | out_be32(&pci_regs->tbatr0, | ||
306 | MPC52xx_PCI_TBATR_ENABLE | MPC52xx_PCI_TARGET_IO ); | ||
307 | out_be32(&pci_regs->tbatr1, | ||
308 | MPC52xx_PCI_TBATR_ENABLE | MPC52xx_PCI_TARGET_MEM ); | ||
309 | |||
310 | out_be32(&pci_regs->tcr, MPC52xx_PCI_TCR_LD); | ||
311 | |||
312 | tmp = in_be32(&pci_regs->gscr); | ||
313 | #if 0 | ||
314 | /* Reset the exteral bus ( internal PCI controller is NOT resetted ) */ | ||
315 | /* Not necessary and can be a bad thing if for example the bootloader | ||
316 | is displaying a splash screen or ... Just left here for | ||
317 | documentation purpose if anyone need it */ | ||
318 | out_be32(&pci_regs->gscr, tmp | MPC52xx_PCI_GSCR_PR); | ||
319 | udelay(50); | ||
320 | #endif | ||
321 | |||
322 | /* Make sure the PCI bridge is out of reset */ | ||
323 | out_be32(&pci_regs->gscr, tmp & ~MPC52xx_PCI_GSCR_PR); | ||
324 | } | ||
325 | |||
326 | static void | ||
327 | mpc52xx_pci_fixup_resources(struct pci_dev *dev) | ||
328 | { | ||
329 | int i; | ||
330 | |||
331 | pr_debug("mpc52xx_pci_fixup_resources() %.4x:%.4x\n", | ||
332 | dev->vendor, dev->device); | ||
333 | |||
334 | /* We don't rely on boot loader for PCI and resets all | ||
335 | devices */ | ||
336 | for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { | ||
337 | struct resource *res = &dev->resource[i]; | ||
338 | if (res->end > res->start) { /* Only valid resources */ | ||
339 | res->end -= res->start; | ||
340 | res->start = 0; | ||
341 | res->flags |= IORESOURCE_UNSET; | ||
342 | } | ||
343 | } | ||
344 | |||
345 | /* The PCI Host bridge of MPC52xx has a prefetch memory resource | ||
346 | fixed to 1Gb. Doesn't fit in the resource system so we remove it */ | ||
347 | if ( (dev->vendor == PCI_VENDOR_ID_MOTOROLA) && | ||
348 | ( dev->device == PCI_DEVICE_ID_MOTOROLA_MPC5200 | ||
349 | || dev->device == PCI_DEVICE_ID_MOTOROLA_MPC5200B) ) { | ||
350 | struct resource *res = &dev->resource[1]; | ||
351 | res->start = res->end = res->flags = 0; | ||
352 | } | ||
353 | } | ||
354 | |||
355 | int __init | ||
356 | mpc52xx_add_bridge(struct device_node *node) | ||
357 | { | ||
358 | int len; | ||
359 | struct mpc52xx_pci __iomem *pci_regs; | ||
360 | struct pci_controller *hose; | ||
361 | const int *bus_range; | ||
362 | struct resource rsrc; | ||
363 | |||
364 | pr_debug("Adding MPC52xx PCI host bridge %s\n", node->full_name); | ||
365 | |||
366 | pci_assign_all_buses = 1; | ||
367 | |||
368 | if (of_address_to_resource(node, 0, &rsrc) != 0) { | ||
369 | printk(KERN_ERR "Can't get %s resources\n", node->full_name); | ||
370 | return -EINVAL; | ||
371 | } | ||
372 | |||
373 | bus_range = get_property(node, "bus-range", &len); | ||
374 | if (bus_range == NULL || len < 2 * sizeof(int)) { | ||
375 | printk(KERN_WARNING "Can't get %s bus-range, assume bus 0\n", | ||
376 | node->full_name); | ||
377 | bus_range = NULL; | ||
378 | } | ||
379 | |||
380 | /* There are some PCI quirks on the 52xx, register the hook to | ||
381 | * fix them. */ | ||
382 | ppc_md.pcibios_fixup_resources = mpc52xx_pci_fixup_resources; | ||
383 | |||
384 | /* Alloc and initialize the pci controller. Values in the device | ||
385 | * tree are needed to configure the 52xx PCI controller. Rather | ||
386 | * than parse the tree here, let pci_process_bridge_OF_ranges() | ||
387 | * do it for us and extract the values after the fact */ | ||
388 | hose = pcibios_alloc_controller(); | ||
389 | if (!hose) | ||
390 | return -ENOMEM; | ||
391 | |||
392 | hose->arch_data = node; | ||
393 | hose->set_cfg_type = 1; | ||
394 | |||
395 | hose->first_busno = bus_range ? bus_range[0] : 0; | ||
396 | hose->last_busno = bus_range ? bus_range[1] : 0xff; | ||
397 | |||
398 | hose->bus_offset = 0; | ||
399 | hose->ops = &mpc52xx_pci_ops; | ||
400 | |||
401 | pci_regs = ioremap(rsrc.start, rsrc.end - rsrc.start + 1); | ||
402 | if (!pci_regs) | ||
403 | return -ENOMEM; | ||
404 | |||
405 | pci_process_bridge_OF_ranges(hose, node, 1); | ||
406 | |||
407 | /* Finish setting up PCI using values obtained by | ||
408 | * pci_proces_bridge_OF_ranges */ | ||
409 | mpc52xx_pci_setup(hose, pci_regs); | ||
410 | |||
411 | return 0; | ||
412 | } | ||
diff --git a/arch/powerpc/platforms/82xx/mpc82xx.c b/arch/powerpc/platforms/82xx/mpc82xx.c index 0f5b30dc60da..74e7892cdfcf 100644 --- a/arch/powerpc/platforms/82xx/mpc82xx.c +++ b/arch/powerpc/platforms/82xx/mpc82xx.c | |||
@@ -50,7 +50,7 @@ | |||
50 | #include <sysdev/fsl_soc.h> | 50 | #include <sysdev/fsl_soc.h> |
51 | #include <sysdev/cpm2_pic.h> | 51 | #include <sysdev/cpm2_pic.h> |
52 | 52 | ||
53 | #include "pq2ads_pd.h" | 53 | #include "pq2ads.h" |
54 | 54 | ||
55 | static int __init get_freq(char *name, unsigned long *val) | 55 | static int __init get_freq(char *name, unsigned long *val) |
56 | { | 56 | { |
diff --git a/arch/powerpc/platforms/82xx/mpc82xx_ads.c b/arch/powerpc/platforms/82xx/mpc82xx_ads.c index ea880f1f0dcd..7334c1a15b90 100644 --- a/arch/powerpc/platforms/82xx/mpc82xx_ads.c +++ b/arch/powerpc/platforms/82xx/mpc82xx_ads.c | |||
@@ -51,7 +51,7 @@ | |||
51 | #include <sysdev/fsl_soc.h> | 51 | #include <sysdev/fsl_soc.h> |
52 | #include <../sysdev/cpm2_pic.h> | 52 | #include <../sysdev/cpm2_pic.h> |
53 | 53 | ||
54 | #include "pq2ads_pd.h" | 54 | #include "pq2ads.h" |
55 | 55 | ||
56 | #ifdef CONFIG_PCI | 56 | #ifdef CONFIG_PCI |
57 | static uint pci_clk_frq; | 57 | static uint pci_clk_frq; |
diff --git a/arch/powerpc/platforms/82xx/pq2ads.h b/arch/powerpc/platforms/82xx/pq2ads.h index fb2f92bcd770..5b5cca6c8c88 100644 --- a/arch/powerpc/platforms/82xx/pq2ads.h +++ b/arch/powerpc/platforms/82xx/pq2ads.h | |||
@@ -22,6 +22,7 @@ | |||
22 | #ifndef __MACH_ADS8260_DEFS | 22 | #ifndef __MACH_ADS8260_DEFS |
23 | #define __MACH_ADS8260_DEFS | 23 | #define __MACH_ADS8260_DEFS |
24 | 24 | ||
25 | #include <linux/seq_file.h> | ||
25 | #include <asm/ppcboot.h> | 26 | #include <asm/ppcboot.h> |
26 | 27 | ||
27 | /* For our show_cpuinfo hooks. */ | 28 | /* For our show_cpuinfo hooks. */ |
@@ -46,12 +47,12 @@ | |||
46 | #define BCSR1_RS232_EN1 ((uint)0x02000000) /* 0 ==enable */ | 47 | #define BCSR1_RS232_EN1 ((uint)0x02000000) /* 0 ==enable */ |
47 | #define BCSR1_RS232_EN2 ((uint)0x01000000) /* 0 ==enable */ | 48 | #define BCSR1_RS232_EN2 ((uint)0x01000000) /* 0 ==enable */ |
48 | #define BCSR3_FETHIEN2 ((uint)0x10000000) /* 0 == enable*/ | 49 | #define BCSR3_FETHIEN2 ((uint)0x10000000) /* 0 == enable*/ |
49 | #define BCSR3_FETH2_RS ((uint)0x80000000) /* 0 == reset */ | 50 | #define BCSR3_FETH2_RST ((uint)0x80000000) /* 0 == reset */ |
50 | 51 | ||
51 | /* cpm serial driver works with constants below */ | 52 | /* cpm serial driver works with constants below */ |
52 | 53 | ||
53 | #define SIU_INT_SMC1 ((uint)0x04+CPM_IRQ_OFFSET) | 54 | #define SIU_INT_SMC1 ((uint)0x04+CPM_IRQ_OFFSET) |
54 | #define SIU_INT_SMC2i ((uint)0x05+CPM_IRQ_OFFSET) | 55 | #define SIU_INT_SMC2 ((uint)0x05+CPM_IRQ_OFFSET) |
55 | #define SIU_INT_SCC1 ((uint)0x28+CPM_IRQ_OFFSET) | 56 | #define SIU_INT_SCC1 ((uint)0x28+CPM_IRQ_OFFSET) |
56 | #define SIU_INT_SCC2 ((uint)0x29+CPM_IRQ_OFFSET) | 57 | #define SIU_INT_SCC2 ((uint)0x29+CPM_IRQ_OFFSET) |
57 | #define SIU_INT_SCC3 ((uint)0x2a+CPM_IRQ_OFFSET) | 58 | #define SIU_INT_SCC3 ((uint)0x2a+CPM_IRQ_OFFSET) |
diff --git a/arch/powerpc/platforms/83xx/misc.c b/arch/powerpc/platforms/83xx/misc.c index f0c6df61faa9..f01806c940e1 100644 --- a/arch/powerpc/platforms/83xx/misc.c +++ b/arch/powerpc/platforms/83xx/misc.c | |||
@@ -18,23 +18,36 @@ | |||
18 | 18 | ||
19 | #include "mpc83xx.h" | 19 | #include "mpc83xx.h" |
20 | 20 | ||
21 | static __be32 __iomem *restart_reg_base; | ||
22 | |||
23 | static int __init mpc83xx_restart_init(void) | ||
24 | { | ||
25 | /* map reset restart_reg_baseister space */ | ||
26 | restart_reg_base = ioremap(get_immrbase() + 0x900, 0xff); | ||
27 | |||
28 | return 0; | ||
29 | } | ||
30 | |||
31 | arch_initcall(mpc83xx_restart_init); | ||
32 | |||
21 | void mpc83xx_restart(char *cmd) | 33 | void mpc83xx_restart(char *cmd) |
22 | { | 34 | { |
23 | #define RST_OFFSET 0x00000900 | 35 | #define RST_OFFSET 0x00000900 |
24 | #define RST_PROT_REG 0x00000018 | 36 | #define RST_PROT_REG 0x00000018 |
25 | #define RST_CTRL_REG 0x0000001c | 37 | #define RST_CTRL_REG 0x0000001c |
26 | __be32 __iomem *reg; | ||
27 | |||
28 | /* map reset register space */ | ||
29 | reg = ioremap(get_immrbase() + 0x900, 0xff); | ||
30 | 38 | ||
31 | local_irq_disable(); | 39 | local_irq_disable(); |
32 | 40 | ||
33 | /* enable software reset "RSTE" */ | 41 | if (restart_reg_base) { |
34 | out_be32(reg + (RST_PROT_REG >> 2), 0x52535445); | 42 | /* enable software reset "RSTE" */ |
43 | out_be32(restart_reg_base + (RST_PROT_REG >> 2), 0x52535445); | ||
44 | |||
45 | /* set software hard reset */ | ||
46 | out_be32(restart_reg_base + (RST_CTRL_REG >> 2), 0x2); | ||
47 | } else { | ||
48 | printk (KERN_EMERG "Error: Restart registers not mapped, spinning!\n"); | ||
49 | } | ||
35 | 50 | ||
36 | /* set software hard reset */ | ||
37 | out_be32(reg + (RST_CTRL_REG >> 2), 0x2); | ||
38 | for (;;) ; | 51 | for (;;) ; |
39 | } | 52 | } |
40 | 53 | ||
diff --git a/arch/powerpc/platforms/83xx/mpc832x_mds.c b/arch/powerpc/platforms/83xx/mpc832x_mds.c index 4d471190be8d..3ecb55f8a6e2 100644 --- a/arch/powerpc/platforms/83xx/mpc832x_mds.c +++ b/arch/powerpc/platforms/83xx/mpc832x_mds.c | |||
@@ -25,6 +25,7 @@ | |||
25 | #include <linux/initrd.h> | 25 | #include <linux/initrd.h> |
26 | 26 | ||
27 | #include <asm/of_device.h> | 27 | #include <asm/of_device.h> |
28 | #include <asm/of_platform.h> | ||
28 | #include <asm/system.h> | 29 | #include <asm/system.h> |
29 | #include <asm/atomic.h> | 30 | #include <asm/atomic.h> |
30 | #include <asm/time.h> | 31 | #include <asm/time.h> |
@@ -153,7 +154,7 @@ static int __init mpc832x_declare_of_platform_devices(void) | |||
153 | } | 154 | } |
154 | device_initcall(mpc832x_declare_of_platform_devices); | 155 | device_initcall(mpc832x_declare_of_platform_devices); |
155 | 156 | ||
156 | void __init mpc832x_sys_init_IRQ(void) | 157 | static void __init mpc832x_sys_init_IRQ(void) |
157 | { | 158 | { |
158 | 159 | ||
159 | struct device_node *np; | 160 | struct device_node *np; |
diff --git a/arch/powerpc/platforms/83xx/mpc834x_itx.c b/arch/powerpc/platforms/83xx/mpc834x_itx.c index 314c42ac6048..2446dea9407e 100644 --- a/arch/powerpc/platforms/83xx/mpc834x_itx.c +++ b/arch/powerpc/platforms/83xx/mpc834x_itx.c | |||
@@ -81,7 +81,7 @@ static void __init mpc834x_itx_setup_arch(void) | |||
81 | #endif | 81 | #endif |
82 | } | 82 | } |
83 | 83 | ||
84 | void __init mpc834x_itx_init_IRQ(void) | 84 | static void __init mpc834x_itx_init_IRQ(void) |
85 | { | 85 | { |
86 | struct device_node *np; | 86 | struct device_node *np; |
87 | 87 | ||
diff --git a/arch/powerpc/platforms/83xx/mpc834x_sys.c b/arch/powerpc/platforms/83xx/mpc834x_sys.c index 80b735a414d9..f30393f0b832 100644 --- a/arch/powerpc/platforms/83xx/mpc834x_sys.c +++ b/arch/powerpc/platforms/83xx/mpc834x_sys.c | |||
@@ -79,7 +79,7 @@ static void __init mpc834x_sys_setup_arch(void) | |||
79 | #endif | 79 | #endif |
80 | } | 80 | } |
81 | 81 | ||
82 | void __init mpc834x_sys_init_IRQ(void) | 82 | static void __init mpc834x_sys_init_IRQ(void) |
83 | { | 83 | { |
84 | struct device_node *np; | 84 | struct device_node *np; |
85 | 85 | ||
diff --git a/arch/powerpc/platforms/83xx/mpc8360e_pb.c b/arch/powerpc/platforms/83xx/mpc8360e_pb.c index 53b92a904e8e..ccce2f9f283d 100644 --- a/arch/powerpc/platforms/83xx/mpc8360e_pb.c +++ b/arch/powerpc/platforms/83xx/mpc8360e_pb.c | |||
@@ -31,6 +31,7 @@ | |||
31 | #include <linux/initrd.h> | 31 | #include <linux/initrd.h> |
32 | 32 | ||
33 | #include <asm/of_device.h> | 33 | #include <asm/of_device.h> |
34 | #include <asm/of_platform.h> | ||
34 | #include <asm/system.h> | 35 | #include <asm/system.h> |
35 | #include <asm/atomic.h> | 36 | #include <asm/atomic.h> |
36 | #include <asm/time.h> | 37 | #include <asm/time.h> |
@@ -158,7 +159,7 @@ static int __init mpc8360_declare_of_platform_devices(void) | |||
158 | } | 159 | } |
159 | device_initcall(mpc8360_declare_of_platform_devices); | 160 | device_initcall(mpc8360_declare_of_platform_devices); |
160 | 161 | ||
161 | void __init mpc8360_sys_init_IRQ(void) | 162 | static void __init mpc8360_sys_init_IRQ(void) |
162 | { | 163 | { |
163 | 164 | ||
164 | struct device_node *np; | 165 | struct device_node *np; |
diff --git a/arch/powerpc/platforms/86xx/Kconfig b/arch/powerpc/platforms/86xx/Kconfig index d1ecc0f9ab58..0c70944d0e37 100644 --- a/arch/powerpc/platforms/86xx/Kconfig +++ b/arch/powerpc/platforms/86xx/Kconfig | |||
@@ -8,6 +8,7 @@ choice | |||
8 | config MPC8641_HPCN | 8 | config MPC8641_HPCN |
9 | bool "Freescale MPC8641 HPCN" | 9 | bool "Freescale MPC8641 HPCN" |
10 | select PPC_I8259 | 10 | select PPC_I8259 |
11 | select DEFAULT_UIMAGE | ||
11 | help | 12 | help |
12 | This option enables support for the MPC8641 HPCN board. | 13 | This option enables support for the MPC8641 HPCN board. |
13 | 14 | ||
diff --git a/arch/powerpc/platforms/86xx/mpc86xx_smp.c b/arch/powerpc/platforms/86xx/mpc86xx_smp.c index bb7fb41933ad..7ef0c6854799 100644 --- a/arch/powerpc/platforms/86xx/mpc86xx_smp.c +++ b/arch/powerpc/platforms/86xx/mpc86xx_smp.c | |||
@@ -65,7 +65,6 @@ smp_86xx_kick_cpu(int nr) | |||
65 | pr_debug("smp_86xx_kick_cpu: kick CPU #%d\n", nr); | 65 | pr_debug("smp_86xx_kick_cpu: kick CPU #%d\n", nr); |
66 | 66 | ||
67 | local_irq_save(flags); | 67 | local_irq_save(flags); |
68 | local_irq_disable(); | ||
69 | 68 | ||
70 | /* Save reset vector */ | 69 | /* Save reset vector */ |
71 | save_vector = *vector; | 70 | save_vector = *vector; |
diff --git a/arch/powerpc/platforms/8xx/Kconfig b/arch/powerpc/platforms/8xx/Kconfig index c8c0ba3cf8e8..beea6834bb7e 100644 --- a/arch/powerpc/platforms/8xx/Kconfig +++ b/arch/powerpc/platforms/8xx/Kconfig | |||
@@ -1,105 +1,16 @@ | |||
1 | menu "Platform support" | ||
2 | depends on PPC_8xx | ||
3 | |||
1 | config FADS | 4 | config FADS |
2 | bool | 5 | bool |
3 | 6 | ||
7 | config CPM1 | ||
8 | bool | ||
9 | |||
4 | choice | 10 | choice |
5 | prompt "8xx Machine Type" | 11 | prompt "8xx Machine Type" |
6 | depends on 8xx | 12 | depends on 8xx |
7 | default RPXLITE | 13 | default MPC885ADS |
8 | |||
9 | config RPXLITE | ||
10 | bool "RPX-Lite" | ||
11 | ---help--- | ||
12 | Single-board computers based around the PowerPC MPC8xx chips and | ||
13 | intended for embedded applications. The following types are | ||
14 | supported: | ||
15 | |||
16 | RPX-Lite: | ||
17 | Embedded Planet RPX Lite. PC104 form-factor SBC based on the MPC823. | ||
18 | |||
19 | RPX-Classic: | ||
20 | Embedded Planet RPX Classic Low-fat. Credit-card-size SBC based on | ||
21 | the MPC 860 | ||
22 | |||
23 | BSE-IP: | ||
24 | Bright Star Engineering ip-Engine. | ||
25 | |||
26 | TQM823L: | ||
27 | TQM850L: | ||
28 | TQM855L: | ||
29 | TQM860L: | ||
30 | MPC8xx based family of mini modules, half credit card size, | ||
31 | up to 64 MB of RAM, 8 MB Flash, (Fast) Ethernet, 2 x serial ports, | ||
32 | 2 x CAN bus interface, ... | ||
33 | Manufacturer: TQ Components, www.tq-group.de | ||
34 | Date of Release: October (?) 1999 | ||
35 | End of Life: not yet :-) | ||
36 | URL: | ||
37 | - module: <http://www.denx.de/PDF/TQM8xxLHWM201.pdf> | ||
38 | - starter kit: <http://www.denx.de/PDF/STK8xxLHWM201.pdf> | ||
39 | - images: <http://www.denx.de/embedded-ppc-en.html> | ||
40 | |||
41 | FPS850L: | ||
42 | FingerPrint Sensor System (based on TQM850L) | ||
43 | Manufacturer: IKENDI AG, <http://www.ikendi.com/> | ||
44 | Date of Release: November 1999 | ||
45 | End of life: end 2000 ? | ||
46 | URL: see TQM850L | ||
47 | |||
48 | IVMS8: | ||
49 | MPC860 based board used in the "Integrated Voice Mail System", | ||
50 | Small Version (8 voice channels) | ||
51 | Manufacturer: Speech Design, <http://www.speech-design.de/> | ||
52 | Date of Release: December 2000 (?) | ||
53 | End of life: - | ||
54 | URL: <http://www.speech-design.de/> | ||
55 | |||
56 | IVML24: | ||
57 | MPC860 based board used in the "Integrated Voice Mail System", | ||
58 | Large Version (24 voice channels) | ||
59 | Manufacturer: Speech Design, <http://www.speech-design.de/> | ||
60 | Date of Release: March 2001 (?) | ||
61 | End of life: - | ||
62 | URL: <http://www.speech-design.de/> | ||
63 | |||
64 | HERMES: | ||
65 | Hermes-Pro ISDN/LAN router with integrated 8 x hub | ||
66 | Manufacturer: Multidata Gesellschaft fur Datentechnik und Informatik | ||
67 | <http://www.multidata.de/> | ||
68 | Date of Release: 2000 (?) | ||
69 | End of life: - | ||
70 | URL: <http://www.multidata.de/english/products/hpro.htm> | ||
71 | |||
72 | IP860: | ||
73 | VMEBus IP (Industry Pack) carrier board with MPC860 | ||
74 | Manufacturer: MicroSys GmbH, <http://www.microsys.de/> | ||
75 | Date of Release: ? | ||
76 | End of life: - | ||
77 | URL: <http://www.microsys.de/html/ip860.html> | ||
78 | |||
79 | PCU_E: | ||
80 | PCU = Peripheral Controller Unit, Extended | ||
81 | Manufacturer: Siemens AG, ICN (Information and Communication Networks) | ||
82 | <http://www.siemens.de/page/1,3771,224315-1-999_2_226207-0,00.html> | ||
83 | Date of Release: April 2001 | ||
84 | End of life: August 2001 | ||
85 | URL: n. a. | ||
86 | |||
87 | config RPXCLASSIC | ||
88 | bool "RPX-Classic" | ||
89 | help | ||
90 | The RPX-Classic is a single-board computer based on the Motorola | ||
91 | MPC860. It features 16MB of DRAM and a variable amount of flash, | ||
92 | I2C EEPROM, thermal monitoring, a PCMCIA slot, a DIP switch and two | ||
93 | LEDs. Variants with Ethernet ports exist. Say Y here to support it | ||
94 | directly. | ||
95 | |||
96 | config BSEIP | ||
97 | bool "BSE-IP" | ||
98 | help | ||
99 | Say Y here to support the Bright Star Engineering ipEngine SBC. | ||
100 | This is a credit-card-sized device featuring a MPC823 processor, | ||
101 | 26MB DRAM, 4MB flash, Ethernet, a 16K-gate FPGA, USB, an LCD/video | ||
102 | controller, and two RS232 ports. | ||
103 | 14 | ||
104 | config MPC8XXFADS | 15 | config MPC8XXFADS |
105 | bool "FADS" | 16 | bool "FADS" |
@@ -107,110 +18,58 @@ config MPC8XXFADS | |||
107 | 18 | ||
108 | config MPC86XADS | 19 | config MPC86XADS |
109 | bool "MPC86XADS" | 20 | bool "MPC86XADS" |
21 | select CPM1 | ||
110 | help | 22 | help |
111 | MPC86x Application Development System by Freescale Semiconductor. | 23 | MPC86x Application Development System by Freescale Semiconductor. |
112 | The MPC86xADS is meant to serve as a platform for s/w and h/w | 24 | The MPC86xADS is meant to serve as a platform for s/w and h/w |
113 | development around the MPC86X processor families. | 25 | development around the MPC86X processor families. |
114 | select FADS | ||
115 | 26 | ||
116 | config MPC885ADS | 27 | config MPC885ADS |
117 | bool "MPC885ADS" | 28 | bool "MPC885ADS" |
29 | select CPM1 | ||
118 | help | 30 | help |
119 | Freescale Semiconductor MPC885 Application Development System (ADS). | 31 | Freescale Semiconductor MPC885 Application Development System (ADS). |
120 | Also known as DUET. | 32 | Also known as DUET. |
121 | The MPC885ADS is meant to serve as a platform for s/w and h/w | 33 | The MPC885ADS is meant to serve as a platform for s/w and h/w |
122 | development around the MPC885 processor family. | 34 | development around the MPC885 processor family. |
123 | 35 | ||
124 | config TQM823L | 36 | endchoice |
125 | bool "TQM823L" | ||
126 | help | ||
127 | Say Y here to support the TQM823L, one of an MPC8xx-based family of | ||
128 | mini SBCs (half credit-card size) from TQ Components first released | ||
129 | in late 1999. Technical references are at | ||
130 | <http://www.denx.de/PDF/TQM8xxLHWM201.pdf>, and | ||
131 | <http://www.denx.de/PDF/STK8xxLHWM201.pdf>, and an image at | ||
132 | <http://www.denx.de/embedded-ppc-en.html>. | ||
133 | |||
134 | config TQM850L | ||
135 | bool "TQM850L" | ||
136 | help | ||
137 | Say Y here to support the TQM850L, one of an MPC8xx-based family of | ||
138 | mini SBCs (half credit-card size) from TQ Components first released | ||
139 | in late 1999. Technical references are at | ||
140 | <http://www.denx.de/PDF/TQM8xxLHWM201.pdf>, and | ||
141 | <http://www.denx.de/PDF/STK8xxLHWM201.pdf>, and an image at | ||
142 | <http://www.denx.de/embedded-ppc-en.html>. | ||
143 | |||
144 | config TQM855L | ||
145 | bool "TQM855L" | ||
146 | help | ||
147 | Say Y here to support the TQM855L, one of an MPC8xx-based family of | ||
148 | mini SBCs (half credit-card size) from TQ Components first released | ||
149 | in late 1999. Technical references are at | ||
150 | <http://www.denx.de/PDF/TQM8xxLHWM201.pdf>, and | ||
151 | <http://www.denx.de/PDF/STK8xxLHWM201.pdf>, and an image at | ||
152 | <http://www.denx.de/embedded-ppc-en.html>. | ||
153 | |||
154 | config TQM860L | ||
155 | bool "TQM860L" | ||
156 | help | ||
157 | Say Y here to support the TQM860L, one of an MPC8xx-based family of | ||
158 | mini SBCs (half credit-card size) from TQ Components first released | ||
159 | in late 1999. Technical references are at | ||
160 | <http://www.denx.de/PDF/TQM8xxLHWM201.pdf>, and | ||
161 | <http://www.denx.de/PDF/STK8xxLHWM201.pdf>, and an image at | ||
162 | <http://www.denx.de/embedded-ppc-en.html>. | ||
163 | |||
164 | config FPS850L | ||
165 | bool "FPS850L" | ||
166 | |||
167 | config IVMS8 | ||
168 | bool "IVMS8" | ||
169 | help | ||
170 | Say Y here to support the Integrated Voice-Mail Small 8-channel SBC | ||
171 | from Speech Design, released March 2001. The manufacturer's website | ||
172 | is at <http://www.speech-design.de/>. | ||
173 | |||
174 | config IVML24 | ||
175 | bool "IVML24" | ||
176 | help | ||
177 | Say Y here to support the Integrated Voice-Mail Large 24-channel SBC | ||
178 | from Speech Design, released March 2001. The manufacturer's website | ||
179 | is at <http://www.speech-design.de/>. | ||
180 | |||
181 | config HERMES_PRO | ||
182 | bool "HERMES" | ||
183 | |||
184 | config IP860 | ||
185 | bool "IP860" | ||
186 | |||
187 | config LWMON | ||
188 | bool "LWMON" | ||
189 | |||
190 | config PCU_E | ||
191 | bool "PCU_E" | ||
192 | |||
193 | config CCM | ||
194 | bool "CCM" | ||
195 | |||
196 | config LANTEC | ||
197 | bool "LANTEC" | ||
198 | 37 | ||
199 | config MBX | 38 | menu "Freescale Ethernet driver platform-specific options" |
200 | bool "MBX" | 39 | depends on (FS_ENET && MPC885ADS) |
201 | help | 40 | |
202 | MBX is a line of Motorola single-board computer based around the | 41 | config MPC8xx_SECOND_ETH |
203 | MPC821 and MPC860 processors, and intended for embedded-controller | 42 | bool "Second Ethernet channel" |
204 | applications. Say Y here to support these boards directly. | 43 | depends on MPC885ADS |
44 | default y | ||
45 | help | ||
46 | This enables support for second Ethernet on MPC885ADS and MPC86xADS boards. | ||
47 | The latter will use SCC1, for 885ADS you can select it below. | ||
48 | |||
49 | choice | ||
50 | prompt "Second Ethernet channel" | ||
51 | depends on MPC8xx_SECOND_ETH | ||
52 | default MPC8xx_SECOND_ETH_FEC2 | ||
53 | |||
54 | config MPC8xx_SECOND_ETH_FEC2 | ||
55 | bool "FEC2" | ||
56 | depends on MPC885ADS | ||
57 | help | ||
58 | Enable FEC2 to serve as 2-nd Ethernet channel. Note that SMC2 | ||
59 | (often 2-nd UART) will not work if this is enabled. | ||
60 | |||
61 | config MPC8xx_SECOND_ETH_SCC3 | ||
62 | bool "SCC3" | ||
63 | depends on MPC885ADS | ||
64 | help | ||
65 | Enable SCC3 to serve as 2-nd Ethernet channel. Note that SMC1 | ||
66 | (often 1-nd UART) will not work if this is enabled. | ||
67 | |||
68 | endchoice | ||
205 | 69 | ||
206 | config WINCEPT | 70 | endmenu |
207 | bool "WinCept" | ||
208 | help | ||
209 | The Wincept 100/110 is a Motorola single-board computer based on the | ||
210 | MPC821 PowerPC, introduced in 1998 and designed to be used in | ||
211 | thin-client machines. Say Y to support it directly. | ||
212 | 71 | ||
213 | endchoice | 72 | endmenu |
214 | 73 | ||
215 | # | 74 | # |
216 | # MPC8xx Communication options | 75 | # MPC8xx Communication options |
@@ -219,79 +78,6 @@ endchoice | |||
219 | menu "MPC8xx CPM Options" | 78 | menu "MPC8xx CPM Options" |
220 | depends on 8xx | 79 | depends on 8xx |
221 | 80 | ||
222 | config SCC_ENET | ||
223 | bool "CPM SCC Ethernet" | ||
224 | depends on NET_ETHERNET | ||
225 | help | ||
226 | Enable Ethernet support via the Motorola MPC8xx serial | ||
227 | communications controller. | ||
228 | |||
229 | choice | ||
230 | prompt "SCC used for Ethernet" | ||
231 | depends on SCC_ENET | ||
232 | default SCC1_ENET | ||
233 | |||
234 | config SCC1_ENET | ||
235 | bool "SCC1" | ||
236 | help | ||
237 | Use MPC8xx serial communications controller 1 to drive Ethernet | ||
238 | (default). | ||
239 | |||
240 | config SCC2_ENET | ||
241 | bool "SCC2" | ||
242 | help | ||
243 | Use MPC8xx serial communications controller 2 to drive Ethernet. | ||
244 | |||
245 | config SCC3_ENET | ||
246 | bool "SCC3" | ||
247 | help | ||
248 | Use MPC8xx serial communications controller 3 to drive Ethernet. | ||
249 | |||
250 | endchoice | ||
251 | |||
252 | config FEC_ENET | ||
253 | bool "860T FEC Ethernet" | ||
254 | depends on NET_ETHERNET | ||
255 | help | ||
256 | Enable Ethernet support via the Fast Ethernet Controller (FCC) on | ||
257 | the Motorola MPC8260. | ||
258 | |||
259 | config USE_MDIO | ||
260 | bool "Use MDIO for PHY configuration" | ||
261 | depends on FEC_ENET | ||
262 | help | ||
263 | On some boards the hardware configuration of the ethernet PHY can be | ||
264 | used without any software interaction over the MDIO interface, so | ||
265 | all MII code can be omitted. Say N here if unsure or if you don't | ||
266 | need link status reports. | ||
267 | |||
268 | config FEC_AM79C874 | ||
269 | bool "Support AMD79C874 PHY" | ||
270 | depends on USE_MDIO | ||
271 | |||
272 | config FEC_LXT970 | ||
273 | bool "Support LXT970 PHY" | ||
274 | depends on USE_MDIO | ||
275 | |||
276 | config FEC_LXT971 | ||
277 | bool "Support LXT971 PHY" | ||
278 | depends on USE_MDIO | ||
279 | |||
280 | config FEC_QS6612 | ||
281 | bool "Support QS6612 PHY" | ||
282 | depends on USE_MDIO | ||
283 | |||
284 | config ENET_BIG_BUFFERS | ||
285 | bool "Use Big CPM Ethernet Buffers" | ||
286 | depends on SCC_ENET || FEC_ENET | ||
287 | help | ||
288 | Allocate large buffers for MPC8xx Ethernet. Increases throughput | ||
289 | and decreases the likelihood of dropped packets, but costs memory. | ||
290 | |||
291 | config HTDMSOUND | ||
292 | bool "Embedded Planet HIOX Audio" | ||
293 | depends on SOUND=y | ||
294 | |||
295 | # This doesn't really belong here, but it is convenient to ask | 81 | # This doesn't really belong here, but it is convenient to ask |
296 | # 8xx specific questions. | 82 | # 8xx specific questions. |
297 | comment "Generic MPC8xx Options" | 83 | comment "Generic MPC8xx Options" |
diff --git a/arch/powerpc/platforms/8xx/Makefile b/arch/powerpc/platforms/8xx/Makefile new file mode 100644 index 000000000000..5e2dae3afd2f --- /dev/null +++ b/arch/powerpc/platforms/8xx/Makefile | |||
@@ -0,0 +1,6 @@ | |||
1 | # | ||
2 | # Makefile for the PowerPC 8xx linux kernel. | ||
3 | # | ||
4 | obj-$(CONFIG_PPC_8xx) += m8xx_setup.o | ||
5 | obj-$(CONFIG_MPC885ADS) += mpc885ads_setup.o | ||
6 | obj-$(CONFIG_MPC86XADS) += mpc86xads_setup.o | ||
diff --git a/arch/powerpc/platforms/8xx/m8xx_setup.c b/arch/powerpc/platforms/8xx/m8xx_setup.c new file mode 100644 index 000000000000..9ed7125f0150 --- /dev/null +++ b/arch/powerpc/platforms/8xx/m8xx_setup.c | |||
@@ -0,0 +1,303 @@ | |||
1 | /* | ||
2 | * Copyright (C) 1995 Linus Torvalds | ||
3 | * Adapted from 'alpha' version by Gary Thomas | ||
4 | * Modified by Cort Dougan (cort@cs.nmt.edu) | ||
5 | * Modified for MBX using prep/chrp/pmac functions by Dan (dmalek@jlc.net) | ||
6 | * Further modified for generic 8xx by Dan. | ||
7 | */ | ||
8 | |||
9 | /* | ||
10 | * bootup setup stuff.. | ||
11 | */ | ||
12 | |||
13 | #include <linux/errno.h> | ||
14 | #include <linux/sched.h> | ||
15 | #include <linux/kernel.h> | ||
16 | #include <linux/mm.h> | ||
17 | #include <linux/stddef.h> | ||
18 | #include <linux/unistd.h> | ||
19 | #include <linux/ptrace.h> | ||
20 | #include <linux/slab.h> | ||
21 | #include <linux/user.h> | ||
22 | #include <linux/a.out.h> | ||
23 | #include <linux/tty.h> | ||
24 | #include <linux/major.h> | ||
25 | #include <linux/interrupt.h> | ||
26 | #include <linux/reboot.h> | ||
27 | #include <linux/init.h> | ||
28 | #include <linux/initrd.h> | ||
29 | #include <linux/ioport.h> | ||
30 | #include <linux/bootmem.h> | ||
31 | #include <linux/seq_file.h> | ||
32 | #include <linux/root_dev.h> | ||
33 | #include <linux/time.h> | ||
34 | #include <linux/rtc.h> | ||
35 | |||
36 | #include <asm/mmu.h> | ||
37 | #include <asm/reg.h> | ||
38 | #include <asm/residual.h> | ||
39 | #include <asm/io.h> | ||
40 | #include <asm/pgtable.h> | ||
41 | #include <asm/mpc8xx.h> | ||
42 | #include <asm/8xx_immap.h> | ||
43 | #include <asm/machdep.h> | ||
44 | #include <asm/bootinfo.h> | ||
45 | #include <asm/time.h> | ||
46 | #include <asm/prom.h> | ||
47 | #include <asm/fs_pd.h> | ||
48 | #include <mm/mmu_decl.h> | ||
49 | |||
50 | #include "sysdev/mpc8xx_pic.h" | ||
51 | |||
52 | void m8xx_calibrate_decr(void); | ||
53 | extern void m8xx_wdt_handler_install(bd_t *bp); | ||
54 | extern int cpm_pic_init(void); | ||
55 | extern int cpm_get_irq(void); | ||
56 | |||
57 | /* A place holder for time base interrupts, if they are ever enabled. */ | ||
58 | irqreturn_t timebase_interrupt(int irq, void * dev) | ||
59 | { | ||
60 | printk ("timebase_interrupt()\n"); | ||
61 | |||
62 | return IRQ_HANDLED; | ||
63 | } | ||
64 | |||
65 | static struct irqaction tbint_irqaction = { | ||
66 | .handler = timebase_interrupt, | ||
67 | .mask = CPU_MASK_NONE, | ||
68 | .name = "tbint", | ||
69 | }; | ||
70 | |||
71 | /* per-board overridable init_internal_rtc() function. */ | ||
72 | void __init __attribute__ ((weak)) | ||
73 | init_internal_rtc(void) | ||
74 | { | ||
75 | sit8xx_t *sys_tmr = (sit8xx_t *) immr_map(im_sit); | ||
76 | |||
77 | /* Disable the RTC one second and alarm interrupts. */ | ||
78 | clrbits16(&sys_tmr->sit_rtcsc, (RTCSC_SIE | RTCSC_ALE)); | ||
79 | |||
80 | /* Enable the RTC */ | ||
81 | setbits16(&sys_tmr->sit_rtcsc, (RTCSC_RTF | RTCSC_RTE)); | ||
82 | immr_unmap(sys_tmr); | ||
83 | } | ||
84 | |||
85 | static int __init get_freq(char *name, unsigned long *val) | ||
86 | { | ||
87 | struct device_node *cpu; | ||
88 | unsigned int *fp; | ||
89 | int found = 0; | ||
90 | |||
91 | /* The cpu node should have timebase and clock frequency properties */ | ||
92 | cpu = of_find_node_by_type(NULL, "cpu"); | ||
93 | |||
94 | if (cpu) { | ||
95 | fp = (unsigned int *)get_property(cpu, name, NULL); | ||
96 | if (fp) { | ||
97 | found = 1; | ||
98 | *val = *fp++; | ||
99 | } | ||
100 | |||
101 | of_node_put(cpu); | ||
102 | } | ||
103 | |||
104 | return found; | ||
105 | } | ||
106 | |||
107 | /* The decrementer counts at the system (internal) clock frequency divided by | ||
108 | * sixteen, or external oscillator divided by four. We force the processor | ||
109 | * to use system clock divided by sixteen. | ||
110 | */ | ||
111 | void __init mpc8xx_calibrate_decr(void) | ||
112 | { | ||
113 | struct device_node *cpu; | ||
114 | cark8xx_t *clk_r1; | ||
115 | car8xx_t *clk_r2; | ||
116 | sitk8xx_t *sys_tmr1; | ||
117 | sit8xx_t *sys_tmr2; | ||
118 | int irq, virq; | ||
119 | |||
120 | clk_r1 = (cark8xx_t *) immr_map(im_clkrstk); | ||
121 | |||
122 | /* Unlock the SCCR. */ | ||
123 | out_be32(&clk_r1->cark_sccrk, ~KAPWR_KEY); | ||
124 | out_be32(&clk_r1->cark_sccrk, KAPWR_KEY); | ||
125 | immr_unmap(clk_r1); | ||
126 | |||
127 | /* Force all 8xx processors to use divide by 16 processor clock. */ | ||
128 | clk_r2 = (car8xx_t *) immr_map(im_clkrst); | ||
129 | setbits32(&clk_r2->car_sccr, 0x02000000); | ||
130 | immr_unmap(clk_r2); | ||
131 | |||
132 | /* Processor frequency is MHz. | ||
133 | */ | ||
134 | ppc_tb_freq = 50000000; | ||
135 | if (!get_freq("bus-frequency", &ppc_tb_freq)) { | ||
136 | printk(KERN_ERR "WARNING: Estimating decrementer frequency " | ||
137 | "(not found)\n"); | ||
138 | } | ||
139 | ppc_tb_freq /= 16; | ||
140 | ppc_proc_freq = 50000000; | ||
141 | if (!get_freq("clock-frequency", &ppc_proc_freq)) | ||
142 | printk(KERN_ERR "WARNING: Estimating processor frequency" | ||
143 | "(not found)\n"); | ||
144 | |||
145 | printk("Decrementer Frequency = 0x%lx\n", ppc_tb_freq); | ||
146 | |||
147 | /* Perform some more timer/timebase initialization. This used | ||
148 | * to be done elsewhere, but other changes caused it to get | ||
149 | * called more than once....that is a bad thing. | ||
150 | * | ||
151 | * First, unlock all of the registers we are going to modify. | ||
152 | * To protect them from corruption during power down, registers | ||
153 | * that are maintained by keep alive power are "locked". To | ||
154 | * modify these registers we have to write the key value to | ||
155 | * the key location associated with the register. | ||
156 | * Some boards power up with these unlocked, while others | ||
157 | * are locked. Writing anything (including the unlock code?) | ||
158 | * to the unlocked registers will lock them again. So, here | ||
159 | * we guarantee the registers are locked, then we unlock them | ||
160 | * for our use. | ||
161 | */ | ||
162 | sys_tmr1 = (sitk8xx_t *) immr_map(im_sitk); | ||
163 | out_be32(&sys_tmr1->sitk_tbscrk, ~KAPWR_KEY); | ||
164 | out_be32(&sys_tmr1->sitk_rtcsck, ~KAPWR_KEY); | ||
165 | out_be32(&sys_tmr1->sitk_tbk, ~KAPWR_KEY); | ||
166 | out_be32(&sys_tmr1->sitk_tbscrk, KAPWR_KEY); | ||
167 | out_be32(&sys_tmr1->sitk_rtcsck, KAPWR_KEY); | ||
168 | out_be32(&sys_tmr1->sitk_tbk, KAPWR_KEY); | ||
169 | immr_unmap(sys_tmr1); | ||
170 | |||
171 | init_internal_rtc(); | ||
172 | |||
173 | /* Enabling the decrementer also enables the timebase interrupts | ||
174 | * (or from the other point of view, to get decrementer interrupts | ||
175 | * we have to enable the timebase). The decrementer interrupt | ||
176 | * is wired into the vector table, nothing to do here for that. | ||
177 | */ | ||
178 | cpu = of_find_node_by_type(NULL, "cpu"); | ||
179 | virq= irq_of_parse_and_map(cpu, 0); | ||
180 | irq = irq_map[virq].hwirq; | ||
181 | |||
182 | sys_tmr2 = (sit8xx_t *) immr_map(im_sit); | ||
183 | out_be16(&sys_tmr2->sit_tbscr, ((1 << (7 - (irq/2))) << 8) | | ||
184 | (TBSCR_TBF | TBSCR_TBE)); | ||
185 | immr_unmap(sys_tmr2); | ||
186 | |||
187 | if (setup_irq(virq, &tbint_irqaction)) | ||
188 | panic("Could not allocate timer IRQ!"); | ||
189 | |||
190 | #ifdef CONFIG_8xx_WDT | ||
191 | /* Install watchdog timer handler early because it might be | ||
192 | * already enabled by the bootloader | ||
193 | */ | ||
194 | m8xx_wdt_handler_install(binfo); | ||
195 | #endif | ||
196 | } | ||
197 | |||
198 | /* The RTC on the MPC8xx is an internal register. | ||
199 | * We want to protect this during power down, so we need to unlock, | ||
200 | * modify, and re-lock. | ||
201 | */ | ||
202 | |||
203 | int mpc8xx_set_rtc_time(struct rtc_time *tm) | ||
204 | { | ||
205 | sitk8xx_t *sys_tmr1; | ||
206 | sit8xx_t *sys_tmr2; | ||
207 | int time; | ||
208 | |||
209 | sys_tmr1 = (sitk8xx_t *) immr_map(im_sitk); | ||
210 | sys_tmr2 = (sit8xx_t *) immr_map(im_sit); | ||
211 | time = mktime(tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, | ||
212 | tm->tm_hour, tm->tm_min, tm->tm_sec); | ||
213 | |||
214 | out_be32(&sys_tmr1->sitk_rtck, KAPWR_KEY); | ||
215 | out_be32(&sys_tmr2->sit_rtc, time); | ||
216 | out_be32(&sys_tmr1->sitk_rtck, ~KAPWR_KEY); | ||
217 | |||
218 | immr_unmap(sys_tmr2); | ||
219 | immr_unmap(sys_tmr1); | ||
220 | return 0; | ||
221 | } | ||
222 | |||
223 | void mpc8xx_get_rtc_time(struct rtc_time *tm) | ||
224 | { | ||
225 | unsigned long data; | ||
226 | sit8xx_t *sys_tmr = (sit8xx_t *) immr_map(im_sit); | ||
227 | |||
228 | /* Get time from the RTC. */ | ||
229 | data = in_be32(&sys_tmr->sit_rtc); | ||
230 | to_tm(data, tm); | ||
231 | tm->tm_year -= 1900; | ||
232 | tm->tm_mon -= 1; | ||
233 | immr_unmap(sys_tmr); | ||
234 | return; | ||
235 | } | ||
236 | |||
237 | void mpc8xx_restart(char *cmd) | ||
238 | { | ||
239 | __volatile__ unsigned char dummy; | ||
240 | car8xx_t * clk_r = (car8xx_t *) immr_map(im_clkrst); | ||
241 | |||
242 | |||
243 | local_irq_disable(); | ||
244 | |||
245 | setbits32(&clk_r->car_plprcr, 0x00000080); | ||
246 | /* Clear the ME bit in MSR to cause checkstop on machine check | ||
247 | */ | ||
248 | mtmsr(mfmsr() & ~0x1000); | ||
249 | |||
250 | dummy = in_8(&clk_r->res[0]); | ||
251 | printk("Restart failed\n"); | ||
252 | while(1); | ||
253 | } | ||
254 | |||
255 | void mpc8xx_show_cpuinfo(struct seq_file *m) | ||
256 | { | ||
257 | struct device_node *root; | ||
258 | uint memsize = total_memory; | ||
259 | const char *model = ""; | ||
260 | |||
261 | seq_printf(m, "Vendor\t\t: Freescale Semiconductor\n"); | ||
262 | |||
263 | root = of_find_node_by_path("/"); | ||
264 | if (root) | ||
265 | model = get_property(root, "model", NULL); | ||
266 | seq_printf(m, "Machine\t\t: %s\n", model); | ||
267 | of_node_put(root); | ||
268 | |||
269 | seq_printf(m, "Memory\t\t: %d MB\n", memsize / (1024 * 1024)); | ||
270 | } | ||
271 | |||
272 | static void cpm_cascade(unsigned int irq, struct irq_desc *desc) | ||
273 | { | ||
274 | int cascade_irq; | ||
275 | |||
276 | if ((cascade_irq = cpm_get_irq()) >= 0) { | ||
277 | struct irq_desc *cdesc = irq_desc + cascade_irq; | ||
278 | |||
279 | generic_handle_irq(cascade_irq); | ||
280 | cdesc->chip->eoi(cascade_irq); | ||
281 | } | ||
282 | desc->chip->eoi(irq); | ||
283 | } | ||
284 | |||
285 | /* Initialize the internal interrupt controller. The number of | ||
286 | * interrupts supported can vary with the processor type, and the | ||
287 | * 82xx family can have up to 64. | ||
288 | * External interrupts can be either edge or level triggered, and | ||
289 | * need to be initialized by the appropriate driver. | ||
290 | */ | ||
291 | void __init m8xx_pic_init(void) | ||
292 | { | ||
293 | int irq; | ||
294 | |||
295 | if (mpc8xx_pic_init()) { | ||
296 | printk(KERN_ERR "Failed interrupt 8xx controller initialization\n"); | ||
297 | return; | ||
298 | } | ||
299 | |||
300 | irq = cpm_pic_init(); | ||
301 | if (irq != NO_IRQ) | ||
302 | set_irq_chained_handler(irq, cpm_cascade); | ||
303 | } | ||
diff --git a/arch/powerpc/platforms/8xx/mpc86xads.h b/arch/powerpc/platforms/8xx/mpc86xads.h new file mode 100644 index 000000000000..b5d19dd0619c --- /dev/null +++ b/arch/powerpc/platforms/8xx/mpc86xads.h | |||
@@ -0,0 +1,95 @@ | |||
1 | /* | ||
2 | * A collection of structures, addresses, and values associated with | ||
3 | * the Freescale MPC86xADS board. | ||
4 | * Copied from the FADS stuff. | ||
5 | * | ||
6 | * Author: MontaVista Software, Inc. | ||
7 | * source@mvista.com | ||
8 | * | ||
9 | * 2005 (c) MontaVista Software, Inc. This file is licensed under the | ||
10 | * terms of the GNU General Public License version 2. This program is licensed | ||
11 | * "as is" without any warranty of any kind, whether express or implied. | ||
12 | */ | ||
13 | |||
14 | #ifdef __KERNEL__ | ||
15 | #ifndef __ASM_MPC86XADS_H__ | ||
16 | #define __ASM_MPC86XADS_H__ | ||
17 | |||
18 | #include <asm/ppcboot.h> | ||
19 | #include <sysdev/fsl_soc.h> | ||
20 | |||
21 | /* U-Boot maps BCSR to 0xff080000 */ | ||
22 | #define BCSR_ADDR ((uint)0xff080000) | ||
23 | #define BCSR_SIZE ((uint)32) | ||
24 | #define BCSR0 ((uint)(BCSR_ADDR + 0x00)) | ||
25 | #define BCSR1 ((uint)(BCSR_ADDR + 0x04)) | ||
26 | #define BCSR2 ((uint)(BCSR_ADDR + 0x08)) | ||
27 | #define BCSR3 ((uint)(BCSR_ADDR + 0x0c)) | ||
28 | #define BCSR4 ((uint)(BCSR_ADDR + 0x10)) | ||
29 | |||
30 | #define CFG_PHYDEV_ADDR ((uint)0xff0a0000) | ||
31 | #define BCSR5 ((uint)(CFG_PHYDEV_ADDR + 0x300)) | ||
32 | |||
33 | #define IMAP_ADDR (get_immrbase()) | ||
34 | #define IMAP_SIZE ((uint)(64 * 1024)) | ||
35 | |||
36 | #define MPC8xx_CPM_OFFSET (0x9c0) | ||
37 | #define CPM_MAP_ADDR (get_immrbase() + MPC8xx_CPM_OFFSET) | ||
38 | #define CPM_IRQ_OFFSET 16 // for compability with cpm_uart driver | ||
39 | |||
40 | #define PCMCIA_MEM_ADDR (uint)0xff020000) | ||
41 | #define PCMCIA_MEM_SIZE ((uint)(64 * 1024)) | ||
42 | |||
43 | /* Bits of interest in the BCSRs. | ||
44 | */ | ||
45 | #define BCSR1_ETHEN ((uint)0x20000000) | ||
46 | #define BCSR1_IRDAEN ((uint)0x10000000) | ||
47 | #define BCSR1_RS232EN_1 ((uint)0x01000000) | ||
48 | #define BCSR1_PCCEN ((uint)0x00800000) | ||
49 | #define BCSR1_PCCVCC0 ((uint)0x00400000) | ||
50 | #define BCSR1_PCCVPP0 ((uint)0x00200000) | ||
51 | #define BCSR1_PCCVPP1 ((uint)0x00100000) | ||
52 | #define BCSR1_PCCVPP_MASK (BCSR1_PCCVPP0 | BCSR1_PCCVPP1) | ||
53 | #define BCSR1_RS232EN_2 ((uint)0x00040000) | ||
54 | #define BCSR1_PCCVCC1 ((uint)0x00010000) | ||
55 | #define BCSR1_PCCVCC_MASK (BCSR1_PCCVCC0 | BCSR1_PCCVCC1) | ||
56 | |||
57 | #define BCSR4_ETH10_RST ((uint)0x80000000) /* 10Base-T PHY reset*/ | ||
58 | #define BCSR4_USB_LO_SPD ((uint)0x04000000) | ||
59 | #define BCSR4_USB_VCC ((uint)0x02000000) | ||
60 | #define BCSR4_USB_FULL_SPD ((uint)0x00040000) | ||
61 | #define BCSR4_USB_EN ((uint)0x00020000) | ||
62 | |||
63 | #define BCSR5_MII2_EN 0x40 | ||
64 | #define BCSR5_MII2_RST 0x20 | ||
65 | #define BCSR5_T1_RST 0x10 | ||
66 | #define BCSR5_ATM155_RST 0x08 | ||
67 | #define BCSR5_ATM25_RST 0x04 | ||
68 | #define BCSR5_MII1_EN 0x02 | ||
69 | #define BCSR5_MII1_RST 0x01 | ||
70 | |||
71 | /* Interrupt level assignments */ | ||
72 | #define PHY_INTERRUPT SIU_IRQ7 /* PHY link change interrupt */ | ||
73 | #define SIU_INT_FEC1 SIU_LEVEL1 /* FEC1 interrupt */ | ||
74 | #define FEC_INTERRUPT SIU_INT_FEC1 /* FEC interrupt */ | ||
75 | |||
76 | /* We don't use the 8259 */ | ||
77 | #define NR_8259_INTS 0 | ||
78 | |||
79 | /* CPM Ethernet through SCC1 */ | ||
80 | #define PA_ENET_RXD ((ushort)0x0001) | ||
81 | #define PA_ENET_TXD ((ushort)0x0002) | ||
82 | #define PA_ENET_TCLK ((ushort)0x0100) | ||
83 | #define PA_ENET_RCLK ((ushort)0x0200) | ||
84 | #define PB_ENET_TENA ((uint)0x00001000) | ||
85 | #define PC_ENET_CLSN ((ushort)0x0010) | ||
86 | #define PC_ENET_RENA ((ushort)0x0020) | ||
87 | |||
88 | /* Control bits in the SICR to route TCLK (CLK1) and RCLK (CLK2) to | ||
89 | * SCC1. Also, make sure GR1 (bit 24) and SC1 (bit 25) are zero. | ||
90 | */ | ||
91 | #define SICR_ENET_MASK ((uint)0x000000ff) | ||
92 | #define SICR_ENET_CLKRT ((uint)0x0000002c) | ||
93 | |||
94 | #endif /* __ASM_MPC86XADS_H__ */ | ||
95 | #endif /* __KERNEL__ */ | ||
diff --git a/arch/powerpc/platforms/8xx/mpc86xads_setup.c b/arch/powerpc/platforms/8xx/mpc86xads_setup.c new file mode 100644 index 000000000000..ef52ce701b0e --- /dev/null +++ b/arch/powerpc/platforms/8xx/mpc86xads_setup.c | |||
@@ -0,0 +1,301 @@ | |||
1 | /*arch/ppc/platforms/mpc86xads-setup.c | ||
2 | * | ||
3 | * Platform setup for the Freescale mpc86xads board | ||
4 | * | ||
5 | * Vitaly Bordug <vbordug@ru.mvista.com> | ||
6 | * | ||
7 | * Copyright 2005 MontaVista Software Inc. | ||
8 | * | ||
9 | * This file is licensed under the terms of the GNU General Public License | ||
10 | * version 2. This program is licensed "as is" without any warranty of any | ||
11 | * kind, whether express or implied. | ||
12 | */ | ||
13 | |||
14 | #include <linux/init.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/param.h> | ||
17 | #include <linux/string.h> | ||
18 | #include <linux/ioport.h> | ||
19 | #include <linux/device.h> | ||
20 | #include <linux/delay.h> | ||
21 | #include <linux/root_dev.h> | ||
22 | |||
23 | #include <linux/fs_enet_pd.h> | ||
24 | #include <linux/fs_uart_pd.h> | ||
25 | #include <linux/mii.h> | ||
26 | |||
27 | #include <asm/delay.h> | ||
28 | #include <asm/io.h> | ||
29 | #include <asm/machdep.h> | ||
30 | #include <asm/page.h> | ||
31 | #include <asm/processor.h> | ||
32 | #include <asm/system.h> | ||
33 | #include <asm/time.h> | ||
34 | #include <asm/ppcboot.h> | ||
35 | #include <asm/mpc8xx.h> | ||
36 | #include <asm/8xx_immap.h> | ||
37 | #include <asm/commproc.h> | ||
38 | #include <asm/fs_pd.h> | ||
39 | #include <asm/prom.h> | ||
40 | |||
41 | extern void cpm_reset(void); | ||
42 | extern void mpc8xx_show_cpuinfo(struct seq_file*); | ||
43 | extern void mpc8xx_restart(char *cmd); | ||
44 | extern void mpc8xx_calibrate_decr(void); | ||
45 | extern int mpc8xx_set_rtc_time(struct rtc_time *tm); | ||
46 | extern void mpc8xx_get_rtc_time(struct rtc_time *tm); | ||
47 | extern void m8xx_pic_init(void); | ||
48 | extern unsigned int mpc8xx_get_irq(void); | ||
49 | |||
50 | static void init_smc1_uart_ioports(struct fs_uart_platform_info* fpi); | ||
51 | static void init_smc2_uart_ioports(struct fs_uart_platform_info* fpi); | ||
52 | static void init_scc1_ioports(struct fs_platform_info* ptr); | ||
53 | |||
54 | void __init mpc86xads_board_setup(void) | ||
55 | { | ||
56 | cpm8xx_t *cp; | ||
57 | unsigned int *bcsr_io; | ||
58 | u8 tmpval8; | ||
59 | |||
60 | bcsr_io = ioremap(BCSR1, sizeof(unsigned long)); | ||
61 | cp = (cpm8xx_t *)immr_map(im_cpm); | ||
62 | |||
63 | if (bcsr_io == NULL) { | ||
64 | printk(KERN_CRIT "Could not remap BCSR\n"); | ||
65 | return; | ||
66 | } | ||
67 | #ifdef CONFIG_SERIAL_CPM_SMC1 | ||
68 | clrbits32(bcsr_io, BCSR1_RS232EN_1); | ||
69 | clrbits32(&cp->cp_simode, 0xe0000000 >> 17); /* brg1 */ | ||
70 | tmpval8 = in_8(&(cp->cp_smc[0].smc_smcm)) | (SMCM_RX | SMCM_TX); | ||
71 | out_8(&(cp->cp_smc[0].smc_smcm), tmpval8); | ||
72 | clrbits16(&cp->cp_smc[0].smc_smcmr, SMCMR_REN | SMCMR_TEN); | ||
73 | #else | ||
74 | setbits32(bcsr_io,BCSR1_RS232EN_1); | ||
75 | out_be16(&cp->cp_smc[0].smc_smcmr, 0); | ||
76 | out_8(&cp->cp_smc[0].smc_smce, 0); | ||
77 | #endif | ||
78 | |||
79 | #ifdef CONFIG_SERIAL_CPM_SMC2 | ||
80 | clrbits32(bcsr_io,BCSR1_RS232EN_2); | ||
81 | clrbits32(&cp->cp_simode, 0xe0000000 >> 1); | ||
82 | setbits32(&cp->cp_simode, 0x20000000 >> 1); /* brg2 */ | ||
83 | tmpval8 = in_8(&(cp->cp_smc[1].smc_smcm)) | (SMCM_RX | SMCM_TX); | ||
84 | out_8(&(cp->cp_smc[1].smc_smcm), tmpval8); | ||
85 | clrbits16(&cp->cp_smc[1].smc_smcmr, SMCMR_REN | SMCMR_TEN); | ||
86 | |||
87 | init_smc2_uart_ioports(0); | ||
88 | #else | ||
89 | setbits32(bcsr_io,BCSR1_RS232EN_2); | ||
90 | out_be16(&cp->cp_smc[1].smc_smcmr, 0); | ||
91 | out_8(&cp->cp_smc[1].smc_smce, 0); | ||
92 | #endif | ||
93 | immr_unmap(cp); | ||
94 | iounmap(bcsr_io); | ||
95 | } | ||
96 | |||
97 | |||
98 | static void init_fec1_ioports(struct fs_platform_info* ptr) | ||
99 | { | ||
100 | iop8xx_t *io_port = (iop8xx_t *)immr_map(im_ioport); | ||
101 | |||
102 | /* configure FEC1 pins */ | ||
103 | |||
104 | setbits16(&io_port->iop_pdpar, 0x1fff); | ||
105 | setbits16(&io_port->iop_pddir, 0x1fff); | ||
106 | |||
107 | immr_unmap(io_port); | ||
108 | } | ||
109 | |||
110 | void init_fec_ioports(struct fs_platform_info *fpi) | ||
111 | { | ||
112 | int fec_no = fs_get_fec_index(fpi->fs_no); | ||
113 | |||
114 | switch (fec_no) { | ||
115 | case 0: | ||
116 | init_fec1_ioports(fpi); | ||
117 | break; | ||
118 | default: | ||
119 | printk(KERN_ERR "init_fec_ioports: invalid FEC number\n"); | ||
120 | return; | ||
121 | } | ||
122 | } | ||
123 | |||
124 | static void init_scc1_ioports(struct fs_platform_info* fpi) | ||
125 | { | ||
126 | unsigned *bcsr_io; | ||
127 | iop8xx_t *io_port; | ||
128 | cpm8xx_t *cp; | ||
129 | |||
130 | bcsr_io = ioremap(BCSR_ADDR, BCSR_SIZE); | ||
131 | io_port = (iop8xx_t *)immr_map(im_ioport); | ||
132 | cp = (cpm8xx_t *)immr_map(im_cpm); | ||
133 | |||
134 | if (bcsr_io == NULL) { | ||
135 | printk(KERN_CRIT "Could not remap BCSR\n"); | ||
136 | return; | ||
137 | } | ||
138 | |||
139 | /* Configure port A pins for Txd and Rxd. | ||
140 | */ | ||
141 | setbits16(&io_port->iop_papar, PA_ENET_RXD | PA_ENET_TXD); | ||
142 | clrbits16(&io_port->iop_padir, PA_ENET_RXD | PA_ENET_TXD); | ||
143 | clrbits16(&io_port->iop_paodr, PA_ENET_TXD); | ||
144 | |||
145 | /* Configure port C pins to enable CLSN and RENA. | ||
146 | */ | ||
147 | clrbits16(&io_port->iop_pcpar, PC_ENET_CLSN | PC_ENET_RENA); | ||
148 | clrbits16(&io_port->iop_pcdir, PC_ENET_CLSN | PC_ENET_RENA); | ||
149 | setbits16(&io_port->iop_pcso, PC_ENET_CLSN | PC_ENET_RENA); | ||
150 | |||
151 | /* Configure port A for TCLK and RCLK. | ||
152 | */ | ||
153 | setbits16(&io_port->iop_papar, PA_ENET_TCLK | PA_ENET_RCLK); | ||
154 | clrbits16(&io_port->iop_padir, PA_ENET_TCLK | PA_ENET_RCLK); | ||
155 | clrbits32(&cp->cp_pbpar, PB_ENET_TENA); | ||
156 | clrbits32(&cp->cp_pbdir, PB_ENET_TENA); | ||
157 | |||
158 | /* Configure Serial Interface clock routing. | ||
159 | * First, clear all SCC bits to zero, then set the ones we want. | ||
160 | */ | ||
161 | clrbits32(&cp->cp_sicr, SICR_ENET_MASK); | ||
162 | setbits32(&cp->cp_sicr, SICR_ENET_CLKRT); | ||
163 | |||
164 | /* In the original SCC enet driver the following code is placed at | ||
165 | the end of the initialization */ | ||
166 | setbits32(&cp->cp_pbpar, PB_ENET_TENA); | ||
167 | setbits32(&cp->cp_pbdir, PB_ENET_TENA); | ||
168 | |||
169 | clrbits32(bcsr_io+1, BCSR1_ETHEN); | ||
170 | iounmap(bcsr_io); | ||
171 | immr_unmap(cp); | ||
172 | immr_unmap(io_port); | ||
173 | } | ||
174 | |||
175 | void init_scc_ioports(struct fs_platform_info *fpi) | ||
176 | { | ||
177 | int scc_no = fs_get_scc_index(fpi->fs_no); | ||
178 | |||
179 | switch (scc_no) { | ||
180 | case 0: | ||
181 | init_scc1_ioports(fpi); | ||
182 | break; | ||
183 | default: | ||
184 | printk(KERN_ERR "init_scc_ioports: invalid SCC number\n"); | ||
185 | return; | ||
186 | } | ||
187 | } | ||
188 | |||
189 | |||
190 | |||
191 | static void init_smc1_uart_ioports(struct fs_uart_platform_info* ptr) | ||
192 | { | ||
193 | unsigned *bcsr_io; | ||
194 | cpm8xx_t *cp = (cpm8xx_t *)immr_map(im_cpm); | ||
195 | |||
196 | setbits32(&cp->cp_pbpar, 0x000000c0); | ||
197 | clrbits32(&cp->cp_pbdir, 0x000000c0); | ||
198 | clrbits16(&cp->cp_pbodr, 0x00c0); | ||
199 | immr_unmap(cp); | ||
200 | |||
201 | bcsr_io = ioremap(BCSR1, sizeof(unsigned long)); | ||
202 | |||
203 | if (bcsr_io == NULL) { | ||
204 | printk(KERN_CRIT "Could not remap BCSR1\n"); | ||
205 | return; | ||
206 | } | ||
207 | clrbits32(bcsr_io,BCSR1_RS232EN_1); | ||
208 | iounmap(bcsr_io); | ||
209 | } | ||
210 | |||
211 | static void init_smc2_uart_ioports(struct fs_uart_platform_info* fpi) | ||
212 | { | ||
213 | unsigned *bcsr_io; | ||
214 | cpm8xx_t *cp = (cpm8xx_t *)immr_map(im_cpm); | ||
215 | |||
216 | setbits32(&cp->cp_pbpar, 0x00000c00); | ||
217 | clrbits32(&cp->cp_pbdir, 0x00000c00); | ||
218 | clrbits16(&cp->cp_pbodr, 0x0c00); | ||
219 | immr_unmap(cp); | ||
220 | |||
221 | bcsr_io = ioremap(BCSR1, sizeof(unsigned long)); | ||
222 | |||
223 | if (bcsr_io == NULL) { | ||
224 | printk(KERN_CRIT "Could not remap BCSR1\n"); | ||
225 | return; | ||
226 | } | ||
227 | clrbits32(bcsr_io,BCSR1_RS232EN_2); | ||
228 | iounmap(bcsr_io); | ||
229 | } | ||
230 | |||
231 | void init_smc_ioports(struct fs_uart_platform_info *data) | ||
232 | { | ||
233 | int smc_no = fs_uart_id_fsid2smc(data->fs_no); | ||
234 | |||
235 | switch (smc_no) { | ||
236 | case 0: | ||
237 | init_smc1_uart_ioports(data); | ||
238 | data->brg = data->clk_rx; | ||
239 | break; | ||
240 | case 1: | ||
241 | init_smc2_uart_ioports(data); | ||
242 | data->brg = data->clk_rx; | ||
243 | break; | ||
244 | default: | ||
245 | printk(KERN_ERR "init_scc_ioports: invalid SCC number\n"); | ||
246 | return; | ||
247 | } | ||
248 | } | ||
249 | |||
250 | int platform_device_skip(char *model, int id) | ||
251 | { | ||
252 | return 0; | ||
253 | } | ||
254 | |||
255 | static void __init mpc86xads_setup_arch(void) | ||
256 | { | ||
257 | struct device_node *cpu; | ||
258 | |||
259 | cpu = of_find_node_by_type(NULL, "cpu"); | ||
260 | if (cpu != 0) { | ||
261 | const unsigned int *fp; | ||
262 | |||
263 | fp = get_property(cpu, "clock-frequency", NULL); | ||
264 | if (fp != 0) | ||
265 | loops_per_jiffy = *fp / HZ; | ||
266 | else | ||
267 | loops_per_jiffy = 50000000 / HZ; | ||
268 | of_node_put(cpu); | ||
269 | } | ||
270 | |||
271 | cpm_reset(); | ||
272 | |||
273 | mpc86xads_board_setup(); | ||
274 | |||
275 | ROOT_DEV = Root_NFS; | ||
276 | } | ||
277 | |||
278 | static int __init mpc86xads_probe(void) | ||
279 | { | ||
280 | char *model = of_get_flat_dt_prop(of_get_flat_dt_root(), | ||
281 | "model", NULL); | ||
282 | if (model == NULL) | ||
283 | return 0; | ||
284 | if (strcmp(model, "MPC866ADS")) | ||
285 | return 0; | ||
286 | |||
287 | return 1; | ||
288 | } | ||
289 | |||
290 | define_machine(mpc86x_ads) { | ||
291 | .name = "MPC86x ADS", | ||
292 | .probe = mpc86xads_probe, | ||
293 | .setup_arch = mpc86xads_setup_arch, | ||
294 | .init_IRQ = m8xx_pic_init, | ||
295 | .show_cpuinfo = mpc8xx_show_cpuinfo, | ||
296 | .get_irq = mpc8xx_get_irq, | ||
297 | .restart = mpc8xx_restart, | ||
298 | .calibrate_decr = mpc8xx_calibrate_decr, | ||
299 | .set_rtc_time = mpc8xx_set_rtc_time, | ||
300 | .get_rtc_time = mpc8xx_get_rtc_time, | ||
301 | }; | ||
diff --git a/arch/powerpc/platforms/8xx/mpc885ads.h b/arch/powerpc/platforms/8xx/mpc885ads.h new file mode 100644 index 000000000000..30cbebfe84c5 --- /dev/null +++ b/arch/powerpc/platforms/8xx/mpc885ads.h | |||
@@ -0,0 +1,95 @@ | |||
1 | /* | ||
2 | * A collection of structures, addresses, and values associated with | ||
3 | * the Freescale MPC885ADS board. | ||
4 | * Copied from the FADS stuff. | ||
5 | * | ||
6 | * Author: MontaVista Software, Inc. | ||
7 | * source@mvista.com | ||
8 | * | ||
9 | * 2005 (c) MontaVista Software, Inc. This file is licensed under the | ||
10 | * terms of the GNU General Public License version 2. This program is licensed | ||
11 | * "as is" without any warranty of any kind, whether express or implied. | ||
12 | */ | ||
13 | |||
14 | #ifdef __KERNEL__ | ||
15 | #ifndef __ASM_MPC885ADS_H__ | ||
16 | #define __ASM_MPC885ADS_H__ | ||
17 | |||
18 | #include <asm/ppcboot.h> | ||
19 | #include <sysdev/fsl_soc.h> | ||
20 | |||
21 | /* U-Boot maps BCSR to 0xff080000 */ | ||
22 | #define BCSR_ADDR ((uint)0xff080000) | ||
23 | #define BCSR_SIZE ((uint)32) | ||
24 | #define BCSR0 ((uint)(BCSR_ADDR + 0x00)) | ||
25 | #define BCSR1 ((uint)(BCSR_ADDR + 0x04)) | ||
26 | #define BCSR2 ((uint)(BCSR_ADDR + 0x08)) | ||
27 | #define BCSR3 ((uint)(BCSR_ADDR + 0x0c)) | ||
28 | #define BCSR4 ((uint)(BCSR_ADDR + 0x10)) | ||
29 | |||
30 | #define CFG_PHYDEV_ADDR ((uint)0xff0a0000) | ||
31 | #define BCSR5 ((uint)(CFG_PHYDEV_ADDR + 0x300)) | ||
32 | |||
33 | #define IMAP_ADDR (get_immrbase()) | ||
34 | #define IMAP_SIZE ((uint)(64 * 1024)) | ||
35 | |||
36 | #define MPC8xx_CPM_OFFSET (0x9c0) | ||
37 | #define CPM_MAP_ADDR (get_immrbase() + MPC8xx_CPM_OFFSET) | ||
38 | #define CPM_IRQ_OFFSET 16 // for compability with cpm_uart driver | ||
39 | |||
40 | #define PCMCIA_MEM_ADDR (uint)0xff020000) | ||
41 | #define PCMCIA_MEM_SIZE ((uint)(64 * 1024)) | ||
42 | |||
43 | /* Bits of interest in the BCSRs. | ||
44 | */ | ||
45 | #define BCSR1_ETHEN ((uint)0x20000000) | ||
46 | #define BCSR1_IRDAEN ((uint)0x10000000) | ||
47 | #define BCSR1_RS232EN_1 ((uint)0x01000000) | ||
48 | #define BCSR1_PCCEN ((uint)0x00800000) | ||
49 | #define BCSR1_PCCVCC0 ((uint)0x00400000) | ||
50 | #define BCSR1_PCCVPP0 ((uint)0x00200000) | ||
51 | #define BCSR1_PCCVPP1 ((uint)0x00100000) | ||
52 | #define BCSR1_PCCVPP_MASK (BCSR1_PCCVPP0 | BCSR1_PCCVPP1) | ||
53 | #define BCSR1_RS232EN_2 ((uint)0x00040000) | ||
54 | #define BCSR1_PCCVCC1 ((uint)0x00010000) | ||
55 | #define BCSR1_PCCVCC_MASK (BCSR1_PCCVCC0 | BCSR1_PCCVCC1) | ||
56 | |||
57 | #define BCSR4_ETH10_RST ((uint)0x80000000) /* 10Base-T PHY reset*/ | ||
58 | #define BCSR4_USB_LO_SPD ((uint)0x04000000) | ||
59 | #define BCSR4_USB_VCC ((uint)0x02000000) | ||
60 | #define BCSR4_USB_FULL_SPD ((uint)0x00040000) | ||
61 | #define BCSR4_USB_EN ((uint)0x00020000) | ||
62 | |||
63 | #define BCSR5_MII2_EN 0x40 | ||
64 | #define BCSR5_MII2_RST 0x20 | ||
65 | #define BCSR5_T1_RST 0x10 | ||
66 | #define BCSR5_ATM155_RST 0x08 | ||
67 | #define BCSR5_ATM25_RST 0x04 | ||
68 | #define BCSR5_MII1_EN 0x02 | ||
69 | #define BCSR5_MII1_RST 0x01 | ||
70 | |||
71 | /* Interrupt level assignments */ | ||
72 | #define PHY_INTERRUPT SIU_IRQ7 /* PHY link change interrupt */ | ||
73 | #define SIU_INT_FEC1 SIU_LEVEL1 /* FEC1 interrupt */ | ||
74 | #define SIU_INT_FEC2 SIU_LEVEL3 /* FEC2 interrupt */ | ||
75 | #define FEC_INTERRUPT SIU_INT_FEC1 /* FEC interrupt */ | ||
76 | |||
77 | /* We don't use the 8259 */ | ||
78 | #define NR_8259_INTS 0 | ||
79 | |||
80 | /* CPM Ethernet through SCC3 */ | ||
81 | #define PA_ENET_RXD ((ushort)0x0040) | ||
82 | #define PA_ENET_TXD ((ushort)0x0080) | ||
83 | #define PE_ENET_TCLK ((uint)0x00004000) | ||
84 | #define PE_ENET_RCLK ((uint)0x00008000) | ||
85 | #define PE_ENET_TENA ((uint)0x00000010) | ||
86 | #define PC_ENET_CLSN ((ushort)0x0400) | ||
87 | #define PC_ENET_RENA ((ushort)0x0800) | ||
88 | |||
89 | /* Control bits in the SICR to route TCLK (CLK5) and RCLK (CLK6) to | ||
90 | * SCC3. Also, make sure GR3 (bit 8) and SC3 (bit 9) are zero */ | ||
91 | #define SICR_ENET_MASK ((uint)0x00ff0000) | ||
92 | #define SICR_ENET_CLKRT ((uint)0x002c0000) | ||
93 | |||
94 | #endif /* __ASM_MPC885ADS_H__ */ | ||
95 | #endif /* __KERNEL__ */ | ||
diff --git a/arch/powerpc/platforms/8xx/mpc885ads_setup.c b/arch/powerpc/platforms/8xx/mpc885ads_setup.c new file mode 100644 index 000000000000..c5fefdf66c0a --- /dev/null +++ b/arch/powerpc/platforms/8xx/mpc885ads_setup.c | |||
@@ -0,0 +1,387 @@ | |||
1 | /*arch/ppc/platforms/mpc885ads-setup.c | ||
2 | * | ||
3 | * Platform setup for the Freescale mpc885ads board | ||
4 | * | ||
5 | * Vitaly Bordug <vbordug@ru.mvista.com> | ||
6 | * | ||
7 | * Copyright 2005 MontaVista Software Inc. | ||
8 | * | ||
9 | * This file is licensed under the terms of the GNU General Public License | ||
10 | * version 2. This program is licensed "as is" without any warranty of any | ||
11 | * kind, whether express or implied. | ||
12 | */ | ||
13 | |||
14 | #include <linux/init.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/param.h> | ||
17 | #include <linux/string.h> | ||
18 | #include <linux/ioport.h> | ||
19 | #include <linux/device.h> | ||
20 | #include <linux/delay.h> | ||
21 | #include <linux/root_dev.h> | ||
22 | |||
23 | #include <linux/fs_enet_pd.h> | ||
24 | #include <linux/fs_uart_pd.h> | ||
25 | #include <linux/mii.h> | ||
26 | |||
27 | #include <asm/delay.h> | ||
28 | #include <asm/io.h> | ||
29 | #include <asm/machdep.h> | ||
30 | #include <asm/page.h> | ||
31 | #include <asm/processor.h> | ||
32 | #include <asm/system.h> | ||
33 | #include <asm/time.h> | ||
34 | #include <asm/ppcboot.h> | ||
35 | #include <asm/mpc8xx.h> | ||
36 | #include <asm/8xx_immap.h> | ||
37 | #include <asm/commproc.h> | ||
38 | #include <asm/fs_pd.h> | ||
39 | #include <asm/prom.h> | ||
40 | |||
41 | extern void cpm_reset(void); | ||
42 | extern void mpc8xx_show_cpuinfo(struct seq_file*); | ||
43 | extern void mpc8xx_restart(char *cmd); | ||
44 | extern void mpc8xx_calibrate_decr(void); | ||
45 | extern int mpc8xx_set_rtc_time(struct rtc_time *tm); | ||
46 | extern void mpc8xx_get_rtc_time(struct rtc_time *tm); | ||
47 | extern void m8xx_pic_init(void); | ||
48 | extern unsigned int mpc8xx_get_irq(void); | ||
49 | |||
50 | static void init_smc1_uart_ioports(struct fs_uart_platform_info* fpi); | ||
51 | static void init_smc2_uart_ioports(struct fs_uart_platform_info* fpi); | ||
52 | static void init_scc3_ioports(struct fs_platform_info* ptr); | ||
53 | |||
54 | void __init mpc885ads_board_setup(void) | ||
55 | { | ||
56 | cpm8xx_t *cp; | ||
57 | unsigned int *bcsr_io; | ||
58 | u8 tmpval8; | ||
59 | |||
60 | #ifdef CONFIG_FS_ENET | ||
61 | iop8xx_t *io_port; | ||
62 | #endif | ||
63 | |||
64 | bcsr_io = ioremap(BCSR1, sizeof(unsigned long)); | ||
65 | cp = (cpm8xx_t *)immr_map(im_cpm); | ||
66 | |||
67 | if (bcsr_io == NULL) { | ||
68 | printk(KERN_CRIT "Could not remap BCSR\n"); | ||
69 | return; | ||
70 | } | ||
71 | #ifdef CONFIG_SERIAL_CPM_SMC1 | ||
72 | clrbits32(bcsr_io, BCSR1_RS232EN_1); | ||
73 | clrbits32(&cp->cp_simode, 0xe0000000 >> 17); /* brg1 */ | ||
74 | tmpval8 = in_8(&(cp->cp_smc[0].smc_smcm)) | (SMCM_RX | SMCM_TX); | ||
75 | out_8(&(cp->cp_smc[0].smc_smcm), tmpval8); | ||
76 | clrbits16(&cp->cp_smc[0].smc_smcmr, SMCMR_REN | SMCMR_TEN); /* brg1 */ | ||
77 | #else | ||
78 | setbits32(bcsr_io,BCSR1_RS232EN_1); | ||
79 | out_be16(&cp->cp_smc[0].smc_smcmr, 0); | ||
80 | out_8(&cp->cp_smc[0].smc_smce, 0); | ||
81 | #endif | ||
82 | |||
83 | #ifdef CONFIG_SERIAL_CPM_SMC2 | ||
84 | clrbits32(bcsr_io,BCSR1_RS232EN_2); | ||
85 | clrbits32(&cp->cp_simode, 0xe0000000 >> 1); | ||
86 | setbits32(&cp->cp_simode, 0x20000000 >> 1); /* brg2 */ | ||
87 | tmpval8 = in_8(&(cp->cp_smc[1].smc_smcm)) | (SMCM_RX | SMCM_TX); | ||
88 | out_8(&(cp->cp_smc[1].smc_smcm), tmpval8); | ||
89 | clrbits16(&cp->cp_smc[1].smc_smcmr, SMCMR_REN | SMCMR_TEN); | ||
90 | |||
91 | init_smc2_uart_ioports(0); | ||
92 | #else | ||
93 | setbits32(bcsr_io,BCSR1_RS232EN_2); | ||
94 | out_be16(&cp->cp_smc[1].smc_smcmr, 0); | ||
95 | out_8(&cp->cp_smc[1].smc_smce, 0); | ||
96 | #endif | ||
97 | immr_unmap(cp); | ||
98 | iounmap(bcsr_io); | ||
99 | |||
100 | #ifdef CONFIG_FS_ENET | ||
101 | /* use MDC for MII (common) */ | ||
102 | io_port = (iop8xx_t*)immr_map(im_ioport); | ||
103 | setbits16(&io_port->iop_pdpar, 0x0080); | ||
104 | clrbits16(&io_port->iop_pddir, 0x0080); | ||
105 | |||
106 | bcsr_io = ioremap(BCSR5, sizeof(unsigned long)); | ||
107 | clrbits32(bcsr_io,BCSR5_MII1_EN); | ||
108 | clrbits32(bcsr_io,BCSR5_MII1_RST); | ||
109 | #ifndef CONFIG_FC_ENET_HAS_SCC | ||
110 | clrbits32(bcsr_io,BCSR5_MII2_EN); | ||
111 | clrbits32(bcsr_io,BCSR5_MII2_RST); | ||
112 | |||
113 | #endif | ||
114 | iounmap(bcsr_io); | ||
115 | immr_unmap(io_port); | ||
116 | |||
117 | #endif | ||
118 | } | ||
119 | |||
120 | |||
121 | static void init_fec1_ioports(struct fs_platform_info* ptr) | ||
122 | { | ||
123 | cpm8xx_t *cp = (cpm8xx_t *)immr_map(im_cpm); | ||
124 | iop8xx_t *io_port = (iop8xx_t *)immr_map(im_ioport); | ||
125 | |||
126 | /* configure FEC1 pins */ | ||
127 | setbits16(&io_port->iop_papar, 0xf830); | ||
128 | setbits16(&io_port->iop_padir, 0x0830); | ||
129 | clrbits16(&io_port->iop_padir, 0xf000); | ||
130 | |||
131 | setbits32(&cp->cp_pbpar, 0x00001001); | ||
132 | clrbits32(&cp->cp_pbdir, 0x00001001); | ||
133 | |||
134 | setbits16(&io_port->iop_pcpar, 0x000c); | ||
135 | clrbits16(&io_port->iop_pcdir, 0x000c); | ||
136 | |||
137 | setbits32(&cp->cp_pepar, 0x00000003); | ||
138 | setbits32(&cp->cp_pedir, 0x00000003); | ||
139 | clrbits32(&cp->cp_peso, 0x00000003); | ||
140 | clrbits32(&cp->cp_cptr, 0x00000100); | ||
141 | |||
142 | immr_unmap(io_port); | ||
143 | immr_unmap(cp); | ||
144 | } | ||
145 | |||
146 | |||
147 | static void init_fec2_ioports(struct fs_platform_info* ptr) | ||
148 | { | ||
149 | cpm8xx_t *cp = (cpm8xx_t *)immr_map(im_cpm); | ||
150 | iop8xx_t *io_port = (iop8xx_t *)immr_map(im_ioport); | ||
151 | |||
152 | /* configure FEC2 pins */ | ||
153 | setbits32(&cp->cp_pepar, 0x0003fffc); | ||
154 | setbits32(&cp->cp_pedir, 0x0003fffc); | ||
155 | clrbits32(&cp->cp_peso, 0x000087fc); | ||
156 | setbits32(&cp->cp_peso, 0x00037800); | ||
157 | clrbits32(&cp->cp_cptr, 0x00000080); | ||
158 | |||
159 | immr_unmap(io_port); | ||
160 | immr_unmap(cp); | ||
161 | } | ||
162 | |||
163 | void init_fec_ioports(struct fs_platform_info *fpi) | ||
164 | { | ||
165 | int fec_no = fs_get_fec_index(fpi->fs_no); | ||
166 | |||
167 | switch (fec_no) { | ||
168 | case 0: | ||
169 | init_fec1_ioports(fpi); | ||
170 | break; | ||
171 | case 1: | ||
172 | init_fec2_ioports(fpi); | ||
173 | break; | ||
174 | default: | ||
175 | printk(KERN_ERR "init_fec_ioports: invalid FEC number\n"); | ||
176 | return; | ||
177 | } | ||
178 | } | ||
179 | |||
180 | static void init_scc3_ioports(struct fs_platform_info* fpi) | ||
181 | { | ||
182 | unsigned *bcsr_io; | ||
183 | iop8xx_t *io_port; | ||
184 | cpm8xx_t *cp; | ||
185 | |||
186 | bcsr_io = ioremap(BCSR_ADDR, BCSR_SIZE); | ||
187 | io_port = (iop8xx_t *)immr_map(im_ioport); | ||
188 | cp = (cpm8xx_t *)immr_map(im_cpm); | ||
189 | |||
190 | if (bcsr_io == NULL) { | ||
191 | printk(KERN_CRIT "Could not remap BCSR\n"); | ||
192 | return; | ||
193 | } | ||
194 | |||
195 | /* Enable the PHY. | ||
196 | */ | ||
197 | clrbits32(bcsr_io+4, BCSR4_ETH10_RST); | ||
198 | udelay(1000); | ||
199 | setbits32(bcsr_io+4, BCSR4_ETH10_RST); | ||
200 | /* Configure port A pins for Txd and Rxd. | ||
201 | */ | ||
202 | setbits16(&io_port->iop_papar, PA_ENET_RXD | PA_ENET_TXD); | ||
203 | clrbits16(&io_port->iop_padir, PA_ENET_RXD | PA_ENET_TXD); | ||
204 | |||
205 | /* Configure port C pins to enable CLSN and RENA. | ||
206 | */ | ||
207 | clrbits16(&io_port->iop_pcpar, PC_ENET_CLSN | PC_ENET_RENA); | ||
208 | clrbits16(&io_port->iop_pcdir, PC_ENET_CLSN | PC_ENET_RENA); | ||
209 | setbits16(&io_port->iop_pcso, PC_ENET_CLSN | PC_ENET_RENA); | ||
210 | |||
211 | /* Configure port E for TCLK and RCLK. | ||
212 | */ | ||
213 | setbits32(&cp->cp_pepar, PE_ENET_TCLK | PE_ENET_RCLK); | ||
214 | clrbits32(&cp->cp_pepar, PE_ENET_TENA); | ||
215 | clrbits32(&cp->cp_pedir, | ||
216 | PE_ENET_TCLK | PE_ENET_RCLK | PE_ENET_TENA); | ||
217 | clrbits32(&cp->cp_peso, PE_ENET_TCLK | PE_ENET_RCLK); | ||
218 | setbits32(&cp->cp_peso, PE_ENET_TENA); | ||
219 | |||
220 | /* Configure Serial Interface clock routing. | ||
221 | * First, clear all SCC bits to zero, then set the ones we want. | ||
222 | */ | ||
223 | clrbits32(&cp->cp_sicr, SICR_ENET_MASK); | ||
224 | setbits32(&cp->cp_sicr, SICR_ENET_CLKRT); | ||
225 | |||
226 | /* Disable Rx and Tx. SMC1 sshould be stopped if SCC3 eternet are used. | ||
227 | */ | ||
228 | clrbits16(&cp->cp_smc[0].smc_smcmr, SMCMR_REN | SMCMR_TEN); | ||
229 | /* On the MPC885ADS SCC ethernet PHY is initialized in the full duplex mode | ||
230 | * by H/W setting after reset. SCC ethernet controller support only half duplex. | ||
231 | * This discrepancy of modes causes a lot of carrier lost errors. | ||
232 | */ | ||
233 | |||
234 | /* In the original SCC enet driver the following code is placed at | ||
235 | the end of the initialization */ | ||
236 | setbits32(&cp->cp_pepar, PE_ENET_TENA); | ||
237 | clrbits32(&cp->cp_pedir, PE_ENET_TENA); | ||
238 | setbits32(&cp->cp_peso, PE_ENET_TENA); | ||
239 | |||
240 | setbits32(bcsr_io+4, BCSR1_ETHEN); | ||
241 | iounmap(bcsr_io); | ||
242 | immr_unmap(io_port); | ||
243 | immr_unmap(cp); | ||
244 | } | ||
245 | |||
246 | void init_scc_ioports(struct fs_platform_info *fpi) | ||
247 | { | ||
248 | int scc_no = fs_get_scc_index(fpi->fs_no); | ||
249 | |||
250 | switch (scc_no) { | ||
251 | case 2: | ||
252 | init_scc3_ioports(fpi); | ||
253 | break; | ||
254 | default: | ||
255 | printk(KERN_ERR "init_scc_ioports: invalid SCC number\n"); | ||
256 | return; | ||
257 | } | ||
258 | } | ||
259 | |||
260 | |||
261 | |||
262 | static void init_smc1_uart_ioports(struct fs_uart_platform_info* ptr) | ||
263 | { | ||
264 | unsigned *bcsr_io; | ||
265 | cpm8xx_t *cp; | ||
266 | |||
267 | cp = (cpm8xx_t *)immr_map(im_cpm); | ||
268 | setbits32(&cp->cp_pepar, 0x000000c0); | ||
269 | clrbits32(&cp->cp_pedir, 0x000000c0); | ||
270 | clrbits32(&cp->cp_peso, 0x00000040); | ||
271 | setbits32(&cp->cp_peso, 0x00000080); | ||
272 | immr_unmap(cp); | ||
273 | |||
274 | bcsr_io = ioremap(BCSR1, sizeof(unsigned long)); | ||
275 | |||
276 | if (bcsr_io == NULL) { | ||
277 | printk(KERN_CRIT "Could not remap BCSR1\n"); | ||
278 | return; | ||
279 | } | ||
280 | clrbits32(bcsr_io,BCSR1_RS232EN_1); | ||
281 | iounmap(bcsr_io); | ||
282 | } | ||
283 | |||
284 | static void init_smc2_uart_ioports(struct fs_uart_platform_info* fpi) | ||
285 | { | ||
286 | unsigned *bcsr_io; | ||
287 | cpm8xx_t *cp; | ||
288 | |||
289 | cp = (cpm8xx_t *)immr_map(im_cpm); | ||
290 | setbits32(&cp->cp_pepar, 0x00000c00); | ||
291 | clrbits32(&cp->cp_pedir, 0x00000c00); | ||
292 | clrbits32(&cp->cp_peso, 0x00000400); | ||
293 | setbits32(&cp->cp_peso, 0x00000800); | ||
294 | immr_unmap(cp); | ||
295 | |||
296 | bcsr_io = ioremap(BCSR1, sizeof(unsigned long)); | ||
297 | |||
298 | if (bcsr_io == NULL) { | ||
299 | printk(KERN_CRIT "Could not remap BCSR1\n"); | ||
300 | return; | ||
301 | } | ||
302 | clrbits32(bcsr_io,BCSR1_RS232EN_2); | ||
303 | iounmap(bcsr_io); | ||
304 | } | ||
305 | |||
306 | void init_smc_ioports(struct fs_uart_platform_info *data) | ||
307 | { | ||
308 | int smc_no = fs_uart_id_fsid2smc(data->fs_no); | ||
309 | |||
310 | switch (smc_no) { | ||
311 | case 0: | ||
312 | init_smc1_uart_ioports(data); | ||
313 | data->brg = data->clk_rx; | ||
314 | break; | ||
315 | case 1: | ||
316 | init_smc2_uart_ioports(data); | ||
317 | data->brg = data->clk_rx; | ||
318 | break; | ||
319 | default: | ||
320 | printk(KERN_ERR "init_scc_ioports: invalid SCC number\n"); | ||
321 | return; | ||
322 | } | ||
323 | } | ||
324 | |||
325 | int platform_device_skip(char *model, int id) | ||
326 | { | ||
327 | #ifdef CONFIG_MPC8xx_SECOND_ETH_SCC3 | ||
328 | const char *dev = "FEC"; | ||
329 | int n = 2; | ||
330 | #else | ||
331 | const char *dev = "SCC"; | ||
332 | int n = 3; | ||
333 | #endif | ||
334 | |||
335 | if (!strcmp(model, dev) && n == id) | ||
336 | return 1; | ||
337 | |||
338 | return 0; | ||
339 | } | ||
340 | |||
341 | static void __init mpc885ads_setup_arch(void) | ||
342 | { | ||
343 | struct device_node *cpu; | ||
344 | |||
345 | cpu = of_find_node_by_type(NULL, "cpu"); | ||
346 | if (cpu != 0) { | ||
347 | const unsigned int *fp; | ||
348 | |||
349 | fp = get_property(cpu, "clock-frequency", NULL); | ||
350 | if (fp != 0) | ||
351 | loops_per_jiffy = *fp / HZ; | ||
352 | else | ||
353 | loops_per_jiffy = 50000000 / HZ; | ||
354 | of_node_put(cpu); | ||
355 | } | ||
356 | |||
357 | cpm_reset(); | ||
358 | |||
359 | mpc885ads_board_setup(); | ||
360 | |||
361 | ROOT_DEV = Root_NFS; | ||
362 | } | ||
363 | |||
364 | static int __init mpc885ads_probe(void) | ||
365 | { | ||
366 | char *model = of_get_flat_dt_prop(of_get_flat_dt_root(), | ||
367 | "model", NULL); | ||
368 | if (model == NULL) | ||
369 | return 0; | ||
370 | if (strcmp(model, "MPC885ADS")) | ||
371 | return 0; | ||
372 | |||
373 | return 1; | ||
374 | } | ||
375 | |||
376 | define_machine(mpc885_ads) { | ||
377 | .name = "MPC885 ADS", | ||
378 | .probe = mpc885ads_probe, | ||
379 | .setup_arch = mpc885ads_setup_arch, | ||
380 | .init_IRQ = m8xx_pic_init, | ||
381 | .show_cpuinfo = mpc8xx_show_cpuinfo, | ||
382 | .get_irq = mpc8xx_get_irq, | ||
383 | .restart = mpc8xx_restart, | ||
384 | .calibrate_decr = mpc8xx_calibrate_decr, | ||
385 | .set_rtc_time = mpc8xx_set_rtc_time, | ||
386 | .get_rtc_time = mpc8xx_get_rtc_time, | ||
387 | }; | ||
diff --git a/arch/powerpc/platforms/Makefile b/arch/powerpc/platforms/Makefile index 507d1b98f270..65e612315b9b 100644 --- a/arch/powerpc/platforms/Makefile +++ b/arch/powerpc/platforms/Makefile | |||
@@ -8,6 +8,8 @@ endif | |||
8 | obj-$(CONFIG_PPC_MPC52xx) += 52xx/ | 8 | obj-$(CONFIG_PPC_MPC52xx) += 52xx/ |
9 | obj-$(CONFIG_PPC_CHRP) += chrp/ | 9 | obj-$(CONFIG_PPC_CHRP) += chrp/ |
10 | obj-$(CONFIG_4xx) += 4xx/ | 10 | obj-$(CONFIG_4xx) += 4xx/ |
11 | obj-$(CONFIG_PPC_8xx) += 8xx/ | ||
12 | obj-$(CONFIG_PPC_82xx) += 82xx/ | ||
11 | obj-$(CONFIG_PPC_83xx) += 83xx/ | 13 | obj-$(CONFIG_PPC_83xx) += 83xx/ |
12 | obj-$(CONFIG_PPC_85xx) += 85xx/ | 14 | obj-$(CONFIG_PPC_85xx) += 85xx/ |
13 | obj-$(CONFIG_PPC_86xx) += 86xx/ | 15 | obj-$(CONFIG_PPC_86xx) += 86xx/ |
@@ -17,4 +19,5 @@ obj-$(CONFIG_PPC_MAPLE) += maple/ | |||
17 | obj-$(CONFIG_PPC_PASEMI) += pasemi/ | 19 | obj-$(CONFIG_PPC_PASEMI) += pasemi/ |
18 | obj-$(CONFIG_PPC_CELL) += cell/ | 20 | obj-$(CONFIG_PPC_CELL) += cell/ |
19 | obj-$(CONFIG_PPC_PS3) += ps3/ | 21 | obj-$(CONFIG_PPC_PS3) += ps3/ |
22 | obj-$(CONFIG_PPC_CELLEB) += celleb/ | ||
20 | obj-$(CONFIG_EMBEDDED6xx) += embedded6xx/ | 23 | obj-$(CONFIG_EMBEDDED6xx) += embedded6xx/ |
diff --git a/arch/powerpc/platforms/cell/Makefile b/arch/powerpc/platforms/cell/Makefile index f90e8337796c..869af89df6ff 100644 --- a/arch/powerpc/platforms/cell/Makefile +++ b/arch/powerpc/platforms/cell/Makefile | |||
@@ -14,7 +14,12 @@ endif | |||
14 | spufs-modular-$(CONFIG_SPU_FS) += spu_syscalls.o | 14 | spufs-modular-$(CONFIG_SPU_FS) += spu_syscalls.o |
15 | spu-priv1-$(CONFIG_PPC_CELL_NATIVE) += spu_priv1_mmio.o | 15 | spu-priv1-$(CONFIG_PPC_CELL_NATIVE) += spu_priv1_mmio.o |
16 | 16 | ||
17 | spu-manage-$(CONFIG_PPC_CELLEB) += spu_manage.o | ||
18 | spu-manage-$(CONFIG_PPC_CELL_NATIVE) += spu_manage.o | ||
19 | |||
17 | obj-$(CONFIG_SPU_BASE) += spu_callbacks.o spu_base.o \ | 20 | obj-$(CONFIG_SPU_BASE) += spu_callbacks.o spu_base.o \ |
18 | spu_coredump.o \ | 21 | spu_coredump.o \ |
19 | $(spufs-modular-m) \ | 22 | $(spufs-modular-m) \ |
20 | $(spu-priv1-y) spufs/ | 23 | $(spu-priv1-y) \ |
24 | $(spu-manage-y) \ | ||
25 | spufs/ | ||
diff --git a/arch/powerpc/platforms/cell/spu_base.c b/arch/powerpc/platforms/cell/spu_base.c index bd7bffc3ddd0..c43999a10deb 100644 --- a/arch/powerpc/platforms/cell/spu_base.c +++ b/arch/powerpc/platforms/cell/spu_base.c | |||
@@ -170,9 +170,11 @@ int | |||
170 | spu_irq_class_0_bottom(struct spu *spu) | 170 | spu_irq_class_0_bottom(struct spu *spu) |
171 | { | 171 | { |
172 | unsigned long stat, mask; | 172 | unsigned long stat, mask; |
173 | unsigned long flags; | ||
173 | 174 | ||
174 | spu->class_0_pending = 0; | 175 | spu->class_0_pending = 0; |
175 | 176 | ||
177 | spin_lock_irqsave(&spu->register_lock, flags); | ||
176 | mask = spu_int_mask_get(spu, 0); | 178 | mask = spu_int_mask_get(spu, 0); |
177 | stat = spu_int_stat_get(spu, 0); | 179 | stat = spu_int_stat_get(spu, 0); |
178 | 180 | ||
@@ -188,6 +190,7 @@ spu_irq_class_0_bottom(struct spu *spu) | |||
188 | __spu_trap_error(spu); | 190 | __spu_trap_error(spu); |
189 | 191 | ||
190 | spu_int_stat_clear(spu, 0, stat); | 192 | spu_int_stat_clear(spu, 0, stat); |
193 | spin_unlock_irqrestore(&spu->register_lock, flags); | ||
191 | 194 | ||
192 | return (stat & 0x7) ? -EIO : 0; | 195 | return (stat & 0x7) ? -EIO : 0; |
193 | } | 196 | } |
diff --git a/arch/powerpc/platforms/cell/spu_manage.c b/arch/powerpc/platforms/cell/spu_manage.c new file mode 100644 index 000000000000..d8b39fe39cdd --- /dev/null +++ b/arch/powerpc/platforms/cell/spu_manage.c | |||
@@ -0,0 +1,420 @@ | |||
1 | /* | ||
2 | * spu management operations for of based platforms | ||
3 | * | ||
4 | * (C) Copyright IBM Deutschland Entwicklung GmbH 2005 | ||
5 | * Copyright 2006 Sony Corp. | ||
6 | * (C) Copyright 2007 TOSHIBA CORPORATION | ||
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; version 2 of the License. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU General Public License along | ||
18 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
19 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
20 | */ | ||
21 | |||
22 | #include <linux/interrupt.h> | ||
23 | #include <linux/list.h> | ||
24 | #include <linux/module.h> | ||
25 | #include <linux/ptrace.h> | ||
26 | #include <linux/slab.h> | ||
27 | #include <linux/wait.h> | ||
28 | #include <linux/mm.h> | ||
29 | #include <linux/io.h> | ||
30 | #include <linux/mutex.h> | ||
31 | #include <linux/device.h> | ||
32 | |||
33 | #include <asm/spu.h> | ||
34 | #include <asm/spu_priv1.h> | ||
35 | #include <asm/firmware.h> | ||
36 | #include <asm/prom.h> | ||
37 | |||
38 | #include "interrupt.h" | ||
39 | |||
40 | struct device_node *spu_devnode(struct spu *spu) | ||
41 | { | ||
42 | return spu->devnode; | ||
43 | } | ||
44 | |||
45 | EXPORT_SYMBOL_GPL(spu_devnode); | ||
46 | |||
47 | static u64 __init find_spu_unit_number(struct device_node *spe) | ||
48 | { | ||
49 | const unsigned int *prop; | ||
50 | int proplen; | ||
51 | prop = get_property(spe, "unit-id", &proplen); | ||
52 | if (proplen == 4) | ||
53 | return (u64)*prop; | ||
54 | |||
55 | prop = get_property(spe, "reg", &proplen); | ||
56 | if (proplen == 4) | ||
57 | return (u64)*prop; | ||
58 | |||
59 | return 0; | ||
60 | } | ||
61 | |||
62 | static int __init cell_spuprop_present(struct spu *spu, struct device_node *spe, | ||
63 | const char *prop) | ||
64 | { | ||
65 | const struct address_prop { | ||
66 | unsigned long address; | ||
67 | unsigned int len; | ||
68 | } __attribute__((packed)) *p; | ||
69 | int proplen; | ||
70 | |||
71 | unsigned long start_pfn, nr_pages; | ||
72 | struct pglist_data *pgdata; | ||
73 | struct zone *zone; | ||
74 | int ret; | ||
75 | |||
76 | p = get_property(spe, prop, &proplen); | ||
77 | WARN_ON(proplen != sizeof (*p)); | ||
78 | |||
79 | start_pfn = p->address >> PAGE_SHIFT; | ||
80 | nr_pages = ((unsigned long)p->len + PAGE_SIZE - 1) >> PAGE_SHIFT; | ||
81 | |||
82 | pgdata = NODE_DATA(spu->node); | ||
83 | zone = pgdata->node_zones; | ||
84 | |||
85 | ret = __add_pages(zone, start_pfn, nr_pages); | ||
86 | |||
87 | return ret; | ||
88 | } | ||
89 | |||
90 | static void __iomem * __init map_spe_prop(struct spu *spu, | ||
91 | struct device_node *n, const char *name) | ||
92 | { | ||
93 | const struct address_prop { | ||
94 | unsigned long address; | ||
95 | unsigned int len; | ||
96 | } __attribute__((packed)) *prop; | ||
97 | |||
98 | const void *p; | ||
99 | int proplen; | ||
100 | void __iomem *ret = NULL; | ||
101 | int err = 0; | ||
102 | |||
103 | p = get_property(n, name, &proplen); | ||
104 | if (proplen != sizeof (struct address_prop)) | ||
105 | return NULL; | ||
106 | |||
107 | prop = p; | ||
108 | |||
109 | err = cell_spuprop_present(spu, n, name); | ||
110 | if (err && (err != -EEXIST)) | ||
111 | goto out; | ||
112 | |||
113 | ret = ioremap(prop->address, prop->len); | ||
114 | |||
115 | out: | ||
116 | return ret; | ||
117 | } | ||
118 | |||
119 | static void spu_unmap(struct spu *spu) | ||
120 | { | ||
121 | if (!firmware_has_feature(FW_FEATURE_LPAR)) | ||
122 | iounmap(spu->priv1); | ||
123 | iounmap(spu->priv2); | ||
124 | iounmap(spu->problem); | ||
125 | iounmap((__force u8 __iomem *)spu->local_store); | ||
126 | } | ||
127 | |||
128 | static int __init spu_map_interrupts_old(struct spu *spu, | ||
129 | struct device_node *np) | ||
130 | { | ||
131 | unsigned int isrc; | ||
132 | const u32 *tmp; | ||
133 | int nid; | ||
134 | |||
135 | /* Get the interrupt source unit from the device-tree */ | ||
136 | tmp = get_property(np, "isrc", NULL); | ||
137 | if (!tmp) | ||
138 | return -ENODEV; | ||
139 | isrc = tmp[0]; | ||
140 | |||
141 | tmp = get_property(np->parent->parent, "node-id", NULL); | ||
142 | if (!tmp) { | ||
143 | printk(KERN_WARNING "%s: can't find node-id\n", __FUNCTION__); | ||
144 | nid = spu->node; | ||
145 | } else | ||
146 | nid = tmp[0]; | ||
147 | |||
148 | /* Add the node number */ | ||
149 | isrc |= nid << IIC_IRQ_NODE_SHIFT; | ||
150 | |||
151 | /* Now map interrupts of all 3 classes */ | ||
152 | spu->irqs[0] = irq_create_mapping(NULL, IIC_IRQ_CLASS_0 | isrc); | ||
153 | spu->irqs[1] = irq_create_mapping(NULL, IIC_IRQ_CLASS_1 | isrc); | ||
154 | spu->irqs[2] = irq_create_mapping(NULL, IIC_IRQ_CLASS_2 | isrc); | ||
155 | |||
156 | /* Right now, we only fail if class 2 failed */ | ||
157 | return spu->irqs[2] == NO_IRQ ? -EINVAL : 0; | ||
158 | } | ||
159 | |||
160 | static int __init spu_map_device_old(struct spu *spu) | ||
161 | { | ||
162 | struct device_node *node = spu->devnode; | ||
163 | const char *prop; | ||
164 | int ret; | ||
165 | |||
166 | ret = -ENODEV; | ||
167 | spu->name = get_property(node, "name", NULL); | ||
168 | if (!spu->name) | ||
169 | goto out; | ||
170 | |||
171 | prop = get_property(node, "local-store", NULL); | ||
172 | if (!prop) | ||
173 | goto out; | ||
174 | spu->local_store_phys = *(unsigned long *)prop; | ||
175 | |||
176 | /* we use local store as ram, not io memory */ | ||
177 | spu->local_store = (void __force *) | ||
178 | map_spe_prop(spu, node, "local-store"); | ||
179 | if (!spu->local_store) | ||
180 | goto out; | ||
181 | |||
182 | prop = get_property(node, "problem", NULL); | ||
183 | if (!prop) | ||
184 | goto out_unmap; | ||
185 | spu->problem_phys = *(unsigned long *)prop; | ||
186 | |||
187 | spu->problem = map_spe_prop(spu, node, "problem"); | ||
188 | if (!spu->problem) | ||
189 | goto out_unmap; | ||
190 | |||
191 | spu->priv2 = map_spe_prop(spu, node, "priv2"); | ||
192 | if (!spu->priv2) | ||
193 | goto out_unmap; | ||
194 | |||
195 | if (!firmware_has_feature(FW_FEATURE_LPAR)) { | ||
196 | spu->priv1 = map_spe_prop(spu, node, "priv1"); | ||
197 | if (!spu->priv1) | ||
198 | goto out_unmap; | ||
199 | } | ||
200 | |||
201 | ret = 0; | ||
202 | goto out; | ||
203 | |||
204 | out_unmap: | ||
205 | spu_unmap(spu); | ||
206 | out: | ||
207 | return ret; | ||
208 | } | ||
209 | |||
210 | static int __init spu_map_interrupts(struct spu *spu, struct device_node *np) | ||
211 | { | ||
212 | struct of_irq oirq; | ||
213 | int ret; | ||
214 | int i; | ||
215 | |||
216 | for (i=0; i < 3; i++) { | ||
217 | ret = of_irq_map_one(np, i, &oirq); | ||
218 | if (ret) { | ||
219 | pr_debug("spu_new: failed to get irq %d\n", i); | ||
220 | goto err; | ||
221 | } | ||
222 | ret = -EINVAL; | ||
223 | pr_debug(" irq %d no 0x%x on %s\n", i, oirq.specifier[0], | ||
224 | oirq.controller->full_name); | ||
225 | spu->irqs[i] = irq_create_of_mapping(oirq.controller, | ||
226 | oirq.specifier, oirq.size); | ||
227 | if (spu->irqs[i] == NO_IRQ) { | ||
228 | pr_debug("spu_new: failed to map it !\n"); | ||
229 | goto err; | ||
230 | } | ||
231 | } | ||
232 | return 0; | ||
233 | |||
234 | err: | ||
235 | pr_debug("failed to map irq %x for spu %s\n", *oirq.specifier, | ||
236 | spu->name); | ||
237 | for (; i >= 0; i--) { | ||
238 | if (spu->irqs[i] != NO_IRQ) | ||
239 | irq_dispose_mapping(spu->irqs[i]); | ||
240 | } | ||
241 | return ret; | ||
242 | } | ||
243 | |||
244 | static int spu_map_resource(struct spu *spu, int nr, | ||
245 | void __iomem** virt, unsigned long *phys) | ||
246 | { | ||
247 | struct device_node *np = spu->devnode; | ||
248 | unsigned long start_pfn, nr_pages; | ||
249 | struct pglist_data *pgdata; | ||
250 | struct zone *zone; | ||
251 | struct resource resource = { }; | ||
252 | unsigned long len; | ||
253 | int ret; | ||
254 | |||
255 | ret = of_address_to_resource(np, nr, &resource); | ||
256 | if (ret) | ||
257 | goto out; | ||
258 | |||
259 | if (phys) | ||
260 | *phys = resource.start; | ||
261 | len = resource.end - resource.start + 1; | ||
262 | *virt = ioremap(resource.start, len); | ||
263 | if (!*virt) | ||
264 | ret = -EINVAL; | ||
265 | |||
266 | start_pfn = resource.start >> PAGE_SHIFT; | ||
267 | nr_pages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT; | ||
268 | |||
269 | pgdata = NODE_DATA(spu->node); | ||
270 | zone = pgdata->node_zones; | ||
271 | |||
272 | ret = __add_pages(zone, start_pfn, nr_pages); | ||
273 | |||
274 | out: | ||
275 | return ret; | ||
276 | } | ||
277 | |||
278 | static int __init spu_map_device(struct spu *spu) | ||
279 | { | ||
280 | struct device_node *np = spu->devnode; | ||
281 | int ret = -ENODEV; | ||
282 | |||
283 | spu->name = get_property(np, "name", NULL); | ||
284 | if (!spu->name) | ||
285 | goto out; | ||
286 | |||
287 | ret = spu_map_resource(spu, 0, (void __iomem**)&spu->local_store, | ||
288 | &spu->local_store_phys); | ||
289 | if (ret) { | ||
290 | pr_debug("spu_new: failed to map %s resource 0\n", | ||
291 | np->full_name); | ||
292 | goto out; | ||
293 | } | ||
294 | ret = spu_map_resource(spu, 1, (void __iomem**)&spu->problem, | ||
295 | &spu->problem_phys); | ||
296 | if (ret) { | ||
297 | pr_debug("spu_new: failed to map %s resource 1\n", | ||
298 | np->full_name); | ||
299 | goto out_unmap; | ||
300 | } | ||
301 | ret = spu_map_resource(spu, 2, (void __iomem**)&spu->priv2, NULL); | ||
302 | if (ret) { | ||
303 | pr_debug("spu_new: failed to map %s resource 2\n", | ||
304 | np->full_name); | ||
305 | goto out_unmap; | ||
306 | } | ||
307 | if (!firmware_has_feature(FW_FEATURE_LPAR)) | ||
308 | ret = spu_map_resource(spu, 3, | ||
309 | (void __iomem**)&spu->priv1, NULL); | ||
310 | if (ret) { | ||
311 | pr_debug("spu_new: failed to map %s resource 3\n", | ||
312 | np->full_name); | ||
313 | goto out_unmap; | ||
314 | } | ||
315 | pr_debug("spu_new: %s maps:\n", np->full_name); | ||
316 | pr_debug(" local store : 0x%016lx -> 0x%p\n", | ||
317 | spu->local_store_phys, spu->local_store); | ||
318 | pr_debug(" problem state : 0x%016lx -> 0x%p\n", | ||
319 | spu->problem_phys, spu->problem); | ||
320 | pr_debug(" priv2 : 0x%p\n", spu->priv2); | ||
321 | pr_debug(" priv1 : 0x%p\n", spu->priv1); | ||
322 | |||
323 | return 0; | ||
324 | |||
325 | out_unmap: | ||
326 | spu_unmap(spu); | ||
327 | out: | ||
328 | pr_debug("failed to map spe %s: %d\n", spu->name, ret); | ||
329 | return ret; | ||
330 | } | ||
331 | |||
332 | static int __init of_enumerate_spus(int (*fn)(void *data)) | ||
333 | { | ||
334 | int ret; | ||
335 | struct device_node *node; | ||
336 | |||
337 | ret = -ENODEV; | ||
338 | for (node = of_find_node_by_type(NULL, "spe"); | ||
339 | node; node = of_find_node_by_type(node, "spe")) { | ||
340 | ret = fn(node); | ||
341 | if (ret) { | ||
342 | printk(KERN_WARNING "%s: Error initializing %s\n", | ||
343 | __FUNCTION__, node->name); | ||
344 | break; | ||
345 | } | ||
346 | } | ||
347 | return ret; | ||
348 | } | ||
349 | |||
350 | static int __init of_create_spu(struct spu *spu, void *data) | ||
351 | { | ||
352 | int ret; | ||
353 | struct device_node *spe = (struct device_node *)data; | ||
354 | static int legacy_map = 0, legacy_irq = 0; | ||
355 | |||
356 | spu->devnode = of_node_get(spe); | ||
357 | spu->spe_id = find_spu_unit_number(spe); | ||
358 | |||
359 | spu->node = of_node_to_nid(spe); | ||
360 | if (spu->node >= MAX_NUMNODES) { | ||
361 | printk(KERN_WARNING "SPE %s on node %d ignored," | ||
362 | " node number too big\n", spe->full_name, spu->node); | ||
363 | printk(KERN_WARNING "Check if CONFIG_NUMA is enabled.\n"); | ||
364 | ret = -ENODEV; | ||
365 | goto out; | ||
366 | } | ||
367 | |||
368 | ret = spu_map_device(spu); | ||
369 | if (ret) { | ||
370 | if (!legacy_map) { | ||
371 | legacy_map = 1; | ||
372 | printk(KERN_WARNING "%s: Legacy device tree found, " | ||
373 | "trying to map old style\n", __FUNCTION__); | ||
374 | } | ||
375 | ret = spu_map_device_old(spu); | ||
376 | if (ret) { | ||
377 | printk(KERN_ERR "Unable to map %s\n", | ||
378 | spu->name); | ||
379 | goto out; | ||
380 | } | ||
381 | } | ||
382 | |||
383 | ret = spu_map_interrupts(spu, spe); | ||
384 | if (ret) { | ||
385 | if (!legacy_irq) { | ||
386 | legacy_irq = 1; | ||
387 | printk(KERN_WARNING "%s: Legacy device tree found, " | ||
388 | "trying old style irq\n", __FUNCTION__); | ||
389 | } | ||
390 | ret = spu_map_interrupts_old(spu, spe); | ||
391 | if (ret) { | ||
392 | printk(KERN_ERR "%s: could not map interrupts", | ||
393 | spu->name); | ||
394 | goto out_unmap; | ||
395 | } | ||
396 | } | ||
397 | |||
398 | pr_debug("Using SPE %s %p %p %p %p %d\n", spu->name, | ||
399 | spu->local_store, spu->problem, spu->priv1, | ||
400 | spu->priv2, spu->number); | ||
401 | goto out; | ||
402 | |||
403 | out_unmap: | ||
404 | spu_unmap(spu); | ||
405 | out: | ||
406 | return ret; | ||
407 | } | ||
408 | |||
409 | static int of_destroy_spu(struct spu *spu) | ||
410 | { | ||
411 | spu_unmap(spu); | ||
412 | of_node_put(spu->devnode); | ||
413 | return 0; | ||
414 | } | ||
415 | |||
416 | const struct spu_management_ops spu_management_of_ops = { | ||
417 | .enumerate_spus = of_enumerate_spus, | ||
418 | .create_spu = of_create_spu, | ||
419 | .destroy_spu = of_destroy_spu, | ||
420 | }; | ||
diff --git a/arch/powerpc/platforms/cell/spu_priv1_mmio.c b/arch/powerpc/platforms/cell/spu_priv1_mmio.c index 910a926b61a2..67fa7247b80a 100644 --- a/arch/powerpc/platforms/cell/spu_priv1_mmio.c +++ b/arch/powerpc/platforms/cell/spu_priv1_mmio.c | |||
@@ -37,490 +37,112 @@ | |||
37 | #include "interrupt.h" | 37 | #include "interrupt.h" |
38 | #include "spu_priv1_mmio.h" | 38 | #include "spu_priv1_mmio.h" |
39 | 39 | ||
40 | static DEFINE_MUTEX(add_spumem_mutex); | ||
41 | |||
42 | struct spu_pdata { | ||
43 | struct device_node *devnode; | ||
44 | struct spu_priv1 __iomem *priv1; | ||
45 | }; | ||
46 | |||
47 | static struct spu_pdata *spu_get_pdata(struct spu *spu) | ||
48 | { | ||
49 | BUG_ON(!spu->pdata); | ||
50 | return spu->pdata; | ||
51 | } | ||
52 | |||
53 | struct device_node *spu_devnode(struct spu *spu) | ||
54 | { | ||
55 | return spu_get_pdata(spu)->devnode; | ||
56 | } | ||
57 | |||
58 | EXPORT_SYMBOL_GPL(spu_devnode); | ||
59 | |||
60 | static int __init cell_spuprop_present(struct spu *spu, struct device_node *spe, | ||
61 | const char *prop) | ||
62 | { | ||
63 | const struct address_prop { | ||
64 | unsigned long address; | ||
65 | unsigned int len; | ||
66 | } __attribute__((packed)) *p; | ||
67 | int proplen; | ||
68 | |||
69 | unsigned long start_pfn, nr_pages; | ||
70 | struct pglist_data *pgdata; | ||
71 | struct zone *zone; | ||
72 | int ret; | ||
73 | |||
74 | p = get_property(spe, prop, &proplen); | ||
75 | WARN_ON(proplen != sizeof (*p)); | ||
76 | |||
77 | start_pfn = p->address >> PAGE_SHIFT; | ||
78 | nr_pages = ((unsigned long)p->len + PAGE_SIZE - 1) >> PAGE_SHIFT; | ||
79 | |||
80 | pgdata = NODE_DATA(spu->node); | ||
81 | zone = pgdata->node_zones; | ||
82 | |||
83 | /* XXX rethink locking here */ | ||
84 | mutex_lock(&add_spumem_mutex); | ||
85 | ret = __add_pages(zone, start_pfn, nr_pages); | ||
86 | mutex_unlock(&add_spumem_mutex); | ||
87 | |||
88 | return ret; | ||
89 | } | ||
90 | |||
91 | static void __iomem * __init map_spe_prop(struct spu *spu, | ||
92 | struct device_node *n, const char *name) | ||
93 | { | ||
94 | const struct address_prop { | ||
95 | unsigned long address; | ||
96 | unsigned int len; | ||
97 | } __attribute__((packed)) *prop; | ||
98 | |||
99 | const void *p; | ||
100 | int proplen; | ||
101 | void __iomem *ret = NULL; | ||
102 | int err = 0; | ||
103 | |||
104 | p = get_property(n, name, &proplen); | ||
105 | if (proplen != sizeof (struct address_prop)) | ||
106 | return NULL; | ||
107 | |||
108 | prop = p; | ||
109 | |||
110 | err = cell_spuprop_present(spu, n, name); | ||
111 | if (err && (err != -EEXIST)) | ||
112 | goto out; | ||
113 | |||
114 | ret = ioremap(prop->address, prop->len); | ||
115 | |||
116 | out: | ||
117 | return ret; | ||
118 | } | ||
119 | |||
120 | static void spu_unmap(struct spu *spu) | ||
121 | { | ||
122 | iounmap(spu->priv2); | ||
123 | iounmap(spu_get_pdata(spu)->priv1); | ||
124 | iounmap(spu->problem); | ||
125 | iounmap((__force u8 __iomem *)spu->local_store); | ||
126 | } | ||
127 | |||
128 | static int __init spu_map_interrupts_old(struct spu *spu, | ||
129 | struct device_node *np) | ||
130 | { | ||
131 | unsigned int isrc; | ||
132 | const u32 *tmp; | ||
133 | int nid; | ||
134 | |||
135 | /* Get the interrupt source unit from the device-tree */ | ||
136 | tmp = get_property(np, "isrc", NULL); | ||
137 | if (!tmp) | ||
138 | return -ENODEV; | ||
139 | isrc = tmp[0]; | ||
140 | |||
141 | tmp = get_property(np->parent->parent, "node-id", NULL); | ||
142 | if (!tmp) { | ||
143 | printk(KERN_WARNING "%s: can't find node-id\n", __FUNCTION__); | ||
144 | nid = spu->node; | ||
145 | } else | ||
146 | nid = tmp[0]; | ||
147 | |||
148 | /* Add the node number */ | ||
149 | isrc |= nid << IIC_IRQ_NODE_SHIFT; | ||
150 | |||
151 | /* Now map interrupts of all 3 classes */ | ||
152 | spu->irqs[0] = irq_create_mapping(NULL, IIC_IRQ_CLASS_0 | isrc); | ||
153 | spu->irqs[1] = irq_create_mapping(NULL, IIC_IRQ_CLASS_1 | isrc); | ||
154 | spu->irqs[2] = irq_create_mapping(NULL, IIC_IRQ_CLASS_2 | isrc); | ||
155 | |||
156 | /* Right now, we only fail if class 2 failed */ | ||
157 | return spu->irqs[2] == NO_IRQ ? -EINVAL : 0; | ||
158 | } | ||
159 | |||
160 | static int __init spu_map_device_old(struct spu *spu, struct device_node *node) | ||
161 | { | ||
162 | const char *prop; | ||
163 | int ret; | ||
164 | |||
165 | ret = -ENODEV; | ||
166 | spu->name = get_property(node, "name", NULL); | ||
167 | if (!spu->name) | ||
168 | goto out; | ||
169 | |||
170 | prop = get_property(node, "local-store", NULL); | ||
171 | if (!prop) | ||
172 | goto out; | ||
173 | spu->local_store_phys = *(unsigned long *)prop; | ||
174 | |||
175 | /* we use local store as ram, not io memory */ | ||
176 | spu->local_store = (void __force *) | ||
177 | map_spe_prop(spu, node, "local-store"); | ||
178 | if (!spu->local_store) | ||
179 | goto out; | ||
180 | |||
181 | prop = get_property(node, "problem", NULL); | ||
182 | if (!prop) | ||
183 | goto out_unmap; | ||
184 | spu->problem_phys = *(unsigned long *)prop; | ||
185 | |||
186 | spu->problem= map_spe_prop(spu, node, "problem"); | ||
187 | if (!spu->problem) | ||
188 | goto out_unmap; | ||
189 | |||
190 | spu_get_pdata(spu)->priv1= map_spe_prop(spu, node, "priv1"); | ||
191 | |||
192 | spu->priv2= map_spe_prop(spu, node, "priv2"); | ||
193 | if (!spu->priv2) | ||
194 | goto out_unmap; | ||
195 | ret = 0; | ||
196 | goto out; | ||
197 | |||
198 | out_unmap: | ||
199 | spu_unmap(spu); | ||
200 | out: | ||
201 | return ret; | ||
202 | } | ||
203 | |||
204 | static int __init spu_map_interrupts(struct spu *spu, struct device_node *np) | ||
205 | { | ||
206 | struct of_irq oirq; | ||
207 | int ret; | ||
208 | int i; | ||
209 | |||
210 | for (i=0; i < 3; i++) { | ||
211 | ret = of_irq_map_one(np, i, &oirq); | ||
212 | if (ret) { | ||
213 | pr_debug("spu_new: failed to get irq %d\n", i); | ||
214 | goto err; | ||
215 | } | ||
216 | ret = -EINVAL; | ||
217 | pr_debug(" irq %d no 0x%x on %s\n", i, oirq.specifier[0], | ||
218 | oirq.controller->full_name); | ||
219 | spu->irqs[i] = irq_create_of_mapping(oirq.controller, | ||
220 | oirq.specifier, oirq.size); | ||
221 | if (spu->irqs[i] == NO_IRQ) { | ||
222 | pr_debug("spu_new: failed to map it !\n"); | ||
223 | goto err; | ||
224 | } | ||
225 | } | ||
226 | return 0; | ||
227 | |||
228 | err: | ||
229 | pr_debug("failed to map irq %x for spu %s\n", *oirq.specifier, | ||
230 | spu->name); | ||
231 | for (; i >= 0; i--) { | ||
232 | if (spu->irqs[i] != NO_IRQ) | ||
233 | irq_dispose_mapping(spu->irqs[i]); | ||
234 | } | ||
235 | return ret; | ||
236 | } | ||
237 | |||
238 | static int spu_map_resource(struct spu *spu, int nr, | ||
239 | void __iomem** virt, unsigned long *phys) | ||
240 | { | ||
241 | struct device_node *np = spu_get_pdata(spu)->devnode; | ||
242 | unsigned long start_pfn, nr_pages; | ||
243 | struct pglist_data *pgdata; | ||
244 | struct zone *zone; | ||
245 | struct resource resource = { }; | ||
246 | unsigned long len; | ||
247 | int ret; | ||
248 | |||
249 | ret = of_address_to_resource(np, nr, &resource); | ||
250 | if (ret) | ||
251 | goto out; | ||
252 | |||
253 | if (phys) | ||
254 | *phys = resource.start; | ||
255 | len = resource.end - resource.start + 1; | ||
256 | *virt = ioremap(resource.start, len); | ||
257 | if (!*virt) | ||
258 | ret = -EINVAL; | ||
259 | |||
260 | start_pfn = resource.start >> PAGE_SHIFT; | ||
261 | nr_pages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT; | ||
262 | |||
263 | pgdata = NODE_DATA(spu->node); | ||
264 | zone = pgdata->node_zones; | ||
265 | |||
266 | /* XXX rethink locking here */ | ||
267 | mutex_lock(&add_spumem_mutex); | ||
268 | ret = __add_pages(zone, start_pfn, nr_pages); | ||
269 | mutex_unlock(&add_spumem_mutex); | ||
270 | |||
271 | out: | ||
272 | return ret; | ||
273 | } | ||
274 | |||
275 | static int __init spu_map_device(struct spu *spu) | ||
276 | { | ||
277 | struct device_node *np = spu_get_pdata(spu)->devnode; | ||
278 | int ret = -ENODEV; | ||
279 | |||
280 | spu->name = get_property(np, "name", NULL); | ||
281 | if (!spu->name) | ||
282 | goto out; | ||
283 | |||
284 | ret = spu_map_resource(spu, 0, (void __iomem**)&spu->local_store, | ||
285 | &spu->local_store_phys); | ||
286 | if (ret) { | ||
287 | pr_debug("spu_new: failed to map %s resource 0\n", | ||
288 | np->full_name); | ||
289 | goto out; | ||
290 | } | ||
291 | ret = spu_map_resource(spu, 1, (void __iomem**)&spu->problem, | ||
292 | &spu->problem_phys); | ||
293 | if (ret) { | ||
294 | pr_debug("spu_new: failed to map %s resource 1\n", | ||
295 | np->full_name); | ||
296 | goto out_unmap; | ||
297 | } | ||
298 | ret = spu_map_resource(spu, 2, (void __iomem**)&spu->priv2, NULL); | ||
299 | if (ret) { | ||
300 | pr_debug("spu_new: failed to map %s resource 2\n", | ||
301 | np->full_name); | ||
302 | goto out_unmap; | ||
303 | } | ||
304 | if (!firmware_has_feature(FW_FEATURE_LPAR)) | ||
305 | ret = spu_map_resource(spu, 3, | ||
306 | (void __iomem**)&spu_get_pdata(spu)->priv1, NULL); | ||
307 | if (ret) { | ||
308 | pr_debug("spu_new: failed to map %s resource 3\n", | ||
309 | np->full_name); | ||
310 | goto out_unmap; | ||
311 | } | ||
312 | pr_debug("spu_new: %s maps:\n", np->full_name); | ||
313 | pr_debug(" local store : 0x%016lx -> 0x%p\n", | ||
314 | spu->local_store_phys, spu->local_store); | ||
315 | pr_debug(" problem state : 0x%016lx -> 0x%p\n", | ||
316 | spu->problem_phys, spu->problem); | ||
317 | pr_debug(" priv2 : 0x%p\n", spu->priv2); | ||
318 | pr_debug(" priv1 : 0x%p\n", | ||
319 | spu_get_pdata(spu)->priv1); | ||
320 | |||
321 | return 0; | ||
322 | |||
323 | out_unmap: | ||
324 | spu_unmap(spu); | ||
325 | out: | ||
326 | pr_debug("failed to map spe %s: %d\n", spu->name, ret); | ||
327 | return ret; | ||
328 | } | ||
329 | |||
330 | static int __init of_enumerate_spus(int (*fn)(void *data)) | ||
331 | { | ||
332 | int ret; | ||
333 | struct device_node *node; | ||
334 | |||
335 | ret = -ENODEV; | ||
336 | for (node = of_find_node_by_type(NULL, "spe"); | ||
337 | node; node = of_find_node_by_type(node, "spe")) { | ||
338 | ret = fn(node); | ||
339 | if (ret) { | ||
340 | printk(KERN_WARNING "%s: Error initializing %s\n", | ||
341 | __FUNCTION__, node->name); | ||
342 | break; | ||
343 | } | ||
344 | } | ||
345 | return ret; | ||
346 | } | ||
347 | |||
348 | static int __init of_create_spu(struct spu *spu, void *data) | ||
349 | { | ||
350 | int ret; | ||
351 | struct device_node *spe = (struct device_node *)data; | ||
352 | |||
353 | spu->pdata = kzalloc(sizeof(struct spu_pdata), | ||
354 | GFP_KERNEL); | ||
355 | if (!spu->pdata) { | ||
356 | ret = -ENOMEM; | ||
357 | goto out; | ||
358 | } | ||
359 | spu_get_pdata(spu)->devnode = of_node_get(spe); | ||
360 | |||
361 | spu->node = of_node_to_nid(spe); | ||
362 | if (spu->node >= MAX_NUMNODES) { | ||
363 | printk(KERN_WARNING "SPE %s on node %d ignored," | ||
364 | " node number too big\n", spe->full_name, spu->node); | ||
365 | printk(KERN_WARNING "Check if CONFIG_NUMA is enabled.\n"); | ||
366 | ret = -ENODEV; | ||
367 | goto out_free; | ||
368 | } | ||
369 | |||
370 | ret = spu_map_device(spu); | ||
371 | /* try old method */ | ||
372 | if (ret) | ||
373 | ret = spu_map_device_old(spu, spe); | ||
374 | if (ret) | ||
375 | goto out_free; | ||
376 | |||
377 | ret = spu_map_interrupts(spu, spe); | ||
378 | if (ret) | ||
379 | ret = spu_map_interrupts_old(spu, spe); | ||
380 | if (ret) | ||
381 | goto out_unmap; | ||
382 | |||
383 | pr_debug(KERN_DEBUG "Using SPE %s %p %p %p %p %d\n", spu->name, | ||
384 | spu->local_store, spu->problem, spu_get_pdata(spu)->priv1, | ||
385 | spu->priv2, spu->number); | ||
386 | goto out; | ||
387 | |||
388 | out_unmap: | ||
389 | spu_unmap(spu); | ||
390 | out_free: | ||
391 | kfree(spu->pdata); | ||
392 | spu->pdata = NULL; | ||
393 | out: | ||
394 | return ret; | ||
395 | } | ||
396 | |||
397 | static int of_destroy_spu(struct spu *spu) | ||
398 | { | ||
399 | spu_unmap(spu); | ||
400 | of_node_put(spu_get_pdata(spu)->devnode); | ||
401 | kfree(spu->pdata); | ||
402 | spu->pdata = NULL; | ||
403 | return 0; | ||
404 | } | ||
405 | |||
406 | const struct spu_management_ops spu_management_of_ops = { | ||
407 | .enumerate_spus = of_enumerate_spus, | ||
408 | .create_spu = of_create_spu, | ||
409 | .destroy_spu = of_destroy_spu, | ||
410 | }; | ||
411 | |||
412 | static void int_mask_and(struct spu *spu, int class, u64 mask) | 40 | static void int_mask_and(struct spu *spu, int class, u64 mask) |
413 | { | 41 | { |
414 | u64 old_mask; | 42 | u64 old_mask; |
415 | 43 | ||
416 | old_mask = in_be64(&spu_get_pdata(spu)->priv1->int_mask_RW[class]); | 44 | old_mask = in_be64(&spu->priv1->int_mask_RW[class]); |
417 | out_be64(&spu_get_pdata(spu)->priv1->int_mask_RW[class], | 45 | out_be64(&spu->priv1->int_mask_RW[class], old_mask & mask); |
418 | old_mask & mask); | ||
419 | } | 46 | } |
420 | 47 | ||
421 | static void int_mask_or(struct spu *spu, int class, u64 mask) | 48 | static void int_mask_or(struct spu *spu, int class, u64 mask) |
422 | { | 49 | { |
423 | u64 old_mask; | 50 | u64 old_mask; |
424 | 51 | ||
425 | old_mask = in_be64(&spu_get_pdata(spu)->priv1->int_mask_RW[class]); | 52 | old_mask = in_be64(&spu->priv1->int_mask_RW[class]); |
426 | out_be64(&spu_get_pdata(spu)->priv1->int_mask_RW[class], | 53 | out_be64(&spu->priv1->int_mask_RW[class], old_mask | mask); |
427 | old_mask | mask); | ||
428 | } | 54 | } |
429 | 55 | ||
430 | static void int_mask_set(struct spu *spu, int class, u64 mask) | 56 | static void int_mask_set(struct spu *spu, int class, u64 mask) |
431 | { | 57 | { |
432 | out_be64(&spu_get_pdata(spu)->priv1->int_mask_RW[class], mask); | 58 | out_be64(&spu->priv1->int_mask_RW[class], mask); |
433 | } | 59 | } |
434 | 60 | ||
435 | static u64 int_mask_get(struct spu *spu, int class) | 61 | static u64 int_mask_get(struct spu *spu, int class) |
436 | { | 62 | { |
437 | return in_be64(&spu_get_pdata(spu)->priv1->int_mask_RW[class]); | 63 | return in_be64(&spu->priv1->int_mask_RW[class]); |
438 | } | 64 | } |
439 | 65 | ||
440 | static void int_stat_clear(struct spu *spu, int class, u64 stat) | 66 | static void int_stat_clear(struct spu *spu, int class, u64 stat) |
441 | { | 67 | { |
442 | out_be64(&spu_get_pdata(spu)->priv1->int_stat_RW[class], stat); | 68 | out_be64(&spu->priv1->int_stat_RW[class], stat); |
443 | } | 69 | } |
444 | 70 | ||
445 | static u64 int_stat_get(struct spu *spu, int class) | 71 | static u64 int_stat_get(struct spu *spu, int class) |
446 | { | 72 | { |
447 | return in_be64(&spu_get_pdata(spu)->priv1->int_stat_RW[class]); | 73 | return in_be64(&spu->priv1->int_stat_RW[class]); |
448 | } | 74 | } |
449 | 75 | ||
450 | static void cpu_affinity_set(struct spu *spu, int cpu) | 76 | static void cpu_affinity_set(struct spu *spu, int cpu) |
451 | { | 77 | { |
452 | u64 target = iic_get_target_id(cpu); | 78 | u64 target = iic_get_target_id(cpu); |
453 | u64 route = target << 48 | target << 32 | target << 16; | 79 | u64 route = target << 48 | target << 32 | target << 16; |
454 | out_be64(&spu_get_pdata(spu)->priv1->int_route_RW, route); | 80 | out_be64(&spu->priv1->int_route_RW, route); |
455 | } | 81 | } |
456 | 82 | ||
457 | static u64 mfc_dar_get(struct spu *spu) | 83 | static u64 mfc_dar_get(struct spu *spu) |
458 | { | 84 | { |
459 | return in_be64(&spu_get_pdata(spu)->priv1->mfc_dar_RW); | 85 | return in_be64(&spu->priv1->mfc_dar_RW); |
460 | } | 86 | } |
461 | 87 | ||
462 | static u64 mfc_dsisr_get(struct spu *spu) | 88 | static u64 mfc_dsisr_get(struct spu *spu) |
463 | { | 89 | { |
464 | return in_be64(&spu_get_pdata(spu)->priv1->mfc_dsisr_RW); | 90 | return in_be64(&spu->priv1->mfc_dsisr_RW); |
465 | } | 91 | } |
466 | 92 | ||
467 | static void mfc_dsisr_set(struct spu *spu, u64 dsisr) | 93 | static void mfc_dsisr_set(struct spu *spu, u64 dsisr) |
468 | { | 94 | { |
469 | out_be64(&spu_get_pdata(spu)->priv1->mfc_dsisr_RW, dsisr); | 95 | out_be64(&spu->priv1->mfc_dsisr_RW, dsisr); |
470 | } | 96 | } |
471 | 97 | ||
472 | static void mfc_sdr_setup(struct spu *spu) | 98 | static void mfc_sdr_setup(struct spu *spu) |
473 | { | 99 | { |
474 | out_be64(&spu_get_pdata(spu)->priv1->mfc_sdr_RW, mfspr(SPRN_SDR1)); | 100 | out_be64(&spu->priv1->mfc_sdr_RW, mfspr(SPRN_SDR1)); |
475 | } | 101 | } |
476 | 102 | ||
477 | static void mfc_sr1_set(struct spu *spu, u64 sr1) | 103 | static void mfc_sr1_set(struct spu *spu, u64 sr1) |
478 | { | 104 | { |
479 | out_be64(&spu_get_pdata(spu)->priv1->mfc_sr1_RW, sr1); | 105 | out_be64(&spu->priv1->mfc_sr1_RW, sr1); |
480 | } | 106 | } |
481 | 107 | ||
482 | static u64 mfc_sr1_get(struct spu *spu) | 108 | static u64 mfc_sr1_get(struct spu *spu) |
483 | { | 109 | { |
484 | return in_be64(&spu_get_pdata(spu)->priv1->mfc_sr1_RW); | 110 | return in_be64(&spu->priv1->mfc_sr1_RW); |
485 | } | 111 | } |
486 | 112 | ||
487 | static void mfc_tclass_id_set(struct spu *spu, u64 tclass_id) | 113 | static void mfc_tclass_id_set(struct spu *spu, u64 tclass_id) |
488 | { | 114 | { |
489 | out_be64(&spu_get_pdata(spu)->priv1->mfc_tclass_id_RW, tclass_id); | 115 | out_be64(&spu->priv1->mfc_tclass_id_RW, tclass_id); |
490 | } | 116 | } |
491 | 117 | ||
492 | static u64 mfc_tclass_id_get(struct spu *spu) | 118 | static u64 mfc_tclass_id_get(struct spu *spu) |
493 | { | 119 | { |
494 | return in_be64(&spu_get_pdata(spu)->priv1->mfc_tclass_id_RW); | 120 | return in_be64(&spu->priv1->mfc_tclass_id_RW); |
495 | } | 121 | } |
496 | 122 | ||
497 | static void tlb_invalidate(struct spu *spu) | 123 | static void tlb_invalidate(struct spu *spu) |
498 | { | 124 | { |
499 | out_be64(&spu_get_pdata(spu)->priv1->tlb_invalidate_entry_W, 0ul); | 125 | out_be64(&spu->priv1->tlb_invalidate_entry_W, 0ul); |
500 | } | 126 | } |
501 | 127 | ||
502 | static void resource_allocation_groupID_set(struct spu *spu, u64 id) | 128 | static void resource_allocation_groupID_set(struct spu *spu, u64 id) |
503 | { | 129 | { |
504 | out_be64(&spu_get_pdata(spu)->priv1->resource_allocation_groupID_RW, | 130 | out_be64(&spu->priv1->resource_allocation_groupID_RW, id); |
505 | id); | ||
506 | } | 131 | } |
507 | 132 | ||
508 | static u64 resource_allocation_groupID_get(struct spu *spu) | 133 | static u64 resource_allocation_groupID_get(struct spu *spu) |
509 | { | 134 | { |
510 | return in_be64( | 135 | return in_be64(&spu->priv1->resource_allocation_groupID_RW); |
511 | &spu_get_pdata(spu)->priv1->resource_allocation_groupID_RW); | ||
512 | } | 136 | } |
513 | 137 | ||
514 | static void resource_allocation_enable_set(struct spu *spu, u64 enable) | 138 | static void resource_allocation_enable_set(struct spu *spu, u64 enable) |
515 | { | 139 | { |
516 | out_be64(&spu_get_pdata(spu)->priv1->resource_allocation_enable_RW, | 140 | out_be64(&spu->priv1->resource_allocation_enable_RW, enable); |
517 | enable); | ||
518 | } | 141 | } |
519 | 142 | ||
520 | static u64 resource_allocation_enable_get(struct spu *spu) | 143 | static u64 resource_allocation_enable_get(struct spu *spu) |
521 | { | 144 | { |
522 | return in_be64( | 145 | return in_be64(&spu->priv1->resource_allocation_enable_RW); |
523 | &spu_get_pdata(spu)->priv1->resource_allocation_enable_RW); | ||
524 | } | 146 | } |
525 | 147 | ||
526 | const struct spu_priv1_ops spu_priv1_mmio_ops = | 148 | const struct spu_priv1_ops spu_priv1_mmio_ops = |
diff --git a/arch/powerpc/platforms/celleb/Makefile b/arch/powerpc/platforms/celleb/Makefile new file mode 100644 index 000000000000..3baf658ac543 --- /dev/null +++ b/arch/powerpc/platforms/celleb/Makefile | |||
@@ -0,0 +1,9 @@ | |||
1 | obj-y += interrupt.o iommu.o setup.o \ | ||
2 | htab.o beat.o pci.o \ | ||
3 | scc_epci.o hvCall.o | ||
4 | |||
5 | obj-$(CONFIG_SMP) += smp.o | ||
6 | obj-$(CONFIG_PPC_UDBG_BEAT) += udbg_beat.o | ||
7 | obj-$(CONFIG_USB) += scc_uhc.o | ||
8 | obj-$(CONFIG_HAS_TXX9_SERIAL) += scc_sio.o | ||
9 | obj-$(CONFIG_SPU_BASE) += spu_priv1.o | ||
diff --git a/arch/powerpc/platforms/celleb/beat.c b/arch/powerpc/platforms/celleb/beat.c new file mode 100644 index 000000000000..99341ce8a697 --- /dev/null +++ b/arch/powerpc/platforms/celleb/beat.c | |||
@@ -0,0 +1,163 @@ | |||
1 | /* | ||
2 | * Simple routines for Celleb/Beat | ||
3 | * | ||
4 | * (C) Copyright 2006-2007 TOSHIBA CORPORATION | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License along | ||
17 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | */ | ||
20 | |||
21 | #include <linux/module.h> | ||
22 | #include <linux/init.h> | ||
23 | #include <linux/err.h> | ||
24 | #include <linux/rtc.h> | ||
25 | |||
26 | #include <asm/hvconsole.h> | ||
27 | #include <asm/time.h> | ||
28 | |||
29 | #include "beat_wrapper.h" | ||
30 | #include "beat.h" | ||
31 | |||
32 | void beat_restart(char *cmd) | ||
33 | { | ||
34 | beat_shutdown_logical_partition(1); | ||
35 | } | ||
36 | |||
37 | void beat_power_off(void) | ||
38 | { | ||
39 | beat_shutdown_logical_partition(0); | ||
40 | } | ||
41 | |||
42 | u64 beat_halt_code = 0x1000000000000000UL; | ||
43 | |||
44 | void beat_halt(void) | ||
45 | { | ||
46 | beat_shutdown_logical_partition(beat_halt_code); | ||
47 | } | ||
48 | |||
49 | int beat_set_rtc_time(struct rtc_time *rtc_time) | ||
50 | { | ||
51 | u64 tim; | ||
52 | tim = mktime(rtc_time->tm_year+1900, | ||
53 | rtc_time->tm_mon+1, rtc_time->tm_mday, | ||
54 | rtc_time->tm_hour, rtc_time->tm_min, rtc_time->tm_sec); | ||
55 | if (beat_rtc_write(tim)) | ||
56 | return -1; | ||
57 | return 0; | ||
58 | } | ||
59 | |||
60 | void beat_get_rtc_time(struct rtc_time *rtc_time) | ||
61 | { | ||
62 | u64 tim; | ||
63 | |||
64 | if (beat_rtc_read(&tim)) | ||
65 | tim = 0; | ||
66 | to_tm(tim, rtc_time); | ||
67 | rtc_time->tm_year -= 1900; | ||
68 | rtc_time->tm_mon -= 1; | ||
69 | } | ||
70 | |||
71 | #define BEAT_NVRAM_SIZE 4096 | ||
72 | |||
73 | ssize_t beat_nvram_read(char *buf, size_t count, loff_t *index) | ||
74 | { | ||
75 | unsigned int i; | ||
76 | unsigned long len; | ||
77 | char *p = buf; | ||
78 | |||
79 | if (*index >= BEAT_NVRAM_SIZE) | ||
80 | return -ENODEV; | ||
81 | i = *index; | ||
82 | if (i + count > BEAT_NVRAM_SIZE) | ||
83 | count = BEAT_NVRAM_SIZE - i; | ||
84 | |||
85 | for (; count != 0; count -= len) { | ||
86 | len = count; | ||
87 | if (len > BEAT_NVRW_CNT) | ||
88 | len = BEAT_NVRW_CNT; | ||
89 | if (beat_eeprom_read(i, len, p)) { | ||
90 | return -EIO; | ||
91 | } | ||
92 | |||
93 | p += len; | ||
94 | i += len; | ||
95 | } | ||
96 | *index = i; | ||
97 | return p - buf; | ||
98 | } | ||
99 | |||
100 | ssize_t beat_nvram_write(char *buf, size_t count, loff_t *index) | ||
101 | { | ||
102 | unsigned int i; | ||
103 | unsigned long len; | ||
104 | char *p = buf; | ||
105 | |||
106 | if (*index >= BEAT_NVRAM_SIZE) | ||
107 | return -ENODEV; | ||
108 | i = *index; | ||
109 | if (i + count > BEAT_NVRAM_SIZE) | ||
110 | count = BEAT_NVRAM_SIZE - i; | ||
111 | |||
112 | for (; count != 0; count -= len) { | ||
113 | len = count; | ||
114 | if (len > BEAT_NVRW_CNT) | ||
115 | len = BEAT_NVRW_CNT; | ||
116 | if (beat_eeprom_write(i, len, p)) { | ||
117 | return -EIO; | ||
118 | } | ||
119 | |||
120 | p += len; | ||
121 | i += len; | ||
122 | } | ||
123 | *index = i; | ||
124 | return p - buf; | ||
125 | } | ||
126 | |||
127 | ssize_t beat_nvram_get_size(void) | ||
128 | { | ||
129 | return BEAT_NVRAM_SIZE; | ||
130 | } | ||
131 | |||
132 | int beat_set_xdabr(unsigned long dabr) | ||
133 | { | ||
134 | if (beat_set_dabr(dabr, DABRX_KERNEL | DABRX_USER)) | ||
135 | return -1; | ||
136 | return 0; | ||
137 | } | ||
138 | |||
139 | int64_t beat_get_term_char(u64 vterm, u64 *len, u64 *t1, u64 *t2) | ||
140 | { | ||
141 | u64 db[2]; | ||
142 | s64 ret; | ||
143 | |||
144 | ret = beat_get_characters_from_console(vterm, len, (u8*)db); | ||
145 | if (ret == 0) { | ||
146 | *t1 = db[0]; | ||
147 | *t2 = db[1]; | ||
148 | } | ||
149 | return ret; | ||
150 | } | ||
151 | |||
152 | int64_t beat_put_term_char(u64 vterm, u64 len, u64 t1, u64 t2) | ||
153 | { | ||
154 | u64 db[2]; | ||
155 | |||
156 | db[0] = t1; | ||
157 | db[1] = t2; | ||
158 | return beat_put_characters_to_console(vterm, len, (u8*)db); | ||
159 | } | ||
160 | |||
161 | EXPORT_SYMBOL(beat_get_term_char); | ||
162 | EXPORT_SYMBOL(beat_put_term_char); | ||
163 | EXPORT_SYMBOL(beat_halt_code); | ||
diff --git a/arch/powerpc/platforms/celleb/beat.h b/arch/powerpc/platforms/celleb/beat.h new file mode 100644 index 000000000000..2b16bf3bee89 --- /dev/null +++ b/arch/powerpc/platforms/celleb/beat.h | |||
@@ -0,0 +1,40 @@ | |||
1 | /* | ||
2 | * Guest OS Interfaces. | ||
3 | * | ||
4 | * (C) Copyright 2006 TOSHIBA CORPORATION | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License along | ||
17 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | */ | ||
20 | |||
21 | #ifndef _CELLEB_BEAT_H | ||
22 | #define _CELLEB_BEAT_H | ||
23 | |||
24 | #define DABRX_KERNEL (1UL<<1) | ||
25 | #define DABRX_USER (1UL<<0) | ||
26 | |||
27 | int64_t beat_get_term_char(uint64_t,uint64_t*,uint64_t*,uint64_t*); | ||
28 | int64_t beat_put_term_char(uint64_t,uint64_t,uint64_t,uint64_t); | ||
29 | int64_t beat_repository_encode(int, const char *, uint64_t[4]); | ||
30 | void beat_restart(char *); | ||
31 | void beat_power_off(void); | ||
32 | void beat_halt(void); | ||
33 | int beat_set_rtc_time(struct rtc_time *); | ||
34 | void beat_get_rtc_time(struct rtc_time *); | ||
35 | ssize_t beat_nvram_get_size(void); | ||
36 | ssize_t beat_nvram_read(char *, size_t, loff_t *); | ||
37 | ssize_t beat_nvram_write(char *, size_t, loff_t *); | ||
38 | int beat_set_xdabr(unsigned long); | ||
39 | |||
40 | #endif /* _CELLEB_BEAT_H */ | ||
diff --git a/arch/powerpc/platforms/celleb/beat_syscall.h b/arch/powerpc/platforms/celleb/beat_syscall.h new file mode 100644 index 000000000000..14e16974773f --- /dev/null +++ b/arch/powerpc/platforms/celleb/beat_syscall.h | |||
@@ -0,0 +1,160 @@ | |||
1 | /* | ||
2 | * Beat hypervisor call numbers | ||
3 | * | ||
4 | * (C) Copyright 2004-2007 TOSHIBA CORPORATION | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License along | ||
17 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | */ | ||
20 | |||
21 | #ifndef BEAT_BEAT_syscall_H | ||
22 | #define BEAT_BEAT_syscall_H | ||
23 | |||
24 | #ifdef __ASSEMBLY__ | ||
25 | #define __BEAT_ADD_VENDOR_ID(__x, __v) ((__v)<<60|(__x)) | ||
26 | #else | ||
27 | #define __BEAT_ADD_VENDOR_ID(__x, __v) ((u64)(__v)<<60|(__x)) | ||
28 | #endif | ||
29 | #define HV_allocate_memory __BEAT_ADD_VENDOR_ID(0, 0) | ||
30 | #define HV_construct_virtual_address_space __BEAT_ADD_VENDOR_ID(2, 0) | ||
31 | #define HV_destruct_virtual_address_space __BEAT_ADD_VENDOR_ID(10, 0) | ||
32 | #define HV_get_virtual_address_space_id_of_ppe __BEAT_ADD_VENDOR_ID(4, 0) | ||
33 | #define HV_query_logical_partition_address_region_info \ | ||
34 | __BEAT_ADD_VENDOR_ID(6, 0) | ||
35 | #define HV_release_memory __BEAT_ADD_VENDOR_ID(13, 0) | ||
36 | #define HV_select_virtual_address_space __BEAT_ADD_VENDOR_ID(7, 0) | ||
37 | #define HV_load_range_registers __BEAT_ADD_VENDOR_ID(68, 0) | ||
38 | #define HV_set_ppe_l2cache_rmt_entry __BEAT_ADD_VENDOR_ID(70, 0) | ||
39 | #define HV_set_ppe_tlb_rmt_entry __BEAT_ADD_VENDOR_ID(71, 0) | ||
40 | #define HV_set_spe_tlb_rmt_entry __BEAT_ADD_VENDOR_ID(72, 0) | ||
41 | #define HV_get_io_address_translation_fault_info __BEAT_ADD_VENDOR_ID(14, 0) | ||
42 | #define HV_get_iopte __BEAT_ADD_VENDOR_ID(16, 0) | ||
43 | #define HV_preload_iopt_cache __BEAT_ADD_VENDOR_ID(17, 0) | ||
44 | #define HV_put_iopte __BEAT_ADD_VENDOR_ID(15, 0) | ||
45 | #define HV_connect_event_ports __BEAT_ADD_VENDOR_ID(21, 0) | ||
46 | #define HV_construct_event_receive_port __BEAT_ADD_VENDOR_ID(18, 0) | ||
47 | #define HV_destruct_event_receive_port __BEAT_ADD_VENDOR_ID(19, 0) | ||
48 | #define HV_destruct_event_send_port __BEAT_ADD_VENDOR_ID(22, 0) | ||
49 | #define HV_get_state_of_event_send_port __BEAT_ADD_VENDOR_ID(25, 0) | ||
50 | #define HV_request_to_connect_event_ports __BEAT_ADD_VENDOR_ID(20, 0) | ||
51 | #define HV_send_event_externally __BEAT_ADD_VENDOR_ID(23, 0) | ||
52 | #define HV_send_event_locally __BEAT_ADD_VENDOR_ID(24, 0) | ||
53 | #define HV_construct_and_connect_irq_plug __BEAT_ADD_VENDOR_ID(28, 0) | ||
54 | #define HV_destruct_irq_plug __BEAT_ADD_VENDOR_ID(29, 0) | ||
55 | #define HV_detect_pending_interrupts __BEAT_ADD_VENDOR_ID(26, 0) | ||
56 | #define HV_end_of_interrupt __BEAT_ADD_VENDOR_ID(27, 0) | ||
57 | #define HV_assign_control_signal_notification_port __BEAT_ADD_VENDOR_ID(45, 0) | ||
58 | #define HV_end_of_control_signal_processing __BEAT_ADD_VENDOR_ID(48, 0) | ||
59 | #define HV_get_control_signal __BEAT_ADD_VENDOR_ID(46, 0) | ||
60 | #define HV_set_irq_mask_for_spe __BEAT_ADD_VENDOR_ID(61, 0) | ||
61 | #define HV_shutdown_logical_partition __BEAT_ADD_VENDOR_ID(44, 0) | ||
62 | #define HV_connect_message_ports __BEAT_ADD_VENDOR_ID(35, 0) | ||
63 | #define HV_destruct_message_port __BEAT_ADD_VENDOR_ID(36, 0) | ||
64 | #define HV_receive_message __BEAT_ADD_VENDOR_ID(37, 0) | ||
65 | #define HV_get_message_port_info __BEAT_ADD_VENDOR_ID(34, 0) | ||
66 | #define HV_request_to_connect_message_ports __BEAT_ADD_VENDOR_ID(33, 0) | ||
67 | #define HV_send_message __BEAT_ADD_VENDOR_ID(32, 0) | ||
68 | #define HV_get_logical_ppe_id __BEAT_ADD_VENDOR_ID(69, 0) | ||
69 | #define HV_pause __BEAT_ADD_VENDOR_ID(9, 0) | ||
70 | #define HV_destruct_shared_memory_handle __BEAT_ADD_VENDOR_ID(51, 0) | ||
71 | #define HV_get_shared_memory_info __BEAT_ADD_VENDOR_ID(52, 0) | ||
72 | #define HV_permit_sharing_memory __BEAT_ADD_VENDOR_ID(50, 0) | ||
73 | #define HV_request_to_attach_shared_memory __BEAT_ADD_VENDOR_ID(49, 0) | ||
74 | #define HV_enable_logical_spe_execution __BEAT_ADD_VENDOR_ID(55, 0) | ||
75 | #define HV_construct_logical_spe __BEAT_ADD_VENDOR_ID(53, 0) | ||
76 | #define HV_disable_logical_spe_execution __BEAT_ADD_VENDOR_ID(56, 0) | ||
77 | #define HV_destruct_logical_spe __BEAT_ADD_VENDOR_ID(54, 0) | ||
78 | #define HV_sense_spe_execution_status __BEAT_ADD_VENDOR_ID(58, 0) | ||
79 | #define HV_insert_htab_entry __BEAT_ADD_VENDOR_ID(101, 0) | ||
80 | #define HV_read_htab_entries __BEAT_ADD_VENDOR_ID(95, 0) | ||
81 | #define HV_write_htab_entry __BEAT_ADD_VENDOR_ID(94, 0) | ||
82 | #define HV_assign_io_address_translation_fault_port \ | ||
83 | __BEAT_ADD_VENDOR_ID(100, 0) | ||
84 | #define HV_set_interrupt_mask __BEAT_ADD_VENDOR_ID(73, 0) | ||
85 | #define HV_get_logical_partition_id __BEAT_ADD_VENDOR_ID(74, 0) | ||
86 | #define HV_create_repository_node2 __BEAT_ADD_VENDOR_ID(90, 0) | ||
87 | #define HV_create_repository_node __BEAT_ADD_VENDOR_ID(90, 0) /* alias */ | ||
88 | #define HV_get_repository_node_value2 __BEAT_ADD_VENDOR_ID(91, 0) | ||
89 | #define HV_get_repository_node_value __BEAT_ADD_VENDOR_ID(91, 0) /* alias */ | ||
90 | #define HV_modify_repository_node_value2 __BEAT_ADD_VENDOR_ID(92, 0) | ||
91 | #define HV_modify_repository_node_value __BEAT_ADD_VENDOR_ID(92, 0) /* alias */ | ||
92 | #define HV_remove_repository_node2 __BEAT_ADD_VENDOR_ID(93, 0) | ||
93 | #define HV_remove_repository_node __BEAT_ADD_VENDOR_ID(93, 0) /* alias */ | ||
94 | #define HV_cancel_shared_memory __BEAT_ADD_VENDOR_ID(104, 0) | ||
95 | #define HV_clear_interrupt_status_of_spe __BEAT_ADD_VENDOR_ID(206, 0) | ||
96 | #define HV_construct_spe_irq_outlet __BEAT_ADD_VENDOR_ID(80, 0) | ||
97 | #define HV_destruct_spe_irq_outlet __BEAT_ADD_VENDOR_ID(81, 0) | ||
98 | #define HV_disconnect_ipspc_service __BEAT_ADD_VENDOR_ID(88, 0) | ||
99 | #define HV_execute_ipspc_command __BEAT_ADD_VENDOR_ID(86, 0) | ||
100 | #define HV_get_interrupt_status_of_spe __BEAT_ADD_VENDOR_ID(205, 0) | ||
101 | #define HV_get_spe_privileged_state_1_registers __BEAT_ADD_VENDOR_ID(208, 0) | ||
102 | #define HV_permit_use_of_ipspc_service __BEAT_ADD_VENDOR_ID(85, 0) | ||
103 | #define HV_reinitialize_logical_spe __BEAT_ADD_VENDOR_ID(82, 0) | ||
104 | #define HV_request_ipspc_service __BEAT_ADD_VENDOR_ID(84, 0) | ||
105 | #define HV_stop_ipspc_command __BEAT_ADD_VENDOR_ID(87, 0) | ||
106 | #define HV_set_spe_privileged_state_1_registers __BEAT_ADD_VENDOR_ID(204, 0) | ||
107 | #define HV_get_status_of_ipspc_service __BEAT_ADD_VENDOR_ID(203, 0) | ||
108 | #define HV_put_characters_to_console __BEAT_ADD_VENDOR_ID(0x101, 1) | ||
109 | #define HV_get_characters_from_console __BEAT_ADD_VENDOR_ID(0x102, 1) | ||
110 | #define HV_get_base_clock __BEAT_ADD_VENDOR_ID(0x111, 1) | ||
111 | #define HV_set_base_clock __BEAT_ADD_VENDOR_ID(0x112, 1) | ||
112 | #define HV_get_frame_cycle __BEAT_ADD_VENDOR_ID(0x114, 1) | ||
113 | #define HV_disable_console __BEAT_ADD_VENDOR_ID(0x115, 1) | ||
114 | #define HV_disable_all_console __BEAT_ADD_VENDOR_ID(0x116, 1) | ||
115 | #define HV_oneshot_timer __BEAT_ADD_VENDOR_ID(0x117, 1) | ||
116 | #define HV_set_dabr __BEAT_ADD_VENDOR_ID(0x118, 1) | ||
117 | #define HV_get_dabr __BEAT_ADD_VENDOR_ID(0x119, 1) | ||
118 | #define HV_start_hv_stats __BEAT_ADD_VENDOR_ID(0x21c, 1) | ||
119 | #define HV_stop_hv_stats __BEAT_ADD_VENDOR_ID(0x21d, 1) | ||
120 | #define HV_get_hv_stats __BEAT_ADD_VENDOR_ID(0x21e, 1) | ||
121 | #define HV_get_hv_error_stats __BEAT_ADD_VENDOR_ID(0x221, 1) | ||
122 | #define HV_get_stats __BEAT_ADD_VENDOR_ID(0x224, 1) | ||
123 | #define HV_get_heap_stats __BEAT_ADD_VENDOR_ID(0x225, 1) | ||
124 | #define HV_get_memory_stats __BEAT_ADD_VENDOR_ID(0x227, 1) | ||
125 | #define HV_get_memory_detail __BEAT_ADD_VENDOR_ID(0x228, 1) | ||
126 | #define HV_set_priority_of_irq_outlet __BEAT_ADD_VENDOR_ID(0x122, 1) | ||
127 | #define HV_get_physical_spe_by_reservation_id __BEAT_ADD_VENDOR_ID(0x128, 1) | ||
128 | #define HV_get_spe_context __BEAT_ADD_VENDOR_ID(0x129, 1) | ||
129 | #define HV_set_spe_context __BEAT_ADD_VENDOR_ID(0x12a, 1) | ||
130 | #define HV_downcount_of_interrupt __BEAT_ADD_VENDOR_ID(0x12e, 1) | ||
131 | #define HV_peek_spe_context __BEAT_ADD_VENDOR_ID(0x12f, 1) | ||
132 | #define HV_read_bpa_register __BEAT_ADD_VENDOR_ID(0x131, 1) | ||
133 | #define HV_write_bpa_register __BEAT_ADD_VENDOR_ID(0x132, 1) | ||
134 | #define HV_map_context_table_of_spe __BEAT_ADD_VENDOR_ID(0x137, 1) | ||
135 | #define HV_get_slb_for_logical_spe __BEAT_ADD_VENDOR_ID(0x138, 1) | ||
136 | #define HV_set_slb_for_logical_spe __BEAT_ADD_VENDOR_ID(0x139, 1) | ||
137 | #define HV_init_pm __BEAT_ADD_VENDOR_ID(0x150, 1) | ||
138 | #define HV_set_pm_signal __BEAT_ADD_VENDOR_ID(0x151, 1) | ||
139 | #define HV_get_pm_signal __BEAT_ADD_VENDOR_ID(0x152, 1) | ||
140 | #define HV_set_pm_config __BEAT_ADD_VENDOR_ID(0x153, 1) | ||
141 | #define HV_get_pm_config __BEAT_ADD_VENDOR_ID(0x154, 1) | ||
142 | #define HV_get_inner_trace_data __BEAT_ADD_VENDOR_ID(0x155, 1) | ||
143 | #define HV_set_ext_trace_buffer __BEAT_ADD_VENDOR_ID(0x156, 1) | ||
144 | #define HV_get_ext_trace_buffer __BEAT_ADD_VENDOR_ID(0x157, 1) | ||
145 | #define HV_set_pm_interrupt __BEAT_ADD_VENDOR_ID(0x158, 1) | ||
146 | #define HV_get_pm_interrupt __BEAT_ADD_VENDOR_ID(0x159, 1) | ||
147 | #define HV_kick_pm __BEAT_ADD_VENDOR_ID(0x160, 1) | ||
148 | #define HV_construct_pm_context __BEAT_ADD_VENDOR_ID(0x164, 1) | ||
149 | #define HV_destruct_pm_context __BEAT_ADD_VENDOR_ID(0x165, 1) | ||
150 | #define HV_be_slow __BEAT_ADD_VENDOR_ID(0x170, 1) | ||
151 | #define HV_assign_ipspc_server_connection_status_notification_port \ | ||
152 | __BEAT_ADD_VENDOR_ID(0x173, 1) | ||
153 | #define HV_get_raid_of_physical_spe __BEAT_ADD_VENDOR_ID(0x174, 1) | ||
154 | #define HV_set_physical_spe_to_rag __BEAT_ADD_VENDOR_ID(0x175, 1) | ||
155 | #define HV_release_physical_spe_from_rag __BEAT_ADD_VENDOR_ID(0x176, 1) | ||
156 | #define HV_rtc_read __BEAT_ADD_VENDOR_ID(0x190, 1) | ||
157 | #define HV_rtc_write __BEAT_ADD_VENDOR_ID(0x191, 1) | ||
158 | #define HV_eeprom_read __BEAT_ADD_VENDOR_ID(0x192, 1) | ||
159 | #define HV_eeprom_write __BEAT_ADD_VENDOR_ID(0x193, 1) | ||
160 | #endif | ||
diff --git a/arch/powerpc/platforms/celleb/beat_wrapper.h b/arch/powerpc/platforms/celleb/beat_wrapper.h new file mode 100644 index 000000000000..76ea0a6a9011 --- /dev/null +++ b/arch/powerpc/platforms/celleb/beat_wrapper.h | |||
@@ -0,0 +1,220 @@ | |||
1 | /* | ||
2 | * Beat hypervisor call I/F | ||
3 | * | ||
4 | * (C) Copyright 2007 TOSHIBA CORPORATION | ||
5 | * | ||
6 | * This code is based on arch/powerpc/platforms/pseries/plpar_wrapper.h. | ||
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 of the License, or | ||
11 | * (at your option) 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 along | ||
19 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
20 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
21 | */ | ||
22 | #ifndef BEAT_HCALL | ||
23 | #include "beat_syscall.h" | ||
24 | |||
25 | /* defined in hvCall.S */ | ||
26 | extern s64 beat_hcall_norets(u64 opcode, ...); | ||
27 | extern s64 beat_hcall_norets8(u64 opcode, u64 arg1, u64 arg2, u64 arg3, | ||
28 | u64 arg4, u64 arg5, u64 arg6, u64 arg7, u64 arg8); | ||
29 | extern s64 beat_hcall1(u64 opcode, u64 retbuf[1], ...); | ||
30 | extern s64 beat_hcall2(u64 opcode, u64 retbuf[2], ...); | ||
31 | extern s64 beat_hcall3(u64 opcode, u64 retbuf[3], ...); | ||
32 | extern s64 beat_hcall4(u64 opcode, u64 retbuf[4], ...); | ||
33 | extern s64 beat_hcall5(u64 opcode, u64 retbuf[5], ...); | ||
34 | extern s64 beat_hcall6(u64 opcode, u64 retbuf[6], ...); | ||
35 | |||
36 | static inline s64 beat_downcount_of_interrupt(u64 plug_id) | ||
37 | { | ||
38 | return beat_hcall_norets(HV_downcount_of_interrupt, plug_id); | ||
39 | } | ||
40 | |||
41 | static inline s64 beat_set_interrupt_mask(u64 index, | ||
42 | u64 val0, u64 val1, u64 val2, u64 val3) | ||
43 | { | ||
44 | return beat_hcall_norets(HV_set_interrupt_mask, index, | ||
45 | val0, val1, val2, val3); | ||
46 | } | ||
47 | |||
48 | static inline s64 beat_destruct_irq_plug(u64 plug_id) | ||
49 | { | ||
50 | return beat_hcall_norets(HV_destruct_irq_plug, plug_id); | ||
51 | } | ||
52 | |||
53 | static inline s64 beat_construct_and_connect_irq_plug(u64 plug_id, | ||
54 | u64 outlet_id) | ||
55 | { | ||
56 | return beat_hcall_norets(HV_construct_and_connect_irq_plug, plug_id, | ||
57 | outlet_id); | ||
58 | } | ||
59 | |||
60 | static inline s64 beat_detect_pending_interrupts(u64 index, u64 *retbuf) | ||
61 | { | ||
62 | return beat_hcall4(HV_detect_pending_interrupts, retbuf, index); | ||
63 | } | ||
64 | |||
65 | static inline s64 beat_pause(u64 style) | ||
66 | { | ||
67 | return beat_hcall_norets(HV_pause, style); | ||
68 | } | ||
69 | |||
70 | static inline s64 beat_read_htab_entries(u64 htab_id, u64 index, u64 *retbuf) | ||
71 | { | ||
72 | return beat_hcall5(HV_read_htab_entries, retbuf, htab_id, index); | ||
73 | } | ||
74 | |||
75 | static inline s64 beat_insert_htab_entry(u64 htab_id, u64 group, | ||
76 | u64 bitmask, u64 hpte_v, u64 hpte_r, u64 *slot) | ||
77 | { | ||
78 | u64 dummy[3]; | ||
79 | s64 ret; | ||
80 | |||
81 | ret = beat_hcall3(HV_insert_htab_entry, dummy, htab_id, group, | ||
82 | bitmask, hpte_v, hpte_r); | ||
83 | *slot = dummy[0]; | ||
84 | return ret; | ||
85 | } | ||
86 | |||
87 | static inline s64 beat_write_htab_entry(u64 htab_id, u64 slot, | ||
88 | u64 hpte_v, u64 hpte_r, u64 mask_v, u64 mask_r, | ||
89 | u64 *ret_v, u64 *ret_r) | ||
90 | { | ||
91 | u64 dummy[2]; | ||
92 | s64 ret; | ||
93 | |||
94 | ret = beat_hcall2(HV_write_htab_entry, dummy, htab_id, slot, | ||
95 | hpte_v, hpte_r, mask_v, mask_r); | ||
96 | *ret_v = dummy[0]; | ||
97 | *ret_r = dummy[1]; | ||
98 | return ret; | ||
99 | } | ||
100 | |||
101 | static inline void beat_shutdown_logical_partition(u64 code) | ||
102 | { | ||
103 | (void)beat_hcall_norets(HV_shutdown_logical_partition, code); | ||
104 | } | ||
105 | |||
106 | static inline s64 beat_rtc_write(u64 time_from_epoch) | ||
107 | { | ||
108 | return beat_hcall_norets(HV_rtc_write, time_from_epoch); | ||
109 | } | ||
110 | |||
111 | static inline s64 beat_rtc_read(u64 *time_from_epoch) | ||
112 | { | ||
113 | u64 dummy[1]; | ||
114 | s64 ret; | ||
115 | |||
116 | ret = beat_hcall1(HV_rtc_read, dummy); | ||
117 | *time_from_epoch = dummy[0]; | ||
118 | return ret; | ||
119 | } | ||
120 | |||
121 | #define BEAT_NVRW_CNT (sizeof(u64) * 6) | ||
122 | |||
123 | static inline s64 beat_eeprom_write(u64 index, u64 length, u8 *buffer) | ||
124 | { | ||
125 | u64 b[6]; | ||
126 | |||
127 | if (length > BEAT_NVRW_CNT) | ||
128 | return -1; | ||
129 | memcpy(b, buffer, sizeof(b)); | ||
130 | return beat_hcall_norets8(HV_eeprom_write, index, length, | ||
131 | b[0], b[1], b[2], b[3], b[4], b[5]); | ||
132 | } | ||
133 | |||
134 | static inline s64 beat_eeprom_read(u64 index, u64 length, u8 *buffer) | ||
135 | { | ||
136 | u64 b[6]; | ||
137 | s64 ret; | ||
138 | |||
139 | if (length > BEAT_NVRW_CNT) | ||
140 | return -1; | ||
141 | ret = beat_hcall6(HV_eeprom_read, b, index, length); | ||
142 | memcpy(buffer, b, length); | ||
143 | return ret; | ||
144 | } | ||
145 | |||
146 | static inline s64 beat_set_dabr(u64 value, u64 style) | ||
147 | { | ||
148 | return beat_hcall_norets(HV_set_dabr, value, style); | ||
149 | } | ||
150 | |||
151 | static inline s64 beat_get_characters_from_console(u64 termno, u64 *len, | ||
152 | u8 *buffer) | ||
153 | { | ||
154 | u64 dummy[3]; | ||
155 | s64 ret; | ||
156 | |||
157 | ret = beat_hcall3(HV_get_characters_from_console, dummy, termno, len); | ||
158 | *len = dummy[0]; | ||
159 | memcpy(buffer, dummy + 1, *len); | ||
160 | return ret; | ||
161 | } | ||
162 | |||
163 | static inline s64 beat_put_characters_to_console(u64 termno, u64 len, | ||
164 | u8 *buffer) | ||
165 | { | ||
166 | u64 b[2]; | ||
167 | |||
168 | memcpy(b, buffer, len); | ||
169 | return beat_hcall_norets(HV_put_characters_to_console, termno, len, b[0], b[1]); | ||
170 | } | ||
171 | |||
172 | static inline s64 beat_get_spe_privileged_state_1_registers( | ||
173 | u64 id, u64 offsetof, u64 *value) | ||
174 | { | ||
175 | u64 dummy[1]; | ||
176 | s64 ret; | ||
177 | |||
178 | ret = beat_hcall1(HV_get_spe_privileged_state_1_registers, dummy, id, | ||
179 | offsetof); | ||
180 | *value = dummy[0]; | ||
181 | return ret; | ||
182 | } | ||
183 | |||
184 | static inline s64 beat_set_irq_mask_for_spe(u64 id, u64 class, u64 mask) | ||
185 | { | ||
186 | return beat_hcall_norets(HV_set_irq_mask_for_spe, id, class, mask); | ||
187 | } | ||
188 | |||
189 | static inline s64 beat_clear_interrupt_status_of_spe(u64 id, u64 class, | ||
190 | u64 mask) | ||
191 | { | ||
192 | return beat_hcall_norets(HV_clear_interrupt_status_of_spe, | ||
193 | id, class, mask); | ||
194 | } | ||
195 | |||
196 | static inline s64 beat_set_spe_privileged_state_1_registers( | ||
197 | u64 id, u64 offsetof, u64 value) | ||
198 | { | ||
199 | return beat_hcall_norets(HV_set_spe_privileged_state_1_registers, | ||
200 | id, offsetof, value); | ||
201 | } | ||
202 | |||
203 | static inline s64 beat_get_interrupt_status_of_spe(u64 id, u64 class, u64 *val) | ||
204 | { | ||
205 | u64 dummy[1]; | ||
206 | s64 ret; | ||
207 | |||
208 | ret = beat_hcall1(HV_get_interrupt_status_of_spe, dummy, id, class); | ||
209 | *val = dummy[0]; | ||
210 | return ret; | ||
211 | } | ||
212 | |||
213 | static inline s64 beat_put_iopte(u64 ioas_id, u64 io_addr, u64 real_addr, | ||
214 | u64 ioid, u64 flags) | ||
215 | { | ||
216 | return beat_hcall_norets(HV_put_iopte, ioas_id, io_addr, real_addr, | ||
217 | ioid, flags); | ||
218 | } | ||
219 | |||
220 | #endif | ||
diff --git a/arch/powerpc/platforms/celleb/htab.c b/arch/powerpc/platforms/celleb/htab.c new file mode 100644 index 000000000000..ffa7c2c2030d --- /dev/null +++ b/arch/powerpc/platforms/celleb/htab.c | |||
@@ -0,0 +1,311 @@ | |||
1 | /* | ||
2 | * "Cell Reference Set" HTAB support. | ||
3 | * | ||
4 | * (C) Copyright 2006-2007 TOSHIBA CORPORATION | ||
5 | * | ||
6 | * This code is based on arch/powerpc/platforms/pseries/lpar.c: | ||
7 | * Copyright (C) 2001 Todd Inglett, IBM Corporation | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License as published by | ||
11 | * the Free Software Foundation; either version 2 of the License, or | ||
12 | * (at your option) any later version. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License along | ||
20 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
21 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
22 | */ | ||
23 | |||
24 | #undef DEBUG_LOW | ||
25 | |||
26 | #include <linux/kernel.h> | ||
27 | #include <linux/spinlock.h> | ||
28 | |||
29 | #include <asm/mmu.h> | ||
30 | #include <asm/page.h> | ||
31 | #include <asm/pgtable.h> | ||
32 | #include <asm/machdep.h> | ||
33 | #include <asm/udbg.h> | ||
34 | |||
35 | #include "beat_wrapper.h" | ||
36 | |||
37 | #ifdef DEBUG_LOW | ||
38 | #define DBG_LOW(fmt...) do { udbg_printf(fmt); } while(0) | ||
39 | #else | ||
40 | #define DBG_LOW(fmt...) do { } while(0) | ||
41 | #endif | ||
42 | |||
43 | static DEFINE_SPINLOCK(beat_htab_lock); | ||
44 | |||
45 | static inline unsigned int beat_read_mask(unsigned hpte_group) | ||
46 | { | ||
47 | unsigned long hpte_v[5]; | ||
48 | unsigned long rmask = 0; | ||
49 | |||
50 | beat_read_htab_entries(0, hpte_group + 0, hpte_v); | ||
51 | if (!(hpte_v[0] & HPTE_V_BOLTED)) | ||
52 | rmask |= 0x8000; | ||
53 | if (!(hpte_v[1] & HPTE_V_BOLTED)) | ||
54 | rmask |= 0x4000; | ||
55 | if (!(hpte_v[2] & HPTE_V_BOLTED)) | ||
56 | rmask |= 0x2000; | ||
57 | if (!(hpte_v[3] & HPTE_V_BOLTED)) | ||
58 | rmask |= 0x1000; | ||
59 | beat_read_htab_entries(0, hpte_group + 4, hpte_v); | ||
60 | if (!(hpte_v[0] & HPTE_V_BOLTED)) | ||
61 | rmask |= 0x0800; | ||
62 | if (!(hpte_v[1] & HPTE_V_BOLTED)) | ||
63 | rmask |= 0x0400; | ||
64 | if (!(hpte_v[2] & HPTE_V_BOLTED)) | ||
65 | rmask |= 0x0200; | ||
66 | if (!(hpte_v[3] & HPTE_V_BOLTED)) | ||
67 | rmask |= 0x0100; | ||
68 | hpte_group = ~hpte_group & (htab_hash_mask * HPTES_PER_GROUP); | ||
69 | beat_read_htab_entries(0, hpte_group + 0, hpte_v); | ||
70 | if (!(hpte_v[0] & HPTE_V_BOLTED)) | ||
71 | rmask |= 0x80; | ||
72 | if (!(hpte_v[1] & HPTE_V_BOLTED)) | ||
73 | rmask |= 0x40; | ||
74 | if (!(hpte_v[2] & HPTE_V_BOLTED)) | ||
75 | rmask |= 0x20; | ||
76 | if (!(hpte_v[3] & HPTE_V_BOLTED)) | ||
77 | rmask |= 0x10; | ||
78 | beat_read_htab_entries(0, hpte_group + 4, hpte_v); | ||
79 | if (!(hpte_v[0] & HPTE_V_BOLTED)) | ||
80 | rmask |= 0x08; | ||
81 | if (!(hpte_v[1] & HPTE_V_BOLTED)) | ||
82 | rmask |= 0x04; | ||
83 | if (!(hpte_v[2] & HPTE_V_BOLTED)) | ||
84 | rmask |= 0x02; | ||
85 | if (!(hpte_v[3] & HPTE_V_BOLTED)) | ||
86 | rmask |= 0x01; | ||
87 | return rmask; | ||
88 | } | ||
89 | |||
90 | static long beat_lpar_hpte_insert(unsigned long hpte_group, | ||
91 | unsigned long va, unsigned long pa, | ||
92 | unsigned long rflags, unsigned long vflags, | ||
93 | int psize) | ||
94 | { | ||
95 | unsigned long lpar_rc; | ||
96 | unsigned long slot; | ||
97 | unsigned long hpte_v, hpte_r; | ||
98 | unsigned long flags; | ||
99 | |||
100 | /* same as iseries */ | ||
101 | if (vflags & HPTE_V_SECONDARY) | ||
102 | return -1; | ||
103 | |||
104 | if (!(vflags & HPTE_V_BOLTED)) | ||
105 | DBG_LOW("hpte_insert(group=%lx, va=%016lx, pa=%016lx, " | ||
106 | "rflags=%lx, vflags=%lx, psize=%d)\n", | ||
107 | hpte_group, va, pa, rflags, vflags, psize); | ||
108 | |||
109 | hpte_v = hpte_encode_v(va, psize) | vflags | HPTE_V_VALID; | ||
110 | hpte_r = hpte_encode_r(pa, psize) | rflags; | ||
111 | |||
112 | if (!(vflags & HPTE_V_BOLTED)) | ||
113 | DBG_LOW(" hpte_v=%016lx, hpte_r=%016lx\n", hpte_v, hpte_r); | ||
114 | |||
115 | if (rflags & (_PAGE_GUARDED|_PAGE_NO_CACHE)) | ||
116 | hpte_r &= ~_PAGE_COHERENT; | ||
117 | |||
118 | spin_lock_irqsave(&beat_htab_lock, flags); | ||
119 | if ((lpar_rc = beat_read_mask(hpte_group)) == 0) { | ||
120 | if (!(vflags & HPTE_V_BOLTED)) | ||
121 | DBG_LOW(" full\n"); | ||
122 | spin_unlock_irqrestore(&beat_htab_lock, flags); | ||
123 | return -1; | ||
124 | } | ||
125 | |||
126 | lpar_rc = beat_insert_htab_entry(0, hpte_group, lpar_rc << 48, | ||
127 | hpte_v, hpte_r, &slot); | ||
128 | spin_unlock_irqrestore(&beat_htab_lock, flags); | ||
129 | |||
130 | /* | ||
131 | * Since we try and ioremap PHBs we don't own, the pte insert | ||
132 | * will fail. However we must catch the failure in hash_page | ||
133 | * or we will loop forever, so return -2 in this case. | ||
134 | */ | ||
135 | if (unlikely(lpar_rc != 0)) { | ||
136 | if (!(vflags & HPTE_V_BOLTED)) | ||
137 | DBG_LOW(" lpar err %lx\n", lpar_rc); | ||
138 | return -2; | ||
139 | } | ||
140 | if (!(vflags & HPTE_V_BOLTED)) | ||
141 | DBG_LOW(" -> slot: %lx\n", slot); | ||
142 | |||
143 | /* We have to pass down the secondary bucket bit here as well */ | ||
144 | return (slot ^ hpte_group) & 15; | ||
145 | } | ||
146 | |||
147 | static long beat_lpar_hpte_remove(unsigned long hpte_group) | ||
148 | { | ||
149 | DBG_LOW("hpte_remove(group=%lx)\n", hpte_group); | ||
150 | return -1; | ||
151 | } | ||
152 | |||
153 | static unsigned long beat_lpar_hpte_getword0(unsigned long slot) | ||
154 | { | ||
155 | unsigned long dword0, dword[5]; | ||
156 | unsigned long lpar_rc; | ||
157 | |||
158 | lpar_rc = beat_read_htab_entries(0, slot & ~3UL, dword); | ||
159 | |||
160 | dword0 = dword[slot&3]; | ||
161 | |||
162 | BUG_ON(lpar_rc != 0); | ||
163 | |||
164 | return dword0; | ||
165 | } | ||
166 | |||
167 | static void beat_lpar_hptab_clear(void) | ||
168 | { | ||
169 | unsigned long size_bytes = 1UL << ppc64_pft_size; | ||
170 | unsigned long hpte_count = size_bytes >> 4; | ||
171 | int i; | ||
172 | unsigned long dummy0, dummy1; | ||
173 | |||
174 | /* TODO: Use bulk call */ | ||
175 | for (i = 0; i < hpte_count; i++) | ||
176 | beat_write_htab_entry(0, i, 0, 0, -1UL, -1UL, &dummy0, &dummy1); | ||
177 | } | ||
178 | |||
179 | /* | ||
180 | * NOTE: for updatepp ops we are fortunate that the linux "newpp" bits and | ||
181 | * the low 3 bits of flags happen to line up. So no transform is needed. | ||
182 | * We can probably optimize here and assume the high bits of newpp are | ||
183 | * already zero. For now I am paranoid. | ||
184 | */ | ||
185 | static long beat_lpar_hpte_updatepp(unsigned long slot, | ||
186 | unsigned long newpp, | ||
187 | unsigned long va, | ||
188 | int psize, int local) | ||
189 | { | ||
190 | unsigned long lpar_rc; | ||
191 | unsigned long dummy0, dummy1, want_v; | ||
192 | unsigned long flags; | ||
193 | |||
194 | want_v = hpte_encode_v(va, psize); | ||
195 | |||
196 | DBG_LOW(" update: " | ||
197 | "avpnv=%016lx, slot=%016lx, psize: %d, newpp %016lx ... ", | ||
198 | want_v & HPTE_V_AVPN, slot, psize, newpp); | ||
199 | |||
200 | spin_lock_irqsave(&beat_htab_lock, flags); | ||
201 | dummy0 = beat_lpar_hpte_getword0(slot); | ||
202 | if ((dummy0 & ~0x7FUL) != (want_v & ~0x7FUL)) { | ||
203 | DBG_LOW("not found !\n"); | ||
204 | spin_unlock_irqrestore(&beat_htab_lock, flags); | ||
205 | return -1; | ||
206 | } | ||
207 | |||
208 | lpar_rc = beat_write_htab_entry(0, slot, 0, newpp, 0, 7, &dummy0, | ||
209 | &dummy1); | ||
210 | spin_unlock_irqrestore(&beat_htab_lock, flags); | ||
211 | if (lpar_rc != 0 || dummy0 == 0) { | ||
212 | DBG_LOW("not found !\n"); | ||
213 | return -1; | ||
214 | } | ||
215 | |||
216 | DBG_LOW("ok %lx %lx\n", dummy0, dummy1); | ||
217 | |||
218 | BUG_ON(lpar_rc != 0); | ||
219 | |||
220 | return 0; | ||
221 | } | ||
222 | |||
223 | static long beat_lpar_hpte_find(unsigned long va, int psize) | ||
224 | { | ||
225 | unsigned long hash; | ||
226 | unsigned long i, j; | ||
227 | long slot; | ||
228 | unsigned long want_v, hpte_v; | ||
229 | |||
230 | hash = hpt_hash(va, mmu_psize_defs[psize].shift); | ||
231 | want_v = hpte_encode_v(va, psize); | ||
232 | |||
233 | for (j = 0; j < 2; j++) { | ||
234 | slot = (hash & htab_hash_mask) * HPTES_PER_GROUP; | ||
235 | for (i = 0; i < HPTES_PER_GROUP; i++) { | ||
236 | hpte_v = beat_lpar_hpte_getword0(slot); | ||
237 | |||
238 | if (HPTE_V_COMPARE(hpte_v, want_v) | ||
239 | && (hpte_v & HPTE_V_VALID) | ||
240 | && (!!(hpte_v & HPTE_V_SECONDARY) == j)) { | ||
241 | /* HPTE matches */ | ||
242 | if (j) | ||
243 | slot = -slot; | ||
244 | return slot; | ||
245 | } | ||
246 | ++slot; | ||
247 | } | ||
248 | hash = ~hash; | ||
249 | } | ||
250 | |||
251 | return -1; | ||
252 | } | ||
253 | |||
254 | static void beat_lpar_hpte_updateboltedpp(unsigned long newpp, | ||
255 | unsigned long ea, | ||
256 | int psize) | ||
257 | { | ||
258 | unsigned long lpar_rc, slot, vsid, va, dummy0, dummy1; | ||
259 | unsigned long flags; | ||
260 | |||
261 | vsid = get_kernel_vsid(ea); | ||
262 | va = (vsid << 28) | (ea & 0x0fffffff); | ||
263 | |||
264 | spin_lock_irqsave(&beat_htab_lock, flags); | ||
265 | slot = beat_lpar_hpte_find(va, psize); | ||
266 | BUG_ON(slot == -1); | ||
267 | |||
268 | lpar_rc = beat_write_htab_entry(0, slot, 0, newpp, 0, 7, | ||
269 | &dummy0, &dummy1); | ||
270 | spin_unlock_irqrestore(&beat_htab_lock, flags); | ||
271 | |||
272 | BUG_ON(lpar_rc != 0); | ||
273 | } | ||
274 | |||
275 | static void beat_lpar_hpte_invalidate(unsigned long slot, unsigned long va, | ||
276 | int psize, int local) | ||
277 | { | ||
278 | unsigned long want_v; | ||
279 | unsigned long lpar_rc; | ||
280 | unsigned long dummy1, dummy2; | ||
281 | unsigned long flags; | ||
282 | |||
283 | DBG_LOW(" inval : slot=%lx, va=%016lx, psize: %d, local: %d\n", | ||
284 | slot, va, psize, local); | ||
285 | want_v = hpte_encode_v(va, psize); | ||
286 | |||
287 | spin_lock_irqsave(&beat_htab_lock, flags); | ||
288 | dummy1 = beat_lpar_hpte_getword0(slot); | ||
289 | |||
290 | if ((dummy1 & ~0x7FUL) != (want_v & ~0x7FUL)) { | ||
291 | DBG_LOW("not found !\n"); | ||
292 | spin_unlock_irqrestore(&beat_htab_lock, flags); | ||
293 | return; | ||
294 | } | ||
295 | |||
296 | lpar_rc = beat_write_htab_entry(0, slot, 0, 0, HPTE_V_VALID, 0, | ||
297 | &dummy1, &dummy2); | ||
298 | spin_unlock_irqrestore(&beat_htab_lock, flags); | ||
299 | |||
300 | BUG_ON(lpar_rc != 0); | ||
301 | } | ||
302 | |||
303 | void __init hpte_init_beat(void) | ||
304 | { | ||
305 | ppc_md.hpte_invalidate = beat_lpar_hpte_invalidate; | ||
306 | ppc_md.hpte_updatepp = beat_lpar_hpte_updatepp; | ||
307 | ppc_md.hpte_updateboltedpp = beat_lpar_hpte_updateboltedpp; | ||
308 | ppc_md.hpte_insert = beat_lpar_hpte_insert; | ||
309 | ppc_md.hpte_remove = beat_lpar_hpte_remove; | ||
310 | ppc_md.hpte_clear_all = beat_lpar_hptab_clear; | ||
311 | } | ||
diff --git a/arch/powerpc/platforms/celleb/hvCall.S b/arch/powerpc/platforms/celleb/hvCall.S new file mode 100644 index 000000000000..74c817448948 --- /dev/null +++ b/arch/powerpc/platforms/celleb/hvCall.S | |||
@@ -0,0 +1,287 @@ | |||
1 | /* | ||
2 | * Beat hypervisor call I/F | ||
3 | * | ||
4 | * (C) Copyright 2007 TOSHIBA CORPORATION | ||
5 | * | ||
6 | * This code is based on arch/powerpc/platforms/pseries/hvCall.S. | ||
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 of the License, or | ||
11 | * (at your option) 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 along | ||
19 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
20 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
21 | */ | ||
22 | |||
23 | #include <asm/ppc_asm.h> | ||
24 | |||
25 | #define STK_PARM(i) (48 + ((i)-3)*8) | ||
26 | |||
27 | /* Not implemented on Beat, now */ | ||
28 | #define HCALL_INST_PRECALL | ||
29 | #define HCALL_INST_POSTCALL | ||
30 | |||
31 | .text | ||
32 | |||
33 | #define HVSC .long 0x44000022 | ||
34 | |||
35 | /* Note: takes only 7 input parameters at maximum */ | ||
36 | _GLOBAL(beat_hcall_norets) | ||
37 | HMT_MEDIUM | ||
38 | |||
39 | mfcr r0 | ||
40 | stw r0,8(r1) | ||
41 | |||
42 | HCALL_INST_PRECALL | ||
43 | |||
44 | mr r11,r3 | ||
45 | mr r3,r4 | ||
46 | mr r4,r5 | ||
47 | mr r5,r6 | ||
48 | mr r6,r7 | ||
49 | mr r7,r8 | ||
50 | mr r8,r9 | ||
51 | |||
52 | HVSC /* invoke the hypervisor */ | ||
53 | |||
54 | HCALL_INST_POSTCALL | ||
55 | |||
56 | lwz r0,8(r1) | ||
57 | mtcrf 0xff,r0 | ||
58 | |||
59 | blr /* return r3 = status */ | ||
60 | |||
61 | /* Note: takes 8 input parameters at maximum */ | ||
62 | _GLOBAL(beat_hcall_norets8) | ||
63 | HMT_MEDIUM | ||
64 | |||
65 | mfcr r0 | ||
66 | stw r0,8(r1) | ||
67 | |||
68 | HCALL_INST_PRECALL | ||
69 | |||
70 | mr r11,r3 | ||
71 | mr r3,r4 | ||
72 | mr r4,r5 | ||
73 | mr r5,r6 | ||
74 | mr r6,r7 | ||
75 | mr r7,r8 | ||
76 | mr r8,r9 | ||
77 | ld r10,STK_PARM(r10)(r1) | ||
78 | |||
79 | HVSC /* invoke the hypervisor */ | ||
80 | |||
81 | HCALL_INST_POSTCALL | ||
82 | |||
83 | lwz r0,8(r1) | ||
84 | mtcrf 0xff,r0 | ||
85 | |||
86 | blr /* return r3 = status */ | ||
87 | |||
88 | /* Note: takes only 6 input parameters, 1 output parameters at maximum */ | ||
89 | _GLOBAL(beat_hcall1) | ||
90 | HMT_MEDIUM | ||
91 | |||
92 | mfcr r0 | ||
93 | stw r0,8(r1) | ||
94 | |||
95 | HCALL_INST_PRECALL | ||
96 | |||
97 | std r4,STK_PARM(r4)(r1) /* save ret buffer */ | ||
98 | |||
99 | mr r11,r3 | ||
100 | mr r3,r5 | ||
101 | mr r4,r6 | ||
102 | mr r5,r7 | ||
103 | mr r6,r8 | ||
104 | mr r7,r9 | ||
105 | mr r8,r10 | ||
106 | |||
107 | HVSC /* invoke the hypervisor */ | ||
108 | |||
109 | HCALL_INST_POSTCALL | ||
110 | |||
111 | ld r12,STK_PARM(r4)(r1) | ||
112 | std r4, 0(r12) | ||
113 | |||
114 | lwz r0,8(r1) | ||
115 | mtcrf 0xff,r0 | ||
116 | |||
117 | blr /* return r3 = status */ | ||
118 | |||
119 | /* Note: takes only 6 input parameters, 2 output parameters at maximum */ | ||
120 | _GLOBAL(beat_hcall2) | ||
121 | HMT_MEDIUM | ||
122 | |||
123 | mfcr r0 | ||
124 | stw r0,8(r1) | ||
125 | |||
126 | HCALL_INST_PRECALL | ||
127 | |||
128 | std r4,STK_PARM(r4)(r1) /* save ret buffer */ | ||
129 | |||
130 | mr r11,r3 | ||
131 | mr r3,r5 | ||
132 | mr r4,r6 | ||
133 | mr r5,r7 | ||
134 | mr r6,r8 | ||
135 | mr r7,r9 | ||
136 | mr r8,r10 | ||
137 | |||
138 | HVSC /* invoke the hypervisor */ | ||
139 | |||
140 | HCALL_INST_POSTCALL | ||
141 | |||
142 | ld r12,STK_PARM(r4)(r1) | ||
143 | std r4, 0(r12) | ||
144 | std r5, 8(r12) | ||
145 | |||
146 | lwz r0,8(r1) | ||
147 | mtcrf 0xff,r0 | ||
148 | |||
149 | blr /* return r3 = status */ | ||
150 | |||
151 | /* Note: takes only 6 input parameters, 3 output parameters at maximum */ | ||
152 | _GLOBAL(beat_hcall3) | ||
153 | HMT_MEDIUM | ||
154 | |||
155 | mfcr r0 | ||
156 | stw r0,8(r1) | ||
157 | |||
158 | HCALL_INST_PRECALL | ||
159 | |||
160 | std r4,STK_PARM(r4)(r1) /* save ret buffer */ | ||
161 | |||
162 | mr r11,r3 | ||
163 | mr r3,r5 | ||
164 | mr r4,r6 | ||
165 | mr r5,r7 | ||
166 | mr r6,r8 | ||
167 | mr r7,r9 | ||
168 | mr r8,r10 | ||
169 | |||
170 | HVSC /* invoke the hypervisor */ | ||
171 | |||
172 | HCALL_INST_POSTCALL | ||
173 | |||
174 | ld r12,STK_PARM(r4)(r1) | ||
175 | std r4, 0(r12) | ||
176 | std r5, 8(r12) | ||
177 | std r6, 16(r12) | ||
178 | |||
179 | lwz r0,8(r1) | ||
180 | mtcrf 0xff,r0 | ||
181 | |||
182 | blr /* return r3 = status */ | ||
183 | |||
184 | /* Note: takes only 6 input parameters, 4 output parameters at maximum */ | ||
185 | _GLOBAL(beat_hcall4) | ||
186 | HMT_MEDIUM | ||
187 | |||
188 | mfcr r0 | ||
189 | stw r0,8(r1) | ||
190 | |||
191 | HCALL_INST_PRECALL | ||
192 | |||
193 | std r4,STK_PARM(r4)(r1) /* save ret buffer */ | ||
194 | |||
195 | mr r11,r3 | ||
196 | mr r3,r5 | ||
197 | mr r4,r6 | ||
198 | mr r5,r7 | ||
199 | mr r6,r8 | ||
200 | mr r7,r9 | ||
201 | mr r8,r10 | ||
202 | |||
203 | HVSC /* invoke the hypervisor */ | ||
204 | |||
205 | HCALL_INST_POSTCALL | ||
206 | |||
207 | ld r12,STK_PARM(r4)(r1) | ||
208 | std r4, 0(r12) | ||
209 | std r5, 8(r12) | ||
210 | std r6, 16(r12) | ||
211 | std r7, 24(r12) | ||
212 | |||
213 | lwz r0,8(r1) | ||
214 | mtcrf 0xff,r0 | ||
215 | |||
216 | blr /* return r3 = status */ | ||
217 | |||
218 | /* Note: takes only 6 input parameters, 5 output parameters at maximum */ | ||
219 | _GLOBAL(beat_hcall5) | ||
220 | HMT_MEDIUM | ||
221 | |||
222 | mfcr r0 | ||
223 | stw r0,8(r1) | ||
224 | |||
225 | HCALL_INST_PRECALL | ||
226 | |||
227 | std r4,STK_PARM(r4)(r1) /* save ret buffer */ | ||
228 | |||
229 | mr r11,r3 | ||
230 | mr r3,r5 | ||
231 | mr r4,r6 | ||
232 | mr r5,r7 | ||
233 | mr r6,r8 | ||
234 | mr r7,r9 | ||
235 | mr r8,r10 | ||
236 | |||
237 | HVSC /* invoke the hypervisor */ | ||
238 | |||
239 | HCALL_INST_POSTCALL | ||
240 | |||
241 | ld r12,STK_PARM(r4)(r1) | ||
242 | std r4, 0(r12) | ||
243 | std r5, 8(r12) | ||
244 | std r6, 16(r12) | ||
245 | std r7, 24(r12) | ||
246 | std r8, 32(r12) | ||
247 | |||
248 | lwz r0,8(r1) | ||
249 | mtcrf 0xff,r0 | ||
250 | |||
251 | blr /* return r3 = status */ | ||
252 | |||
253 | /* Note: takes only 6 input parameters, 6 output parameters at maximum */ | ||
254 | _GLOBAL(beat_hcall6) | ||
255 | HMT_MEDIUM | ||
256 | |||
257 | mfcr r0 | ||
258 | stw r0,8(r1) | ||
259 | |||
260 | HCALL_INST_PRECALL | ||
261 | |||
262 | std r4,STK_PARM(r4)(r1) /* save ret buffer */ | ||
263 | |||
264 | mr r11,r3 | ||
265 | mr r3,r5 | ||
266 | mr r4,r6 | ||
267 | mr r5,r7 | ||
268 | mr r6,r8 | ||
269 | mr r7,r9 | ||
270 | mr r8,r10 | ||
271 | |||
272 | HVSC /* invoke the hypervisor */ | ||
273 | |||
274 | HCALL_INST_POSTCALL | ||
275 | |||
276 | ld r12,STK_PARM(r4)(r1) | ||
277 | std r4, 0(r12) | ||
278 | std r5, 8(r12) | ||
279 | std r6, 16(r12) | ||
280 | std r7, 24(r12) | ||
281 | std r8, 32(r12) | ||
282 | std r9, 40(r12) | ||
283 | |||
284 | lwz r0,8(r1) | ||
285 | mtcrf 0xff,r0 | ||
286 | |||
287 | blr /* return r3 = status */ | ||
diff --git a/arch/powerpc/platforms/celleb/interrupt.c b/arch/powerpc/platforms/celleb/interrupt.c new file mode 100644 index 000000000000..98e6665681d3 --- /dev/null +++ b/arch/powerpc/platforms/celleb/interrupt.c | |||
@@ -0,0 +1,274 @@ | |||
1 | /* | ||
2 | * Celleb/Beat Interrupt controller | ||
3 | * | ||
4 | * (C) Copyright 2006-2007 TOSHIBA CORPORATION | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License along | ||
17 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | */ | ||
20 | |||
21 | #include <linux/init.h> | ||
22 | #include <linux/interrupt.h> | ||
23 | #include <linux/irq.h> | ||
24 | #include <linux/percpu.h> | ||
25 | #include <linux/types.h> | ||
26 | |||
27 | #include <asm/machdep.h> | ||
28 | |||
29 | #include "interrupt.h" | ||
30 | #include "beat_wrapper.h" | ||
31 | |||
32 | #define MAX_IRQS NR_IRQS | ||
33 | static DEFINE_SPINLOCK(beatic_irq_mask_lock); | ||
34 | static uint64_t beatic_irq_mask_enable[(MAX_IRQS+255)/64]; | ||
35 | static uint64_t beatic_irq_mask_ack[(MAX_IRQS+255)/64]; | ||
36 | |||
37 | static struct irq_host *beatic_host = NULL; | ||
38 | |||
39 | /* | ||
40 | * In this implementation, "virq" == "IRQ plug number", | ||
41 | * "(irq_hw_number_t)hwirq" == "IRQ outlet number". | ||
42 | */ | ||
43 | |||
44 | /* assumption: locked */ | ||
45 | static inline void beatic_update_irq_mask(unsigned int irq_plug) | ||
46 | { | ||
47 | int off; | ||
48 | unsigned long masks[4]; | ||
49 | |||
50 | off = (irq_plug / 256) * 4; | ||
51 | masks[0] = beatic_irq_mask_enable[off + 0] | ||
52 | & beatic_irq_mask_ack[off + 0]; | ||
53 | masks[1] = beatic_irq_mask_enable[off + 1] | ||
54 | & beatic_irq_mask_ack[off + 1]; | ||
55 | masks[2] = beatic_irq_mask_enable[off + 2] | ||
56 | & beatic_irq_mask_ack[off + 2]; | ||
57 | masks[3] = beatic_irq_mask_enable[off + 3] | ||
58 | & beatic_irq_mask_ack[off + 3]; | ||
59 | if (beat_set_interrupt_mask(irq_plug&~255UL, | ||
60 | masks[0], masks[1], masks[2], masks[3]) != 0) | ||
61 | panic("Failed to set mask IRQ!"); | ||
62 | } | ||
63 | |||
64 | static void beatic_mask_irq(unsigned int irq_plug) | ||
65 | { | ||
66 | unsigned long flags; | ||
67 | |||
68 | spin_lock_irqsave(&beatic_irq_mask_lock, flags); | ||
69 | beatic_irq_mask_enable[irq_plug/64] &= ~(1UL << (63 - (irq_plug%64))); | ||
70 | beatic_update_irq_mask(irq_plug); | ||
71 | spin_unlock_irqrestore(&beatic_irq_mask_lock, flags); | ||
72 | } | ||
73 | |||
74 | static void beatic_unmask_irq(unsigned int irq_plug) | ||
75 | { | ||
76 | unsigned long flags; | ||
77 | |||
78 | spin_lock_irqsave(&beatic_irq_mask_lock, flags); | ||
79 | beatic_irq_mask_enable[irq_plug/64] |= 1UL << (63 - (irq_plug%64)); | ||
80 | beatic_update_irq_mask(irq_plug); | ||
81 | spin_unlock_irqrestore(&beatic_irq_mask_lock, flags); | ||
82 | } | ||
83 | |||
84 | static void beatic_ack_irq(unsigned int irq_plug) | ||
85 | { | ||
86 | unsigned long flags; | ||
87 | |||
88 | spin_lock_irqsave(&beatic_irq_mask_lock, flags); | ||
89 | beatic_irq_mask_ack[irq_plug/64] &= ~(1UL << (63 - (irq_plug%64))); | ||
90 | beatic_update_irq_mask(irq_plug); | ||
91 | spin_unlock_irqrestore(&beatic_irq_mask_lock, flags); | ||
92 | } | ||
93 | |||
94 | static void beatic_end_irq(unsigned int irq_plug) | ||
95 | { | ||
96 | s64 err; | ||
97 | unsigned long flags; | ||
98 | |||
99 | if ((err = beat_downcount_of_interrupt(irq_plug)) != 0) { | ||
100 | if ((err & 0xFFFFFFFF) != 0xFFFFFFF5) /* -11: wrong state */ | ||
101 | panic("Failed to downcount IRQ! Error = %16lx", err); | ||
102 | |||
103 | printk(KERN_ERR "IRQ over-downcounted, plug %d\n", irq_plug); | ||
104 | } | ||
105 | spin_lock_irqsave(&beatic_irq_mask_lock, flags); | ||
106 | beatic_irq_mask_ack[irq_plug/64] |= 1UL << (63 - (irq_plug%64)); | ||
107 | beatic_update_irq_mask(irq_plug); | ||
108 | spin_unlock_irqrestore(&beatic_irq_mask_lock, flags); | ||
109 | } | ||
110 | |||
111 | static struct irq_chip beatic_pic = { | ||
112 | .typename = " CELL-BEAT ", | ||
113 | .unmask = beatic_unmask_irq, | ||
114 | .mask = beatic_mask_irq, | ||
115 | .eoi = beatic_end_irq, | ||
116 | }; | ||
117 | |||
118 | /* | ||
119 | * Dispose binding hardware IRQ number (hw) and Virtuql IRQ number (virq), | ||
120 | * update flags. | ||
121 | * | ||
122 | * Note that the number (virq) is already assigned at upper layer. | ||
123 | */ | ||
124 | static void beatic_pic_host_unmap(struct irq_host *h, unsigned int virq) | ||
125 | { | ||
126 | beat_destruct_irq_plug(virq); | ||
127 | } | ||
128 | |||
129 | /* | ||
130 | * Create or update binding hardware IRQ number (hw) and Virtuql | ||
131 | * IRQ number (virq). This is called only once for a given mapping. | ||
132 | * | ||
133 | * Note that the number (virq) is already assigned at upper layer. | ||
134 | */ | ||
135 | static int beatic_pic_host_map(struct irq_host *h, unsigned int virq, | ||
136 | irq_hw_number_t hw) | ||
137 | { | ||
138 | struct irq_desc *desc = get_irq_desc(virq); | ||
139 | int64_t err; | ||
140 | |||
141 | if ((err = beat_construct_and_connect_irq_plug(virq, hw)) < 0) | ||
142 | return -EIO; | ||
143 | |||
144 | desc->status |= IRQ_LEVEL; | ||
145 | set_irq_chip_and_handler(virq, &beatic_pic, handle_fasteoi_irq); | ||
146 | return 0; | ||
147 | } | ||
148 | |||
149 | /* | ||
150 | * Update binding hardware IRQ number (hw) and Virtuql | ||
151 | * IRQ number (virq). This is called only once for a given mapping. | ||
152 | */ | ||
153 | static void beatic_pic_host_remap(struct irq_host *h, unsigned int virq, | ||
154 | irq_hw_number_t hw) | ||
155 | { | ||
156 | beat_construct_and_connect_irq_plug(virq, hw); | ||
157 | } | ||
158 | |||
159 | /* | ||
160 | * Translate device-tree interrupt spec to irq_hw_number_t style (ulong), | ||
161 | * to pass away to irq_create_mapping(). | ||
162 | * | ||
163 | * Called from irq_create_of_mapping() only. | ||
164 | * Note: We have only 1 entry to translate. | ||
165 | */ | ||
166 | static int beatic_pic_host_xlate(struct irq_host *h, struct device_node *ct, | ||
167 | u32 *intspec, unsigned int intsize, | ||
168 | irq_hw_number_t *out_hwirq, | ||
169 | unsigned int *out_flags) | ||
170 | { | ||
171 | u64 *intspec2 = (u64 *)intspec; | ||
172 | |||
173 | *out_hwirq = *intspec2; | ||
174 | *out_flags |= IRQ_TYPE_LEVEL_LOW; | ||
175 | return 0; | ||
176 | } | ||
177 | |||
178 | static struct irq_host_ops beatic_pic_host_ops = { | ||
179 | .map = beatic_pic_host_map, | ||
180 | .remap = beatic_pic_host_remap, | ||
181 | .unmap = beatic_pic_host_unmap, | ||
182 | .xlate = beatic_pic_host_xlate, | ||
183 | }; | ||
184 | |||
185 | /* | ||
186 | * Get an IRQ number | ||
187 | * Note: returns VIRQ | ||
188 | */ | ||
189 | static inline unsigned int beatic_get_irq_plug(void) | ||
190 | { | ||
191 | int i; | ||
192 | uint64_t pending[4], ub; | ||
193 | |||
194 | for (i = 0; i < MAX_IRQS; i += 256) { | ||
195 | beat_detect_pending_interrupts(i, pending); | ||
196 | __asm__ ("cntlzd %0,%1":"=r"(ub): | ||
197 | "r"(pending[0] & beatic_irq_mask_enable[i/64+0] | ||
198 | & beatic_irq_mask_ack[i/64+0])); | ||
199 | if (ub != 64) | ||
200 | return i + ub + 0; | ||
201 | __asm__ ("cntlzd %0,%1":"=r"(ub): | ||
202 | "r"(pending[1] & beatic_irq_mask_enable[i/64+1] | ||
203 | & beatic_irq_mask_ack[i/64+1])); | ||
204 | if (ub != 64) | ||
205 | return i + ub + 64; | ||
206 | __asm__ ("cntlzd %0,%1":"=r"(ub): | ||
207 | "r"(pending[2] & beatic_irq_mask_enable[i/64+2] | ||
208 | & beatic_irq_mask_ack[i/64+2])); | ||
209 | if (ub != 64) | ||
210 | return i + ub + 128; | ||
211 | __asm__ ("cntlzd %0,%1":"=r"(ub): | ||
212 | "r"(pending[3] & beatic_irq_mask_enable[i/64+3] | ||
213 | & beatic_irq_mask_ack[i/64+3])); | ||
214 | if (ub != 64) | ||
215 | return i + ub + 192; | ||
216 | } | ||
217 | |||
218 | return NO_IRQ; | ||
219 | } | ||
220 | unsigned int beatic_get_irq(void) | ||
221 | { | ||
222 | unsigned int ret; | ||
223 | |||
224 | ret = beatic_get_irq_plug(); | ||
225 | if (ret != NO_IRQ) | ||
226 | beatic_ack_irq(ret); | ||
227 | return ret; | ||
228 | } | ||
229 | |||
230 | /* | ||
231 | */ | ||
232 | void __init beatic_init_IRQ(void) | ||
233 | { | ||
234 | int i; | ||
235 | |||
236 | memset(beatic_irq_mask_enable, 0, sizeof(beatic_irq_mask_enable)); | ||
237 | memset(beatic_irq_mask_ack, 255, sizeof(beatic_irq_mask_ack)); | ||
238 | for (i = 0; i < MAX_IRQS; i += 256) | ||
239 | beat_set_interrupt_mask(i, 0L, 0L, 0L, 0L); | ||
240 | |||
241 | /* Set out get_irq function */ | ||
242 | ppc_md.get_irq = beatic_get_irq; | ||
243 | |||
244 | /* Allocate an irq host */ | ||
245 | beatic_host = irq_alloc_host(IRQ_HOST_MAP_NOMAP, 0, | ||
246 | &beatic_pic_host_ops, | ||
247 | 0); | ||
248 | BUG_ON(beatic_host == NULL); | ||
249 | irq_set_default_host(beatic_host); | ||
250 | } | ||
251 | |||
252 | #ifdef CONFIG_SMP | ||
253 | |||
254 | /* Nullified to compile with SMP mode */ | ||
255 | void beatic_setup_cpu(int cpu) | ||
256 | { | ||
257 | } | ||
258 | |||
259 | void beatic_cause_IPI(int cpu, int mesg) | ||
260 | { | ||
261 | } | ||
262 | |||
263 | void beatic_request_IPIs(void) | ||
264 | { | ||
265 | } | ||
266 | #endif /* CONFIG_SMP */ | ||
267 | |||
268 | void beatic_deinit_IRQ(void) | ||
269 | { | ||
270 | int i; | ||
271 | |||
272 | for (i = 1; i < NR_IRQS; i++) | ||
273 | beat_destruct_irq_plug(i); | ||
274 | } | ||
diff --git a/arch/powerpc/platforms/celleb/interrupt.h b/arch/powerpc/platforms/celleb/interrupt.h new file mode 100644 index 000000000000..b470fd0051f1 --- /dev/null +++ b/arch/powerpc/platforms/celleb/interrupt.h | |||
@@ -0,0 +1,33 @@ | |||
1 | /* | ||
2 | * Celleb/Beat Interrupt controller | ||
3 | * | ||
4 | * (C) Copyright 2006 TOSHIBA CORPORATION | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License along | ||
17 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | */ | ||
20 | |||
21 | #ifndef ASM_BEAT_PIC_H | ||
22 | #define ASM_BEAT_PIC_H | ||
23 | #ifdef __KERNEL__ | ||
24 | |||
25 | extern void beatic_init_IRQ(void); | ||
26 | extern unsigned int beatic_get_irq(void); | ||
27 | extern void beatic_cause_IPI(int cpu, int mesg); | ||
28 | extern void beatic_request_IPIs(void); | ||
29 | extern void beatic_setup_cpu(int); | ||
30 | extern void beatic_deinit_IRQ(void); | ||
31 | |||
32 | #endif | ||
33 | #endif /* ASM_BEAT_PIC_H */ | ||
diff --git a/arch/powerpc/platforms/celleb/iommu.c b/arch/powerpc/platforms/celleb/iommu.c new file mode 100644 index 000000000000..f63b94c65353 --- /dev/null +++ b/arch/powerpc/platforms/celleb/iommu.c | |||
@@ -0,0 +1,104 @@ | |||
1 | /* | ||
2 | * Support for IOMMU on Celleb platform. | ||
3 | * | ||
4 | * (C) Copyright 2006-2007 TOSHIBA CORPORATION | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License along | ||
17 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | */ | ||
20 | |||
21 | #include <linux/kernel.h> | ||
22 | #include <linux/init.h> | ||
23 | #include <linux/dma-mapping.h> | ||
24 | #include <linux/pci.h> | ||
25 | |||
26 | #include <asm/of_platform.h> | ||
27 | |||
28 | #include "beat_wrapper.h" | ||
29 | |||
30 | #define DMA_FLAGS 0xf800000000000000UL /* r/w permitted, coherency required, | ||
31 | strongest order */ | ||
32 | |||
33 | static int __init find_dma_window(u64 *io_space_id, u64 *ioid, | ||
34 | u64 *base, u64 *size, u64 *io_page_size) | ||
35 | { | ||
36 | struct device_node *dn; | ||
37 | const unsigned long *dma_window; | ||
38 | |||
39 | for_each_node_by_type(dn, "ioif") { | ||
40 | dma_window = get_property(dn, "toshiba,dma-window", NULL); | ||
41 | if (dma_window) { | ||
42 | *io_space_id = (dma_window[0] >> 32) & 0xffffffffUL; | ||
43 | *ioid = dma_window[0] & 0x7ffUL; | ||
44 | *base = dma_window[1]; | ||
45 | *size = dma_window[2]; | ||
46 | *io_page_size = 1 << dma_window[3]; | ||
47 | of_node_put(dn); | ||
48 | return 1; | ||
49 | } | ||
50 | } | ||
51 | return 0; | ||
52 | } | ||
53 | |||
54 | static void __init celleb_init_direct_mapping(void) | ||
55 | { | ||
56 | u64 lpar_addr, io_addr; | ||
57 | u64 io_space_id, ioid, dma_base, dma_size, io_page_size; | ||
58 | |||
59 | if (!find_dma_window(&io_space_id, &ioid, &dma_base, &dma_size, | ||
60 | &io_page_size)) { | ||
61 | pr_info("No dma window found !\n"); | ||
62 | return; | ||
63 | } | ||
64 | |||
65 | for (lpar_addr = 0; lpar_addr < dma_size; lpar_addr += io_page_size) { | ||
66 | io_addr = lpar_addr + dma_base; | ||
67 | (void)beat_put_iopte(io_space_id, io_addr, lpar_addr, | ||
68 | ioid, DMA_FLAGS); | ||
69 | } | ||
70 | |||
71 | dma_direct_offset = dma_base; | ||
72 | } | ||
73 | |||
74 | static int celleb_of_bus_notify(struct notifier_block *nb, | ||
75 | unsigned long action, void *data) | ||
76 | { | ||
77 | struct device *dev = data; | ||
78 | |||
79 | /* We are only intereted in device addition */ | ||
80 | if (action != BUS_NOTIFY_ADD_DEVICE) | ||
81 | return 0; | ||
82 | |||
83 | dev->archdata.dma_ops = pci_dma_ops; | ||
84 | |||
85 | return 0; | ||
86 | } | ||
87 | |||
88 | static struct notifier_block celleb_of_bus_notifier = { | ||
89 | .notifier_call = celleb_of_bus_notify | ||
90 | }; | ||
91 | |||
92 | static int __init celleb_init_iommu(void) | ||
93 | { | ||
94 | if (!machine_is(celleb)) | ||
95 | return -ENODEV; | ||
96 | |||
97 | celleb_init_direct_mapping(); | ||
98 | pci_dma_ops = &dma_direct_ops; | ||
99 | bus_register_notifier(&of_platform_bus_type, &celleb_of_bus_notifier); | ||
100 | |||
101 | return 0; | ||
102 | } | ||
103 | |||
104 | arch_initcall(celleb_init_iommu); | ||
diff --git a/arch/powerpc/platforms/celleb/pci.c b/arch/powerpc/platforms/celleb/pci.c new file mode 100644 index 000000000000..867f83a7d0c9 --- /dev/null +++ b/arch/powerpc/platforms/celleb/pci.c | |||
@@ -0,0 +1,481 @@ | |||
1 | /* | ||
2 | * Support for PCI on Celleb platform. | ||
3 | * | ||
4 | * (C) Copyright 2006-2007 TOSHIBA CORPORATION | ||
5 | * | ||
6 | * This code is based on arch/powerpc/kernel/rtas_pci.c: | ||
7 | * Copyright (C) 2001 Dave Engebretsen, IBM Corporation | ||
8 | * Copyright (C) 2003 Anton Blanchard <anton@au.ibm.com>, IBM | ||
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 of the License, or | ||
13 | * (at your option) 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 along | ||
21 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
22 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
23 | */ | ||
24 | |||
25 | #undef DEBUG | ||
26 | |||
27 | #include <linux/kernel.h> | ||
28 | #include <linux/threads.h> | ||
29 | #include <linux/pci.h> | ||
30 | #include <linux/string.h> | ||
31 | #include <linux/init.h> | ||
32 | #include <linux/bootmem.h> | ||
33 | #include <linux/pci_regs.h> | ||
34 | |||
35 | #include <asm/io.h> | ||
36 | #include <asm/irq.h> | ||
37 | #include <asm/prom.h> | ||
38 | #include <asm/machdep.h> | ||
39 | #include <asm/pci-bridge.h> | ||
40 | #include <asm/ppc-pci.h> | ||
41 | |||
42 | #include "pci.h" | ||
43 | #include "interrupt.h" | ||
44 | |||
45 | #define MAX_PCI_DEVICES 32 | ||
46 | #define MAX_PCI_FUNCTIONS 8 | ||
47 | #define MAX_PCI_BASE_ADDRS 3 /* use 64 bit address */ | ||
48 | |||
49 | /* definition for fake pci configuration area for GbE, .... ,and etc. */ | ||
50 | |||
51 | struct celleb_pci_resource { | ||
52 | struct resource r[MAX_PCI_BASE_ADDRS]; | ||
53 | }; | ||
54 | |||
55 | struct celleb_pci_private { | ||
56 | unsigned char *fake_config[MAX_PCI_DEVICES][MAX_PCI_FUNCTIONS]; | ||
57 | struct celleb_pci_resource *res[MAX_PCI_DEVICES][MAX_PCI_FUNCTIONS]; | ||
58 | }; | ||
59 | |||
60 | static inline u8 celleb_fake_config_readb(void *addr) | ||
61 | { | ||
62 | u8 *p = addr; | ||
63 | return *p; | ||
64 | } | ||
65 | |||
66 | static inline u16 celleb_fake_config_readw(void *addr) | ||
67 | { | ||
68 | u16 *p = addr; | ||
69 | return le16_to_cpu(*p); | ||
70 | } | ||
71 | |||
72 | static inline u32 celleb_fake_config_readl(void *addr) | ||
73 | { | ||
74 | u32 *p = addr; | ||
75 | return le32_to_cpu(*p); | ||
76 | } | ||
77 | |||
78 | static inline void celleb_fake_config_writeb(u32 val, void *addr) | ||
79 | { | ||
80 | u8 *p = addr; | ||
81 | *p = val; | ||
82 | } | ||
83 | |||
84 | static inline void celleb_fake_config_writew(u32 val, void *addr) | ||
85 | { | ||
86 | u16 val16; | ||
87 | u16 *p = addr; | ||
88 | val16 = cpu_to_le16(val); | ||
89 | *p = val16; | ||
90 | } | ||
91 | |||
92 | static inline void celleb_fake_config_writel(u32 val, void *addr) | ||
93 | { | ||
94 | u32 val32; | ||
95 | u32 *p = addr; | ||
96 | val32 = cpu_to_le32(val); | ||
97 | *p = val32; | ||
98 | } | ||
99 | |||
100 | static unsigned char *get_fake_config_start(struct pci_controller *hose, | ||
101 | int devno, int fn) | ||
102 | { | ||
103 | struct celleb_pci_private *private = hose->private_data; | ||
104 | |||
105 | if (private == NULL) | ||
106 | return NULL; | ||
107 | |||
108 | return private->fake_config[devno][fn]; | ||
109 | } | ||
110 | |||
111 | static struct celleb_pci_resource *get_resource_start( | ||
112 | struct pci_controller *hose, | ||
113 | int devno, int fn) | ||
114 | { | ||
115 | struct celleb_pci_private *private = hose->private_data; | ||
116 | |||
117 | if (private == NULL) | ||
118 | return NULL; | ||
119 | |||
120 | return private->res[devno][fn]; | ||
121 | } | ||
122 | |||
123 | |||
124 | static void celleb_config_read_fake(unsigned char *config, int where, | ||
125 | int size, u32 *val) | ||
126 | { | ||
127 | char *p = config + where; | ||
128 | |||
129 | switch (size) { | ||
130 | case 1: | ||
131 | *val = celleb_fake_config_readb(p); | ||
132 | break; | ||
133 | case 2: | ||
134 | *val = celleb_fake_config_readw(p); | ||
135 | break; | ||
136 | case 4: | ||
137 | *val = celleb_fake_config_readl(p); | ||
138 | break; | ||
139 | } | ||
140 | |||
141 | return; | ||
142 | } | ||
143 | |||
144 | static void celleb_config_write_fake(unsigned char *config, int where, | ||
145 | int size, u32 val) | ||
146 | { | ||
147 | char *p = config + where; | ||
148 | |||
149 | switch (size) { | ||
150 | case 1: | ||
151 | celleb_fake_config_writeb(val, p); | ||
152 | break; | ||
153 | case 2: | ||
154 | celleb_fake_config_writew(val, p); | ||
155 | break; | ||
156 | case 4: | ||
157 | celleb_fake_config_writel(val, p); | ||
158 | break; | ||
159 | } | ||
160 | return; | ||
161 | } | ||
162 | |||
163 | static int celleb_fake_pci_read_config(struct pci_bus *bus, | ||
164 | unsigned int devfn, int where, int size, u32 *val) | ||
165 | { | ||
166 | char *config; | ||
167 | struct device_node *node; | ||
168 | struct pci_controller *hose; | ||
169 | unsigned int devno = devfn >> 3; | ||
170 | unsigned int fn = devfn & 0x7; | ||
171 | |||
172 | /* allignment check */ | ||
173 | BUG_ON(where % size); | ||
174 | |||
175 | pr_debug(" fake read: bus=0x%x, ", bus->number); | ||
176 | node = (struct device_node *)bus->sysdata; | ||
177 | hose = pci_find_hose_for_OF_device(node); | ||
178 | config = get_fake_config_start(hose, devno, fn); | ||
179 | |||
180 | pr_debug("devno=0x%x, where=0x%x, size=0x%x, ", devno, where, size); | ||
181 | if (!config) { | ||
182 | pr_debug("failed\n"); | ||
183 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
184 | } | ||
185 | |||
186 | celleb_config_read_fake(config, where, size, val); | ||
187 | pr_debug("val=0x%x\n", *val); | ||
188 | |||
189 | return PCIBIOS_SUCCESSFUL; | ||
190 | } | ||
191 | |||
192 | |||
193 | static int celleb_fake_pci_write_config(struct pci_bus *bus, | ||
194 | unsigned int devfn, int where, int size, u32 val) | ||
195 | { | ||
196 | char *config; | ||
197 | struct device_node *node; | ||
198 | struct pci_controller *hose; | ||
199 | struct celleb_pci_resource *res; | ||
200 | unsigned int devno = devfn >> 3; | ||
201 | unsigned int fn = devfn & 0x7; | ||
202 | |||
203 | /* allignment check */ | ||
204 | BUG_ON(where % size); | ||
205 | |||
206 | node = (struct device_node *)bus->sysdata; | ||
207 | hose = pci_find_hose_for_OF_device(node); | ||
208 | config = get_fake_config_start(hose, devno, fn); | ||
209 | |||
210 | if (!config) | ||
211 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
212 | |||
213 | if (val == ~0) { | ||
214 | int i = (where - PCI_BASE_ADDRESS_0) >> 3; | ||
215 | |||
216 | switch (where) { | ||
217 | case PCI_BASE_ADDRESS_0: | ||
218 | case PCI_BASE_ADDRESS_2: | ||
219 | if (size != 4) | ||
220 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
221 | res = get_resource_start(hose, devno, fn); | ||
222 | if (!res) | ||
223 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
224 | celleb_config_write_fake(config, where, size, | ||
225 | (res->r[i].end - res->r[i].start)); | ||
226 | return PCIBIOS_SUCCESSFUL; | ||
227 | case PCI_BASE_ADDRESS_1: | ||
228 | case PCI_BASE_ADDRESS_3: | ||
229 | case PCI_BASE_ADDRESS_4: | ||
230 | case PCI_BASE_ADDRESS_5: | ||
231 | break; | ||
232 | default: | ||
233 | break; | ||
234 | } | ||
235 | } | ||
236 | |||
237 | celleb_config_write_fake(config, where, size, val); | ||
238 | pr_debug(" fake write: where=%x, size=%d, val=%x\n", | ||
239 | where, size, val); | ||
240 | |||
241 | return PCIBIOS_SUCCESSFUL; | ||
242 | } | ||
243 | |||
244 | static struct pci_ops celleb_fake_pci_ops = { | ||
245 | celleb_fake_pci_read_config, | ||
246 | celleb_fake_pci_write_config | ||
247 | }; | ||
248 | |||
249 | static inline void celleb_setup_pci_base_addrs(struct pci_controller *hose, | ||
250 | unsigned int devno, unsigned int fn, | ||
251 | unsigned int num_base_addr) | ||
252 | { | ||
253 | u32 val; | ||
254 | unsigned char *config; | ||
255 | struct celleb_pci_resource *res; | ||
256 | |||
257 | config = get_fake_config_start(hose, devno, fn); | ||
258 | res = get_resource_start(hose, devno, fn); | ||
259 | |||
260 | if (!config || !res) | ||
261 | return; | ||
262 | |||
263 | switch (num_base_addr) { | ||
264 | case 3: | ||
265 | val = (res->r[2].start & 0xfffffff0) | ||
266 | | PCI_BASE_ADDRESS_MEM_TYPE_64; | ||
267 | celleb_config_write_fake(config, PCI_BASE_ADDRESS_4, 4, val); | ||
268 | val = res->r[2].start >> 32; | ||
269 | celleb_config_write_fake(config, PCI_BASE_ADDRESS_5, 4, val); | ||
270 | /* FALLTHROUGH */ | ||
271 | case 2: | ||
272 | val = (res->r[1].start & 0xfffffff0) | ||
273 | | PCI_BASE_ADDRESS_MEM_TYPE_64; | ||
274 | celleb_config_write_fake(config, PCI_BASE_ADDRESS_2, 4, val); | ||
275 | val = res->r[1].start >> 32; | ||
276 | celleb_config_write_fake(config, PCI_BASE_ADDRESS_3, 4, val); | ||
277 | /* FALLTHROUGH */ | ||
278 | case 1: | ||
279 | val = (res->r[0].start & 0xfffffff0) | ||
280 | | PCI_BASE_ADDRESS_MEM_TYPE_64; | ||
281 | celleb_config_write_fake(config, PCI_BASE_ADDRESS_0, 4, val); | ||
282 | val = res->r[0].start >> 32; | ||
283 | celleb_config_write_fake(config, PCI_BASE_ADDRESS_1, 4, val); | ||
284 | break; | ||
285 | } | ||
286 | |||
287 | val = PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; | ||
288 | celleb_config_write_fake(config, PCI_COMMAND, 2, val); | ||
289 | } | ||
290 | |||
291 | static int __devinit celleb_setup_fake_pci_device(struct device_node *node, | ||
292 | struct pci_controller *hose) | ||
293 | { | ||
294 | unsigned int rlen; | ||
295 | int num_base_addr = 0; | ||
296 | u32 val; | ||
297 | const u32 *wi0, *wi1, *wi2, *wi3, *wi4; | ||
298 | unsigned int devno, fn; | ||
299 | struct celleb_pci_private *private = hose->private_data; | ||
300 | unsigned char **config = NULL; | ||
301 | struct celleb_pci_resource **res = NULL; | ||
302 | const char *name; | ||
303 | const unsigned long *li; | ||
304 | int size, result; | ||
305 | |||
306 | if (private == NULL) { | ||
307 | printk(KERN_ERR "PCI: " | ||
308 | "memory space for pci controller is not assigned\n"); | ||
309 | goto error; | ||
310 | } | ||
311 | |||
312 | name = get_property(node, "model", &rlen); | ||
313 | if (!name) { | ||
314 | printk(KERN_ERR "PCI: model property not found.\n"); | ||
315 | goto error; | ||
316 | } | ||
317 | |||
318 | wi4 = get_property(node, "reg", &rlen); | ||
319 | if (wi4 == NULL) | ||
320 | goto error; | ||
321 | |||
322 | devno = ((wi4[0] >> 8) & 0xff) >> 3; | ||
323 | fn = (wi4[0] >> 8) & 0x7; | ||
324 | |||
325 | pr_debug("PCI: celleb_setup_fake_pci() %s devno=%x fn=%x\n", name, | ||
326 | devno, fn); | ||
327 | |||
328 | size = 256; | ||
329 | config = &private->fake_config[devno][fn]; | ||
330 | if (mem_init_done) | ||
331 | *config = kzalloc(size, GFP_KERNEL); | ||
332 | else | ||
333 | *config = alloc_bootmem(size); | ||
334 | if (*config == NULL) { | ||
335 | printk(KERN_ERR "PCI: " | ||
336 | "not enough memory for fake configuration space\n"); | ||
337 | goto error; | ||
338 | } | ||
339 | pr_debug("PCI: fake config area assigned 0x%016lx\n", | ||
340 | (unsigned long)*config); | ||
341 | |||
342 | size = sizeof(struct celleb_pci_resource); | ||
343 | res = &private->res[devno][fn]; | ||
344 | if (mem_init_done) | ||
345 | *res = kzalloc(size, GFP_KERNEL); | ||
346 | else | ||
347 | *res = alloc_bootmem(size); | ||
348 | if (*res == NULL) { | ||
349 | printk(KERN_ERR | ||
350 | "PCI: not enough memory for resource data space\n"); | ||
351 | goto error; | ||
352 | } | ||
353 | pr_debug("PCI: res assigned 0x%016lx\n", (unsigned long)*res); | ||
354 | |||
355 | wi0 = get_property(node, "device-id", NULL); | ||
356 | wi1 = get_property(node, "vendor-id", NULL); | ||
357 | wi2 = get_property(node, "class-code", NULL); | ||
358 | wi3 = get_property(node, "revision-id", NULL); | ||
359 | |||
360 | celleb_config_write_fake(*config, PCI_DEVICE_ID, 2, wi0[0] & 0xffff); | ||
361 | celleb_config_write_fake(*config, PCI_VENDOR_ID, 2, wi1[0] & 0xffff); | ||
362 | pr_debug("class-code = 0x%08x\n", wi2[0]); | ||
363 | |||
364 | celleb_config_write_fake(*config, PCI_CLASS_PROG, 1, wi2[0] & 0xff); | ||
365 | celleb_config_write_fake(*config, PCI_CLASS_DEVICE, 2, | ||
366 | (wi2[0] >> 8) & 0xffff); | ||
367 | celleb_config_write_fake(*config, PCI_REVISION_ID, 1, wi3[0]); | ||
368 | |||
369 | while (num_base_addr < MAX_PCI_BASE_ADDRS) { | ||
370 | result = of_address_to_resource(node, | ||
371 | num_base_addr, &(*res)->r[num_base_addr]); | ||
372 | if (result) | ||
373 | break; | ||
374 | num_base_addr++; | ||
375 | } | ||
376 | |||
377 | celleb_setup_pci_base_addrs(hose, devno, fn, num_base_addr); | ||
378 | |||
379 | li = get_property(node, "interrupts", &rlen); | ||
380 | val = li[0]; | ||
381 | celleb_config_write_fake(*config, PCI_INTERRUPT_PIN, 1, 1); | ||
382 | celleb_config_write_fake(*config, PCI_INTERRUPT_LINE, 1, val); | ||
383 | |||
384 | #ifdef DEBUG | ||
385 | pr_debug("PCI: %s irq=%ld\n", name, li[0]); | ||
386 | for (i = 0; i < 6; i++) { | ||
387 | celleb_config_read_fake(*config, | ||
388 | PCI_BASE_ADDRESS_0 + 0x4 * i, 4, | ||
389 | &val); | ||
390 | pr_debug("PCI: %s fn=%d base_address_%d=0x%x\n", | ||
391 | name, fn, i, val); | ||
392 | } | ||
393 | #endif | ||
394 | |||
395 | celleb_config_write_fake(*config, PCI_HEADER_TYPE, 1, | ||
396 | PCI_HEADER_TYPE_NORMAL); | ||
397 | |||
398 | return 0; | ||
399 | |||
400 | error: | ||
401 | if (mem_init_done) { | ||
402 | if (config && *config) | ||
403 | kfree(*config); | ||
404 | if (res && *res) | ||
405 | kfree(*res); | ||
406 | |||
407 | } else { | ||
408 | if (config && *config) { | ||
409 | size = 256; | ||
410 | free_bootmem((unsigned long)(*config), size); | ||
411 | } | ||
412 | if (res && *res) { | ||
413 | size = sizeof(struct celleb_pci_resource); | ||
414 | free_bootmem((unsigned long)(*res), size); | ||
415 | } | ||
416 | } | ||
417 | |||
418 | return 1; | ||
419 | } | ||
420 | |||
421 | static int __devinit phb_set_bus_ranges(struct device_node *dev, | ||
422 | struct pci_controller *phb) | ||
423 | { | ||
424 | const int *bus_range; | ||
425 | unsigned int len; | ||
426 | |||
427 | bus_range = get_property(dev, "bus-range", &len); | ||
428 | if (bus_range == NULL || len < 2 * sizeof(int)) | ||
429 | return 1; | ||
430 | |||
431 | phb->first_busno = bus_range[0]; | ||
432 | phb->last_busno = bus_range[1]; | ||
433 | |||
434 | return 0; | ||
435 | } | ||
436 | |||
437 | static void __devinit celleb_alloc_private_mem(struct pci_controller *hose) | ||
438 | { | ||
439 | if (mem_init_done) | ||
440 | hose->private_data = | ||
441 | kzalloc(sizeof(struct celleb_pci_private), GFP_KERNEL); | ||
442 | else | ||
443 | hose->private_data = | ||
444 | alloc_bootmem(sizeof(struct celleb_pci_private)); | ||
445 | } | ||
446 | |||
447 | int __devinit celleb_setup_phb(struct pci_controller *phb) | ||
448 | { | ||
449 | const char *name; | ||
450 | struct device_node *dev = phb->arch_data; | ||
451 | struct device_node *node; | ||
452 | unsigned int rlen; | ||
453 | |||
454 | name = get_property(dev, "name", &rlen); | ||
455 | if (!name) | ||
456 | return 1; | ||
457 | |||
458 | pr_debug("PCI: celleb_setup_phb() %s\n", name); | ||
459 | phb_set_bus_ranges(dev, phb); | ||
460 | |||
461 | if (strcmp(name, "epci") == 0) { | ||
462 | phb->ops = &celleb_epci_ops; | ||
463 | return celleb_setup_epci(dev, phb); | ||
464 | |||
465 | } else if (strcmp(name, "pci-pseudo") == 0) { | ||
466 | phb->ops = &celleb_fake_pci_ops; | ||
467 | celleb_alloc_private_mem(phb); | ||
468 | for (node = of_get_next_child(dev, NULL); | ||
469 | node != NULL; node = of_get_next_child(dev, node)) | ||
470 | celleb_setup_fake_pci_device(node, phb); | ||
471 | |||
472 | } else | ||
473 | return 1; | ||
474 | |||
475 | return 0; | ||
476 | } | ||
477 | |||
478 | int celleb_pci_probe_mode(struct pci_bus *bus) | ||
479 | { | ||
480 | return PCI_PROBE_DEVTREE; | ||
481 | } | ||
diff --git a/arch/powerpc/platforms/celleb/pci.h b/arch/powerpc/platforms/celleb/pci.h new file mode 100644 index 000000000000..5340e348e297 --- /dev/null +++ b/arch/powerpc/platforms/celleb/pci.h | |||
@@ -0,0 +1,35 @@ | |||
1 | /* | ||
2 | * pci prototypes for Celleb platform | ||
3 | * | ||
4 | * (C) Copyright 2006-2007 TOSHIBA CORPORATION | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License along | ||
17 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | */ | ||
20 | |||
21 | #ifndef _CELLEB_PCI_H | ||
22 | #define _CELLEB_PCI_H | ||
23 | |||
24 | #include <linux/pci.h> | ||
25 | |||
26 | #include <asm/pci-bridge.h> | ||
27 | #include <asm/prom.h> | ||
28 | |||
29 | extern int celleb_setup_phb(struct pci_controller *); | ||
30 | extern int celleb_pci_probe_mode(struct pci_bus *); | ||
31 | |||
32 | extern struct pci_ops celleb_epci_ops; | ||
33 | extern int celleb_setup_epci(struct device_node *, struct pci_controller *); | ||
34 | |||
35 | #endif /* _CELLEB_PCI_H */ | ||
diff --git a/arch/powerpc/platforms/celleb/scc.h b/arch/powerpc/platforms/celleb/scc.h new file mode 100644 index 000000000000..e9ce8a7c1882 --- /dev/null +++ b/arch/powerpc/platforms/celleb/scc.h | |||
@@ -0,0 +1,145 @@ | |||
1 | /* | ||
2 | * SCC (Super Companion Chip) definitions | ||
3 | * | ||
4 | * (C) Copyright 2004-2006 TOSHIBA CORPORATION | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License along | ||
17 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | */ | ||
20 | |||
21 | #ifndef _CELLEB_SCC_H | ||
22 | #define _CELLEB_SCC_H | ||
23 | |||
24 | #define PCI_VENDOR_ID_TOSHIBA_2 0x102f | ||
25 | #define PCI_DEVICE_ID_TOSHIBA_SCC_PCIEXC_BRIDGE 0x01b0 | ||
26 | #define PCI_DEVICE_ID_TOSHIBA_SCC_EPCI_BRIDGE 0x01b1 | ||
27 | #define PCI_DEVICE_ID_TOSHIBA_SCC_BRIDGE 0x01b2 | ||
28 | #define PCI_DEVICE_ID_TOSHIBA_SCC_GBE 0x01b3 | ||
29 | #define PCI_DEVICE_ID_TOSHIBA_SCC_ATA 0x01b4 | ||
30 | #define PCI_DEVICE_ID_TOSHIBA_SCC_USB2 0x01b5 | ||
31 | #define PCI_DEVICE_ID_TOSHIBA_SCC_USB 0x01b6 | ||
32 | #define PCI_DEVICE_ID_TOSHIBA_SCC_ENCDEC 0x01b7 | ||
33 | |||
34 | #define SCC_EPCI_REG 0x0000d000 | ||
35 | |||
36 | /* EPCI registers */ | ||
37 | #define SCC_EPCI_CNF10_REG 0x010 | ||
38 | #define SCC_EPCI_CNF14_REG 0x014 | ||
39 | #define SCC_EPCI_CNF18_REG 0x018 | ||
40 | #define SCC_EPCI_PVBAT 0x100 | ||
41 | #define SCC_EPCI_VPMBAT 0x104 | ||
42 | #define SCC_EPCI_VPIBAT 0x108 | ||
43 | #define SCC_EPCI_VCSR 0x110 | ||
44 | #define SCC_EPCI_VIENAB 0x114 | ||
45 | #define SCC_EPCI_VISTAT 0x118 | ||
46 | #define SCC_EPCI_VRDCOUNT 0x124 | ||
47 | #define SCC_EPCI_BAM0 0x12c | ||
48 | #define SCC_EPCI_BAM1 0x134 | ||
49 | #define SCC_EPCI_BAM2 0x13c | ||
50 | #define SCC_EPCI_IADR 0x164 | ||
51 | #define SCC_EPCI_CLKRST 0x800 | ||
52 | #define SCC_EPCI_INTSET 0x804 | ||
53 | #define SCC_EPCI_STATUS 0x808 | ||
54 | #define SCC_EPCI_ABTSET 0x80c | ||
55 | #define SCC_EPCI_WATRP 0x810 | ||
56 | #define SCC_EPCI_DUMMYRADR 0x814 | ||
57 | #define SCC_EPCI_SWRESP 0x818 | ||
58 | #define SCC_EPCI_CNTOPT 0x81c | ||
59 | #define SCC_EPCI_ECMODE 0xf00 | ||
60 | #define SCC_EPCI_IOM_AC_NUM 5 | ||
61 | #define SCC_EPCI_IOM_ACTE(n) (0xf10 + (n) * 4) | ||
62 | #define SCC_EPCI_IOT_AC_NUM 4 | ||
63 | #define SCC_EPCI_IOT_ACTE(n) (0xf30 + (n) * 4) | ||
64 | #define SCC_EPCI_MAEA 0xf50 | ||
65 | #define SCC_EPCI_MAEC 0xf54 | ||
66 | #define SCC_EPCI_CKCTRL 0xff0 | ||
67 | |||
68 | /* bits for SCC_EPCI_VCSR */ | ||
69 | #define SCC_EPCI_VCSR_FRE 0x00020000 | ||
70 | #define SCC_EPCI_VCSR_FWE 0x00010000 | ||
71 | #define SCC_EPCI_VCSR_DR 0x00000400 | ||
72 | #define SCC_EPCI_VCSR_SR 0x00000008 | ||
73 | #define SCC_EPCI_VCSR_AT 0x00000004 | ||
74 | |||
75 | /* bits for SCC_EPCI_VIENAB/SCC_EPCI_VISTAT */ | ||
76 | #define SCC_EPCI_VISTAT_PMPE 0x00000008 | ||
77 | #define SCC_EPCI_VISTAT_PMFE 0x00000004 | ||
78 | #define SCC_EPCI_VISTAT_PRA 0x00000002 | ||
79 | #define SCC_EPCI_VISTAT_PRD 0x00000001 | ||
80 | #define SCC_EPCI_VISTAT_ALL 0x0000000f | ||
81 | |||
82 | #define SCC_EPCI_VIENAB_PMPEE 0x00000008 | ||
83 | #define SCC_EPCI_VIENAB_PMFEE 0x00000004 | ||
84 | #define SCC_EPCI_VIENAB_PRA 0x00000002 | ||
85 | #define SCC_EPCI_VIENAB_PRD 0x00000001 | ||
86 | #define SCC_EPCI_VIENAB_ALL 0x0000000f | ||
87 | |||
88 | /* bits for SCC_EPCI_CLKRST */ | ||
89 | #define SCC_EPCI_CLKRST_CKS_MASK 0x00030000 | ||
90 | #define SCC_EPCI_CLKRST_CKS_2 0x00000000 | ||
91 | #define SCC_EPCI_CLKRST_CKS_4 0x00010000 | ||
92 | #define SCC_EPCI_CLKRST_CKS_8 0x00020000 | ||
93 | #define SCC_EPCI_CLKRST_PCICRST 0x00000400 | ||
94 | #define SCC_EPCI_CLKRST_BC 0x00000200 | ||
95 | #define SCC_EPCI_CLKRST_PCIRST 0x00000100 | ||
96 | #define SCC_EPCI_CLKRST_PCKEN 0x00000001 | ||
97 | |||
98 | /* bits for SCC_EPCI_INTSET/SCC_EPCI_STATUS */ | ||
99 | #define SCC_EPCI_INT_2M 0x01000000 | ||
100 | #define SCC_EPCI_INT_RERR 0x00200000 | ||
101 | #define SCC_EPCI_INT_SERR 0x00100000 | ||
102 | #define SCC_EPCI_INT_PRTER 0x00080000 | ||
103 | #define SCC_EPCI_INT_SER 0x00040000 | ||
104 | #define SCC_EPCI_INT_PER 0x00020000 | ||
105 | #define SCC_EPCI_INT_PAI 0x00010000 | ||
106 | #define SCC_EPCI_INT_1M 0x00000100 | ||
107 | #define SCC_EPCI_INT_PME 0x00000010 | ||
108 | #define SCC_EPCI_INT_INTD 0x00000008 | ||
109 | #define SCC_EPCI_INT_INTC 0x00000004 | ||
110 | #define SCC_EPCI_INT_INTB 0x00000002 | ||
111 | #define SCC_EPCI_INT_INTA 0x00000001 | ||
112 | #define SCC_EPCI_INT_DEVINT 0x0000000f | ||
113 | #define SCC_EPCI_INT_ALL 0x003f001f | ||
114 | #define SCC_EPCI_INT_ALLERR 0x003f0000 | ||
115 | |||
116 | /* bits for SCC_EPCI_CKCTRL */ | ||
117 | #define SCC_EPCI_CKCTRL_CRST0 0x00010000 | ||
118 | #define SCC_EPCI_CKCTRL_CRST1 0x00020000 | ||
119 | #define SCC_EPCI_CKCTRL_OCLKEN 0x00000100 | ||
120 | #define SCC_EPCI_CKCTRL_LCLKEN 0x00000001 | ||
121 | |||
122 | #define SCC_EPCI_IDSEL_AD_TO_SLOT(ad) ((ad) - 10) | ||
123 | #define SCC_EPCI_MAX_DEVNU SCC_EPCI_IDSEL_AD_TO_SLOT(32) | ||
124 | |||
125 | /* bits for SCC_EPCI_CNTOPT */ | ||
126 | #define SCC_EPCI_CNTOPT_O2PMB 0x00000002 | ||
127 | |||
128 | /* UHC registers */ | ||
129 | #define SCC_UHC_CKRCTRL 0xff0 | ||
130 | #define SCC_UHC_ECMODE 0xf00 | ||
131 | |||
132 | /* bits for SCC_UHC_CKRCTRL */ | ||
133 | #define SCC_UHC_F48MCKLEN 0x00000001 | ||
134 | #define SCC_UHC_P_SUSPEND 0x00000002 | ||
135 | #define SCC_UHC_PHY_SUSPEND_SEL 0x00000004 | ||
136 | #define SCC_UHC_HCLKEN 0x00000100 | ||
137 | #define SCC_UHC_USBEN 0x00010000 | ||
138 | #define SCC_UHC_USBCEN 0x00020000 | ||
139 | #define SCC_UHC_PHYEN 0x00040000 | ||
140 | |||
141 | /* bits for SCC_UHC_ECMODE */ | ||
142 | #define SCC_UHC_ECMODE_BY_BYTE 0x00000555 | ||
143 | #define SCC_UHC_ECMODE_BY_WORD 0x00000aaa | ||
144 | |||
145 | #endif /* _CELLEB_SCC_H */ | ||
diff --git a/arch/powerpc/platforms/celleb/scc_epci.c b/arch/powerpc/platforms/celleb/scc_epci.c new file mode 100644 index 000000000000..0edbc0c4f338 --- /dev/null +++ b/arch/powerpc/platforms/celleb/scc_epci.c | |||
@@ -0,0 +1,409 @@ | |||
1 | /* | ||
2 | * Support for SCC external PCI | ||
3 | * | ||
4 | * (C) Copyright 2004-2007 TOSHIBA CORPORATION | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License along | ||
17 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | */ | ||
20 | |||
21 | #undef DEBUG | ||
22 | |||
23 | #include <linux/kernel.h> | ||
24 | #include <linux/threads.h> | ||
25 | #include <linux/pci.h> | ||
26 | #include <linux/init.h> | ||
27 | #include <linux/pci_regs.h> | ||
28 | #include <linux/bootmem.h> | ||
29 | |||
30 | #include <asm/io.h> | ||
31 | #include <asm/irq.h> | ||
32 | #include <asm/prom.h> | ||
33 | #include <asm/machdep.h> | ||
34 | #include <asm/pci-bridge.h> | ||
35 | #include <asm/ppc-pci.h> | ||
36 | |||
37 | #include "scc.h" | ||
38 | #include "pci.h" | ||
39 | #include "interrupt.h" | ||
40 | |||
41 | #define MAX_PCI_DEVICES 32 | ||
42 | #define MAX_PCI_FUNCTIONS 8 | ||
43 | |||
44 | #define iob() __asm__ __volatile__("eieio; sync":::"memory") | ||
45 | |||
46 | |||
47 | #if 0 /* test code for epci dummy read */ | ||
48 | static void celleb_epci_dummy_read(struct pci_dev *dev) | ||
49 | { | ||
50 | void *epci_base; | ||
51 | struct device_node *node; | ||
52 | struct pci_controller *hose; | ||
53 | u32 val; | ||
54 | |||
55 | node = (struct device_node *)dev->bus->sysdata; | ||
56 | hose = pci_find_hose_for_OF_device(node); | ||
57 | |||
58 | if (!hose) | ||
59 | return; | ||
60 | |||
61 | epci_base = (void *)hose->cfg_addr; | ||
62 | |||
63 | val = in_be32(epci_base + SCC_EPCI_WATRP); | ||
64 | iosync(); | ||
65 | |||
66 | return; | ||
67 | } | ||
68 | #endif | ||
69 | |||
70 | static inline void clear_and_disable_master_abort_interrupt( | ||
71 | struct pci_controller *hose) | ||
72 | { | ||
73 | void __iomem *addr; | ||
74 | addr = (void *)hose->cfg_addr + PCI_COMMAND; | ||
75 | out_be32(addr, in_be32(addr) | (PCI_STATUS_REC_MASTER_ABORT << 16)); | ||
76 | } | ||
77 | |||
78 | static int celleb_epci_check_abort(struct pci_controller *hose, | ||
79 | unsigned long addr) | ||
80 | { | ||
81 | void __iomem *reg, *epci_base; | ||
82 | u32 val; | ||
83 | |||
84 | iob(); | ||
85 | epci_base = (void *)hose->cfg_addr; | ||
86 | |||
87 | reg = epci_base + PCI_COMMAND; | ||
88 | val = in_be32(reg); | ||
89 | |||
90 | if (val & (PCI_STATUS_REC_MASTER_ABORT << 16)) { | ||
91 | out_be32(reg, | ||
92 | (val & 0xffff) | (PCI_STATUS_REC_MASTER_ABORT << 16)); | ||
93 | |||
94 | /* clear PCI Controller error, FRE, PMFE */ | ||
95 | reg = epci_base + SCC_EPCI_STATUS; | ||
96 | out_be32(reg, SCC_EPCI_INT_PAI); | ||
97 | |||
98 | reg = epci_base + SCC_EPCI_VCSR; | ||
99 | val = in_be32(reg) & 0xffff; | ||
100 | val |= SCC_EPCI_VCSR_FRE; | ||
101 | out_be32(reg, val); | ||
102 | |||
103 | reg = epci_base + SCC_EPCI_VISTAT; | ||
104 | out_be32(reg, SCC_EPCI_VISTAT_PMFE); | ||
105 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
106 | } | ||
107 | |||
108 | return PCIBIOS_SUCCESSFUL; | ||
109 | } | ||
110 | |||
111 | static unsigned long celleb_epci_make_config_addr(struct pci_controller *hose, | ||
112 | unsigned int devfn, int where) | ||
113 | { | ||
114 | unsigned long addr; | ||
115 | struct pci_bus *bus = hose->bus; | ||
116 | |||
117 | if (bus->self) | ||
118 | addr = (unsigned long)hose->cfg_data + | ||
119 | (((bus->number & 0xff) << 16) | ||
120 | | ((devfn & 0xff) << 8) | ||
121 | | (where & 0xff) | ||
122 | | 0x01000000); | ||
123 | else | ||
124 | addr = (unsigned long)hose->cfg_data + | ||
125 | (((devfn & 0xff) << 8) | (where & 0xff)); | ||
126 | |||
127 | pr_debug("EPCI: config_addr = 0x%016lx\n", addr); | ||
128 | |||
129 | return addr; | ||
130 | } | ||
131 | |||
132 | static int celleb_epci_read_config(struct pci_bus *bus, | ||
133 | unsigned int devfn, int where, int size, u32 * val) | ||
134 | { | ||
135 | unsigned long addr; | ||
136 | struct device_node *node; | ||
137 | struct pci_controller *hose; | ||
138 | |||
139 | /* allignment check */ | ||
140 | BUG_ON(where % size); | ||
141 | |||
142 | node = (struct device_node *)bus->sysdata; | ||
143 | hose = pci_find_hose_for_OF_device(node); | ||
144 | |||
145 | if (!hose->cfg_data) | ||
146 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
147 | |||
148 | if (bus->number == hose->first_busno && devfn == 0) { | ||
149 | /* EPCI controller self */ | ||
150 | |||
151 | addr = (unsigned long)hose->cfg_addr + where; | ||
152 | |||
153 | switch (size) { | ||
154 | case 1: | ||
155 | *val = in_8((u8 *)addr); | ||
156 | break; | ||
157 | case 2: | ||
158 | *val = in_be16((u16 *)addr); | ||
159 | break; | ||
160 | case 4: | ||
161 | *val = in_be32((u32 *)addr); | ||
162 | break; | ||
163 | default: | ||
164 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
165 | } | ||
166 | |||
167 | } else { | ||
168 | |||
169 | clear_and_disable_master_abort_interrupt(hose); | ||
170 | addr = celleb_epci_make_config_addr(hose, devfn, where); | ||
171 | |||
172 | switch (size) { | ||
173 | case 1: | ||
174 | *val = in_8((u8 *)addr); | ||
175 | break; | ||
176 | case 2: | ||
177 | *val = in_le16((u16 *)addr); | ||
178 | break; | ||
179 | case 4: | ||
180 | *val = in_le32((u32 *)addr); | ||
181 | break; | ||
182 | default: | ||
183 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
184 | } | ||
185 | } | ||
186 | |||
187 | pr_debug("EPCI: " | ||
188 | "addr=0x%lx, devfn=0x%x, where=0x%x, size=0x%x, val=0x%x\n", | ||
189 | addr, devfn, where, size, *val); | ||
190 | |||
191 | return celleb_epci_check_abort(hose, 0); | ||
192 | } | ||
193 | |||
194 | static int celleb_epci_write_config(struct pci_bus *bus, | ||
195 | unsigned int devfn, int where, int size, u32 val) | ||
196 | { | ||
197 | unsigned long addr; | ||
198 | struct device_node *node; | ||
199 | struct pci_controller *hose; | ||
200 | |||
201 | /* allignment check */ | ||
202 | BUG_ON(where % size); | ||
203 | |||
204 | node = (struct device_node *)bus->sysdata; | ||
205 | hose = pci_find_hose_for_OF_device(node); | ||
206 | |||
207 | if (!hose->cfg_data) | ||
208 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
209 | |||
210 | if (bus->number == hose->first_busno && devfn == 0) { | ||
211 | /* EPCI controller self */ | ||
212 | |||
213 | addr = (unsigned long)hose->cfg_addr + where; | ||
214 | |||
215 | switch (size) { | ||
216 | case 1: | ||
217 | out_8((u8 *)addr, val); | ||
218 | break; | ||
219 | case 2: | ||
220 | out_be16((u16 *)addr, val); | ||
221 | break; | ||
222 | case 4: | ||
223 | out_be32((u32 *)addr, val); | ||
224 | break; | ||
225 | default: | ||
226 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
227 | } | ||
228 | |||
229 | } else { | ||
230 | |||
231 | clear_and_disable_master_abort_interrupt(hose); | ||
232 | addr = celleb_epci_make_config_addr(hose, devfn, where); | ||
233 | |||
234 | switch (size) { | ||
235 | case 1: | ||
236 | out_8((u8 *)addr, val); | ||
237 | break; | ||
238 | case 2: | ||
239 | out_le16((u16 *)addr, val); | ||
240 | break; | ||
241 | case 4: | ||
242 | out_le32((u32 *)addr, val); | ||
243 | break; | ||
244 | default: | ||
245 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
246 | } | ||
247 | } | ||
248 | |||
249 | return celleb_epci_check_abort(hose, addr); | ||
250 | } | ||
251 | |||
252 | struct pci_ops celleb_epci_ops = { | ||
253 | celleb_epci_read_config, | ||
254 | celleb_epci_write_config, | ||
255 | }; | ||
256 | |||
257 | /* to be moved in FW */ | ||
258 | static int __devinit celleb_epci_init(struct pci_controller *hose) | ||
259 | { | ||
260 | u32 val; | ||
261 | void __iomem *reg, *epci_base; | ||
262 | int hwres = 0; | ||
263 | |||
264 | epci_base = (void *)hose->cfg_addr; | ||
265 | |||
266 | /* PCI core reset(Internal bus and PCI clock) */ | ||
267 | reg = epci_base + SCC_EPCI_CKCTRL; | ||
268 | val = in_be32(reg); | ||
269 | if (val == 0x00030101) | ||
270 | hwres = 1; | ||
271 | else { | ||
272 | val &= ~(SCC_EPCI_CKCTRL_CRST0 | SCC_EPCI_CKCTRL_CRST1); | ||
273 | out_be32(reg, val); | ||
274 | |||
275 | /* set PCI core clock */ | ||
276 | val = in_be32(reg); | ||
277 | val |= (SCC_EPCI_CKCTRL_OCLKEN | SCC_EPCI_CKCTRL_LCLKEN); | ||
278 | out_be32(reg, val); | ||
279 | |||
280 | /* release PCI core reset (internal bus) */ | ||
281 | val = in_be32(reg); | ||
282 | val |= SCC_EPCI_CKCTRL_CRST0; | ||
283 | out_be32(reg, val); | ||
284 | |||
285 | /* set PCI clock select */ | ||
286 | reg = epci_base + SCC_EPCI_CLKRST; | ||
287 | val = in_be32(reg); | ||
288 | val &= ~SCC_EPCI_CLKRST_CKS_MASK; | ||
289 | val |= SCC_EPCI_CLKRST_CKS_2; | ||
290 | out_be32(reg, val); | ||
291 | |||
292 | /* set arbiter */ | ||
293 | reg = epci_base + SCC_EPCI_ABTSET; | ||
294 | out_be32(reg, 0x0f1f001f); /* temporary value */ | ||
295 | |||
296 | /* buffer on */ | ||
297 | reg = epci_base + SCC_EPCI_CLKRST; | ||
298 | val = in_be32(reg); | ||
299 | val |= SCC_EPCI_CLKRST_BC; | ||
300 | out_be32(reg, val); | ||
301 | |||
302 | /* PCI clock enable */ | ||
303 | val = in_be32(reg); | ||
304 | val |= SCC_EPCI_CLKRST_PCKEN; | ||
305 | out_be32(reg, val); | ||
306 | |||
307 | /* release PCI core reset (all) */ | ||
308 | reg = epci_base + SCC_EPCI_CKCTRL; | ||
309 | val = in_be32(reg); | ||
310 | val |= (SCC_EPCI_CKCTRL_CRST0 | SCC_EPCI_CKCTRL_CRST1); | ||
311 | out_be32(reg, val); | ||
312 | |||
313 | /* set base translation registers. (already set by Beat) */ | ||
314 | |||
315 | /* set base address masks. (already set by Beat) */ | ||
316 | } | ||
317 | |||
318 | /* release interrupt masks and clear all interrupts */ | ||
319 | reg = epci_base + SCC_EPCI_INTSET; | ||
320 | out_be32(reg, 0x013f011f); /* all interrupts enable */ | ||
321 | reg = epci_base + SCC_EPCI_VIENAB; | ||
322 | val = SCC_EPCI_VIENAB_PMPEE | SCC_EPCI_VIENAB_PMFEE; | ||
323 | out_be32(reg, val); | ||
324 | reg = epci_base + SCC_EPCI_STATUS; | ||
325 | out_be32(reg, 0xffffffff); | ||
326 | reg = epci_base + SCC_EPCI_VISTAT; | ||
327 | out_be32(reg, 0xffffffff); | ||
328 | |||
329 | /* disable PCI->IB address translation */ | ||
330 | reg = epci_base + SCC_EPCI_VCSR; | ||
331 | val = in_be32(reg); | ||
332 | val &= ~(SCC_EPCI_VCSR_DR | SCC_EPCI_VCSR_AT); | ||
333 | out_be32(reg, val); | ||
334 | |||
335 | /* set base addresses. (no need to set?) */ | ||
336 | |||
337 | /* memory space, bus master enable */ | ||
338 | reg = epci_base + PCI_COMMAND; | ||
339 | val = PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; | ||
340 | out_be32(reg, val); | ||
341 | |||
342 | /* endian mode setup */ | ||
343 | reg = epci_base + SCC_EPCI_ECMODE; | ||
344 | val = 0x00550155; | ||
345 | out_be32(reg, val); | ||
346 | |||
347 | /* set control option */ | ||
348 | reg = epci_base + SCC_EPCI_CNTOPT; | ||
349 | val = in_be32(reg); | ||
350 | val |= SCC_EPCI_CNTOPT_O2PMB; | ||
351 | out_be32(reg, val); | ||
352 | |||
353 | /* XXX: temporay: set registers for address conversion setup */ | ||
354 | reg = epci_base + SCC_EPCI_CNF10_REG; | ||
355 | out_be32(reg, 0x80000008); | ||
356 | reg = epci_base + SCC_EPCI_CNF14_REG; | ||
357 | out_be32(reg, 0x40000008); | ||
358 | |||
359 | reg = epci_base + SCC_EPCI_BAM0; | ||
360 | out_be32(reg, 0x80000000); | ||
361 | reg = epci_base + SCC_EPCI_BAM1; | ||
362 | out_be32(reg, 0xe0000000); | ||
363 | |||
364 | reg = epci_base + SCC_EPCI_PVBAT; | ||
365 | out_be32(reg, 0x80000000); | ||
366 | |||
367 | if (!hwres) { | ||
368 | /* release external PCI reset */ | ||
369 | reg = epci_base + SCC_EPCI_CLKRST; | ||
370 | val = in_be32(reg); | ||
371 | val |= SCC_EPCI_CLKRST_PCIRST; | ||
372 | out_be32(reg, val); | ||
373 | } | ||
374 | |||
375 | return 0; | ||
376 | } | ||
377 | |||
378 | int __devinit celleb_setup_epci(struct device_node *node, | ||
379 | struct pci_controller *hose) | ||
380 | { | ||
381 | struct resource r; | ||
382 | |||
383 | pr_debug("PCI: celleb_setup_epci()\n"); | ||
384 | |||
385 | if (of_address_to_resource(node, 0, &r)) | ||
386 | goto error; | ||
387 | hose->cfg_addr = ioremap(r.start, (r.end - r.start + 1)); | ||
388 | if (!hose->cfg_addr) | ||
389 | goto error; | ||
390 | pr_debug("EPCI: cfg_addr map 0x%016lx->0x%016lx + 0x%016lx\n", | ||
391 | r.start, (unsigned long)hose->cfg_addr, | ||
392 | (r.end - r.start + 1)); | ||
393 | |||
394 | if (of_address_to_resource(node, 2, &r)) | ||
395 | goto error; | ||
396 | hose->cfg_data = ioremap(r.start, (r.end - r.start + 1)); | ||
397 | if (!hose->cfg_data) | ||
398 | goto error; | ||
399 | pr_debug("EPCI: cfg_data map 0x%016lx->0x%016lx + 0x%016lx\n", | ||
400 | r.start, (unsigned long)hose->cfg_data, | ||
401 | (r.end - r.start + 1)); | ||
402 | |||
403 | celleb_epci_init(hose); | ||
404 | |||
405 | return 0; | ||
406 | |||
407 | error: | ||
408 | return 1; | ||
409 | } | ||
diff --git a/arch/powerpc/platforms/celleb/scc_sio.c b/arch/powerpc/platforms/celleb/scc_sio.c new file mode 100644 index 000000000000..bcd25f54d986 --- /dev/null +++ b/arch/powerpc/platforms/celleb/scc_sio.c | |||
@@ -0,0 +1,101 @@ | |||
1 | /* | ||
2 | * setup serial port in SCC | ||
3 | * | ||
4 | * (C) Copyright 2006 TOSHIBA CORPORATION | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License along | ||
17 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | */ | ||
20 | |||
21 | #include <linux/tty.h> | ||
22 | #include <linux/serial.h> | ||
23 | #include <linux/serial_core.h> | ||
24 | #include <linux/console.h> | ||
25 | |||
26 | #include <asm/io.h> | ||
27 | #include <asm/prom.h> | ||
28 | |||
29 | /* sio irq0=0xb00010022 irq0=0xb00010023 irq2=0xb00010024 | ||
30 | mmio=0xfff000-0x1000,0xff2000-0x1000 */ | ||
31 | static int txx9_serial_bitmap = 0; | ||
32 | |||
33 | static struct { | ||
34 | uint32_t offset; | ||
35 | uint32_t index; | ||
36 | } txx9_scc_tab[3] = { | ||
37 | { 0x300, 0 }, /* 0xFFF300 */ | ||
38 | { 0x400, 0 }, /* 0xFFF400 */ | ||
39 | { 0x800, 1 } /* 0xFF2800 */ | ||
40 | }; | ||
41 | |||
42 | static int txx9_serial_init(void) | ||
43 | { | ||
44 | extern int early_serial_txx9_setup(struct uart_port *port); | ||
45 | struct device_node *node; | ||
46 | int i; | ||
47 | struct uart_port req; | ||
48 | struct of_irq irq; | ||
49 | struct resource res; | ||
50 | |||
51 | node = of_find_node_by_path("/ioif1/sio"); | ||
52 | if (!node) | ||
53 | return 0; | ||
54 | |||
55 | for(i = 0; i < sizeof(txx9_scc_tab)/sizeof(txx9_scc_tab[0]); i++) { | ||
56 | if (!(txx9_serial_bitmap & (1<<i))) | ||
57 | continue; | ||
58 | |||
59 | if (of_irq_map_one(node, i, &irq)) | ||
60 | continue; | ||
61 | if (of_address_to_resource(node, txx9_scc_tab[i].index, &res)) | ||
62 | continue; | ||
63 | |||
64 | memset(&req, 0, sizeof(req)); | ||
65 | req.line = i; | ||
66 | req.iotype = UPIO_MEM; | ||
67 | req.mapbase = res.start + txx9_scc_tab[i].offset; | ||
68 | #ifdef CONFIG_SERIAL_TXX9_CONSOLE | ||
69 | req.membase = ioremap(req.mapbase, 0x24); | ||
70 | #endif | ||
71 | req.irq = irq_create_of_mapping(irq.controller, | ||
72 | irq.specifier, irq.size); | ||
73 | req.flags |= UPF_IOREMAP | UPF_BUGGY_UART /*HAVE_CTS_LINE*/; | ||
74 | req.uartclk = 83300000; | ||
75 | early_serial_txx9_setup(&req); | ||
76 | } | ||
77 | |||
78 | of_node_put(node); | ||
79 | return 0; | ||
80 | } | ||
81 | |||
82 | static int txx9_serial_config(char *ptr) | ||
83 | { | ||
84 | int i; | ||
85 | |||
86 | for (;;) { | ||
87 | switch(get_option(&ptr, &i)) { | ||
88 | default: | ||
89 | return 0; | ||
90 | case 2: | ||
91 | txx9_serial_bitmap |= 1 << i; | ||
92 | break; | ||
93 | case 1: | ||
94 | txx9_serial_bitmap |= 1 << i; | ||
95 | return 0; | ||
96 | } | ||
97 | } | ||
98 | } | ||
99 | __setup("txx9_serial=", txx9_serial_config); | ||
100 | |||
101 | console_initcall(txx9_serial_init); | ||
diff --git a/arch/powerpc/platforms/celleb/scc_uhc.c b/arch/powerpc/platforms/celleb/scc_uhc.c new file mode 100644 index 000000000000..a7c548bde2e3 --- /dev/null +++ b/arch/powerpc/platforms/celleb/scc_uhc.c | |||
@@ -0,0 +1,94 @@ | |||
1 | /* | ||
2 | * SCC (Super Companion Chip) UHC setup | ||
3 | * | ||
4 | * (C) Copyright 2006-2007 TOSHIBA CORPORATION | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License along | ||
17 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | */ | ||
20 | |||
21 | #include <linux/kernel.h> | ||
22 | #include <linux/pci.h> | ||
23 | |||
24 | #include <asm/delay.h> | ||
25 | #include <asm/io.h> | ||
26 | #include <asm/machdep.h> | ||
27 | |||
28 | #include "scc.h" | ||
29 | |||
30 | #define UHC_RESET_WAIT_MAX 10000 | ||
31 | |||
32 | static inline int uhc_clkctrl_ready(u32 val) | ||
33 | { | ||
34 | const u32 mask = SCC_UHC_USBCEN | SCC_UHC_USBCEN; | ||
35 | return((val & mask) == mask); | ||
36 | } | ||
37 | |||
38 | /* | ||
39 | * UHC(usb host controler) enable function. | ||
40 | * affect to both of OHCI and EHCI core module. | ||
41 | */ | ||
42 | static void enable_scc_uhc(struct pci_dev *dev) | ||
43 | { | ||
44 | void __iomem *uhc_base; | ||
45 | u32 __iomem *uhc_clkctrl; | ||
46 | u32 __iomem *uhc_ecmode; | ||
47 | u32 val = 0; | ||
48 | int i; | ||
49 | |||
50 | if (!machine_is(celleb)) | ||
51 | return; | ||
52 | |||
53 | uhc_base = ioremap(pci_resource_start(dev, 0), | ||
54 | pci_resource_len(dev, 0)); | ||
55 | if (!uhc_base) { | ||
56 | printk(KERN_ERR "failed to map UHC register base.\n"); | ||
57 | return; | ||
58 | } | ||
59 | uhc_clkctrl = uhc_base + SCC_UHC_CKRCTRL; | ||
60 | uhc_ecmode = uhc_base + SCC_UHC_ECMODE; | ||
61 | |||
62 | /* setup for normal mode */ | ||
63 | val |= SCC_UHC_F48MCKLEN; | ||
64 | out_be32(uhc_clkctrl, val); | ||
65 | val |= SCC_UHC_PHY_SUSPEND_SEL; | ||
66 | out_be32(uhc_clkctrl, val); | ||
67 | udelay(10); | ||
68 | val |= SCC_UHC_PHYEN; | ||
69 | out_be32(uhc_clkctrl, val); | ||
70 | udelay(50); | ||
71 | |||
72 | /* disable reset */ | ||
73 | val |= SCC_UHC_HCLKEN; | ||
74 | out_be32(uhc_clkctrl, val); | ||
75 | val |= (SCC_UHC_USBCEN | SCC_UHC_USBEN); | ||
76 | out_be32(uhc_clkctrl, val); | ||
77 | i = 0; | ||
78 | while (!uhc_clkctrl_ready(in_be32(uhc_clkctrl))) { | ||
79 | udelay(10); | ||
80 | if (i++ > UHC_RESET_WAIT_MAX) { | ||
81 | printk(KERN_ERR "Failed to disable UHC reset %x\n", | ||
82 | in_be32(uhc_clkctrl)); | ||
83 | break; | ||
84 | } | ||
85 | } | ||
86 | |||
87 | /* Endian Conversion Mode for Master ALL area */ | ||
88 | out_be32(uhc_ecmode, SCC_UHC_ECMODE_BY_BYTE); | ||
89 | |||
90 | iounmap(uhc_base); | ||
91 | } | ||
92 | |||
93 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_TOSHIBA_2, | ||
94 | PCI_DEVICE_ID_TOSHIBA_SCC_USB, enable_scc_uhc); | ||
diff --git a/arch/powerpc/platforms/celleb/setup.c b/arch/powerpc/platforms/celleb/setup.c new file mode 100644 index 000000000000..1de63acfda87 --- /dev/null +++ b/arch/powerpc/platforms/celleb/setup.c | |||
@@ -0,0 +1,191 @@ | |||
1 | /* | ||
2 | * Celleb setup code | ||
3 | * | ||
4 | * (C) Copyright 2006-2007 TOSHIBA CORPORATION | ||
5 | * | ||
6 | * This code is based on arch/powerpc/platforms/cell/setup.c: | ||
7 | * Copyright (C) 1995 Linus Torvalds | ||
8 | * Adapted from 'alpha' version by Gary Thomas | ||
9 | * Modified by Cort Dougan (cort@cs.nmt.edu) | ||
10 | * Modified by PPC64 Team, IBM Corp | ||
11 | * Modified by Cell Team, IBM Deutschland Entwicklung GmbH | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or modify | ||
14 | * it under the terms of the GNU General Public License as published by | ||
15 | * the Free Software Foundation; either version 2 of the License, or | ||
16 | * (at your option) any later version. | ||
17 | * | ||
18 | * This program is distributed in the hope that it will be useful, | ||
19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
21 | * GNU General Public License for more details. | ||
22 | * | ||
23 | * You should have received a copy of the GNU General Public License along | ||
24 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
25 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
26 | */ | ||
27 | |||
28 | #undef DEBUG | ||
29 | |||
30 | #include <linux/cpu.h> | ||
31 | #include <linux/sched.h> | ||
32 | #include <linux/kernel.h> | ||
33 | #include <linux/mm.h> | ||
34 | #include <linux/stddef.h> | ||
35 | #include <linux/unistd.h> | ||
36 | #include <linux/reboot.h> | ||
37 | #include <linux/init.h> | ||
38 | #include <linux/delay.h> | ||
39 | #include <linux/irq.h> | ||
40 | #include <linux/seq_file.h> | ||
41 | #include <linux/root_dev.h> | ||
42 | #include <linux/console.h> | ||
43 | |||
44 | #include <asm/mmu.h> | ||
45 | #include <asm/processor.h> | ||
46 | #include <asm/io.h> | ||
47 | #include <asm/kexec.h> | ||
48 | #include <asm/prom.h> | ||
49 | #include <asm/machdep.h> | ||
50 | #include <asm/cputable.h> | ||
51 | #include <asm/irq.h> | ||
52 | #include <asm/spu_priv1.h> | ||
53 | #include <asm/firmware.h> | ||
54 | #include <asm/of_platform.h> | ||
55 | |||
56 | #include "interrupt.h" | ||
57 | #include "beat_wrapper.h" | ||
58 | #include "beat.h" | ||
59 | #include "pci.h" | ||
60 | |||
61 | static char celleb_machine_type[128] = "Celleb"; | ||
62 | |||
63 | static void celleb_show_cpuinfo(struct seq_file *m) | ||
64 | { | ||
65 | struct device_node *root; | ||
66 | const char *model = ""; | ||
67 | |||
68 | root = of_find_node_by_path("/"); | ||
69 | if (root) | ||
70 | model = get_property(root, "model", NULL); | ||
71 | /* using "CHRP" is to trick anaconda into installing FCx into Celleb */ | ||
72 | seq_printf(m, "machine\t\t: %s %s\n", celleb_machine_type, model); | ||
73 | of_node_put(root); | ||
74 | } | ||
75 | |||
76 | static int celleb_machine_type_hack(char *ptr) | ||
77 | { | ||
78 | strncpy(celleb_machine_type, ptr, sizeof(celleb_machine_type)); | ||
79 | celleb_machine_type[sizeof(celleb_machine_type)-1] = 0; | ||
80 | return 0; | ||
81 | } | ||
82 | |||
83 | __setup("celleb_machine_type_hack", celleb_machine_type_hack); | ||
84 | |||
85 | static void celleb_progress(char *s, unsigned short hex) | ||
86 | { | ||
87 | printk("*** %04x : %s\n", hex, s ? s : ""); | ||
88 | } | ||
89 | |||
90 | static void __init celleb_setup_arch(void) | ||
91 | { | ||
92 | #ifdef CONFIG_SPU_BASE | ||
93 | spu_priv1_ops = &spu_priv1_beat_ops; | ||
94 | spu_management_ops = &spu_management_of_ops; | ||
95 | #endif | ||
96 | |||
97 | #ifdef CONFIG_SMP | ||
98 | smp_init_celleb(); | ||
99 | #endif | ||
100 | |||
101 | /* init to some ~sane value until calibrate_delay() runs */ | ||
102 | loops_per_jiffy = 50000000; | ||
103 | |||
104 | if (ROOT_DEV == 0) { | ||
105 | printk("No ramdisk, default root is /dev/hda2\n"); | ||
106 | ROOT_DEV = Root_HDA2; | ||
107 | } | ||
108 | |||
109 | #ifdef CONFIG_DUMMY_CONSOLE | ||
110 | conswitchp = &dummy_con; | ||
111 | #endif | ||
112 | } | ||
113 | |||
114 | static void beat_power_save(void) | ||
115 | { | ||
116 | beat_pause(0); | ||
117 | } | ||
118 | |||
119 | static int __init celleb_probe(void) | ||
120 | { | ||
121 | unsigned long root = of_get_flat_dt_root(); | ||
122 | |||
123 | if (!of_flat_dt_is_compatible(root, "Beat")) | ||
124 | return 0; | ||
125 | |||
126 | powerpc_firmware_features |= FW_FEATURE_CELLEB_POSSIBLE; | ||
127 | hpte_init_beat(); | ||
128 | return 1; | ||
129 | } | ||
130 | |||
131 | /* | ||
132 | * Cell has no legacy IO; anything calling this function has to | ||
133 | * fail or bad things will happen | ||
134 | */ | ||
135 | static int celleb_check_legacy_ioport(unsigned int baseport) | ||
136 | { | ||
137 | return -ENODEV; | ||
138 | } | ||
139 | |||
140 | static void celleb_kexec_cpu_down(int crash, int secondary) | ||
141 | { | ||
142 | beatic_deinit_IRQ(); | ||
143 | } | ||
144 | |||
145 | static struct of_device_id celleb_bus_ids[] = { | ||
146 | { .type = "scc", }, | ||
147 | { .type = "ioif", }, /* old style */ | ||
148 | {}, | ||
149 | }; | ||
150 | |||
151 | static int __init celleb_publish_devices(void) | ||
152 | { | ||
153 | if (!machine_is(celleb)) | ||
154 | return 0; | ||
155 | |||
156 | /* Publish OF platform devices for southbridge IOs */ | ||
157 | of_platform_bus_probe(NULL, celleb_bus_ids, NULL); | ||
158 | |||
159 | return 0; | ||
160 | } | ||
161 | device_initcall(celleb_publish_devices); | ||
162 | |||
163 | define_machine(celleb) { | ||
164 | .name = "Cell Reference Set", | ||
165 | .probe = celleb_probe, | ||
166 | .setup_arch = celleb_setup_arch, | ||
167 | .show_cpuinfo = celleb_show_cpuinfo, | ||
168 | .restart = beat_restart, | ||
169 | .power_off = beat_power_off, | ||
170 | .halt = beat_halt, | ||
171 | .get_rtc_time = beat_get_rtc_time, | ||
172 | .set_rtc_time = beat_set_rtc_time, | ||
173 | .calibrate_decr = generic_calibrate_decr, | ||
174 | .check_legacy_ioport = celleb_check_legacy_ioport, | ||
175 | .progress = celleb_progress, | ||
176 | .power_save = beat_power_save, | ||
177 | .nvram_size = beat_nvram_get_size, | ||
178 | .nvram_read = beat_nvram_read, | ||
179 | .nvram_write = beat_nvram_write, | ||
180 | .set_dabr = beat_set_xdabr, | ||
181 | .init_IRQ = beatic_init_IRQ, | ||
182 | .get_irq = beatic_get_irq, | ||
183 | .pci_probe_mode = celleb_pci_probe_mode, | ||
184 | .pci_setup_phb = celleb_setup_phb, | ||
185 | #ifdef CONFIG_KEXEC | ||
186 | .kexec_cpu_down = celleb_kexec_cpu_down, | ||
187 | .machine_kexec = default_machine_kexec, | ||
188 | .machine_kexec_prepare = default_machine_kexec_prepare, | ||
189 | .machine_crash_shutdown = default_machine_crash_shutdown, | ||
190 | #endif | ||
191 | }; | ||
diff --git a/arch/powerpc/platforms/celleb/smp.c b/arch/powerpc/platforms/celleb/smp.c new file mode 100644 index 000000000000..a7631250aeb4 --- /dev/null +++ b/arch/powerpc/platforms/celleb/smp.c | |||
@@ -0,0 +1,124 @@ | |||
1 | /* | ||
2 | * SMP support for Celleb platform. (Incomplete) | ||
3 | * | ||
4 | * (C) Copyright 2006 TOSHIBA CORPORATION | ||
5 | * | ||
6 | * This code is based on arch/powerpc/platforms/cell/smp.c: | ||
7 | * Dave Engebretsen, Peter Bergner, and | ||
8 | * Mike Corrigan {engebret|bergner|mikec}@us.ibm.com | ||
9 | * Plus various changes from other IBM teams... | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License as published by | ||
13 | * the Free Software Foundation; either version 2 of the License, or | ||
14 | * (at your option) any later version. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | * GNU General Public License for more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License along | ||
22 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
23 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
24 | */ | ||
25 | |||
26 | #undef DEBUG | ||
27 | |||
28 | #include <linux/kernel.h> | ||
29 | #include <linux/smp.h> | ||
30 | #include <linux/interrupt.h> | ||
31 | #include <linux/init.h> | ||
32 | #include <linux/threads.h> | ||
33 | #include <linux/cpu.h> | ||
34 | |||
35 | #include <asm/irq.h> | ||
36 | #include <asm/smp.h> | ||
37 | #include <asm/machdep.h> | ||
38 | #include <asm/udbg.h> | ||
39 | |||
40 | #include "interrupt.h" | ||
41 | |||
42 | #ifdef DEBUG | ||
43 | #define DBG(fmt...) udbg_printf(fmt) | ||
44 | #else | ||
45 | #define DBG(fmt...) | ||
46 | #endif | ||
47 | |||
48 | /* | ||
49 | * The primary thread of each non-boot processor is recorded here before | ||
50 | * smp init. | ||
51 | */ | ||
52 | /* static cpumask_t of_spin_map; */ | ||
53 | |||
54 | /** | ||
55 | * smp_startup_cpu() - start the given cpu | ||
56 | * | ||
57 | * At boot time, there is nothing to do for primary threads which were | ||
58 | * started from Open Firmware. For anything else, call RTAS with the | ||
59 | * appropriate start location. | ||
60 | * | ||
61 | * Returns: | ||
62 | * 0 - failure | ||
63 | * 1 - success | ||
64 | */ | ||
65 | static inline int __devinit smp_startup_cpu(unsigned int lcpu) | ||
66 | { | ||
67 | return 0; | ||
68 | } | ||
69 | |||
70 | static void smp_beatic_message_pass(int target, int msg) | ||
71 | { | ||
72 | unsigned int i; | ||
73 | |||
74 | if (target < NR_CPUS) { | ||
75 | beatic_cause_IPI(target, msg); | ||
76 | } else { | ||
77 | for_each_online_cpu(i) { | ||
78 | if (target == MSG_ALL_BUT_SELF | ||
79 | && i == smp_processor_id()) | ||
80 | continue; | ||
81 | beatic_cause_IPI(i, msg); | ||
82 | } | ||
83 | } | ||
84 | } | ||
85 | |||
86 | static int __init smp_beatic_probe(void) | ||
87 | { | ||
88 | return cpus_weight(cpu_possible_map); | ||
89 | } | ||
90 | |||
91 | static void __devinit smp_beatic_setup_cpu(int cpu) | ||
92 | { | ||
93 | beatic_setup_cpu(cpu); | ||
94 | } | ||
95 | |||
96 | static void __devinit smp_celleb_kick_cpu(int nr) | ||
97 | { | ||
98 | BUG_ON(nr < 0 || nr >= NR_CPUS); | ||
99 | |||
100 | if (!smp_startup_cpu(nr)) | ||
101 | return; | ||
102 | } | ||
103 | |||
104 | static int smp_celleb_cpu_bootable(unsigned int nr) | ||
105 | { | ||
106 | return 1; | ||
107 | } | ||
108 | static struct smp_ops_t bpa_beatic_smp_ops = { | ||
109 | .message_pass = smp_beatic_message_pass, | ||
110 | .probe = smp_beatic_probe, | ||
111 | .kick_cpu = smp_celleb_kick_cpu, | ||
112 | .setup_cpu = smp_beatic_setup_cpu, | ||
113 | .cpu_bootable = smp_celleb_cpu_bootable, | ||
114 | }; | ||
115 | |||
116 | /* This is called very early */ | ||
117 | void __init smp_init_celleb(void) | ||
118 | { | ||
119 | DBG(" -> smp_init_celleb()\n"); | ||
120 | |||
121 | smp_ops = &bpa_beatic_smp_ops; | ||
122 | |||
123 | DBG(" <- smp_init_celleb()\n"); | ||
124 | } | ||
diff --git a/arch/powerpc/platforms/celleb/spu_priv1.c b/arch/powerpc/platforms/celleb/spu_priv1.c new file mode 100644 index 000000000000..2bf6700f747a --- /dev/null +++ b/arch/powerpc/platforms/celleb/spu_priv1.c | |||
@@ -0,0 +1,208 @@ | |||
1 | /* | ||
2 | * spu hypervisor abstraction for Beat | ||
3 | * | ||
4 | * (C) Copyright 2006-2007 TOSHIBA CORPORATION | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License along | ||
17 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | */ | ||
20 | |||
21 | #include <linux/module.h> | ||
22 | |||
23 | #include <asm/types.h> | ||
24 | #include <asm/spu.h> | ||
25 | #include <asm/spu_priv1.h> | ||
26 | |||
27 | #include "beat_wrapper.h" | ||
28 | |||
29 | static inline void _int_mask_set(struct spu *spu, int class, u64 mask) | ||
30 | { | ||
31 | spu->shadow_int_mask_RW[class] = mask; | ||
32 | beat_set_irq_mask_for_spe(spu->spe_id, class, mask); | ||
33 | } | ||
34 | |||
35 | static inline u64 _int_mask_get(struct spu *spu, int class) | ||
36 | { | ||
37 | return spu->shadow_int_mask_RW[class]; | ||
38 | } | ||
39 | |||
40 | static void int_mask_set(struct spu *spu, int class, u64 mask) | ||
41 | { | ||
42 | _int_mask_set(spu, class, mask); | ||
43 | } | ||
44 | |||
45 | static u64 int_mask_get(struct spu *spu, int class) | ||
46 | { | ||
47 | return _int_mask_get(spu, class); | ||
48 | } | ||
49 | |||
50 | static void int_mask_and(struct spu *spu, int class, u64 mask) | ||
51 | { | ||
52 | u64 old_mask; | ||
53 | old_mask = _int_mask_get(spu, class); | ||
54 | _int_mask_set(spu, class, old_mask & mask); | ||
55 | } | ||
56 | |||
57 | static void int_mask_or(struct spu *spu, int class, u64 mask) | ||
58 | { | ||
59 | u64 old_mask; | ||
60 | old_mask = _int_mask_get(spu, class); | ||
61 | _int_mask_set(spu, class, old_mask | mask); | ||
62 | } | ||
63 | |||
64 | static void int_stat_clear(struct spu *spu, int class, u64 stat) | ||
65 | { | ||
66 | beat_clear_interrupt_status_of_spe(spu->spe_id, class, stat); | ||
67 | } | ||
68 | |||
69 | static u64 int_stat_get(struct spu *spu, int class) | ||
70 | { | ||
71 | u64 int_stat; | ||
72 | beat_get_interrupt_status_of_spe(spu->spe_id, class, &int_stat); | ||
73 | return int_stat; | ||
74 | } | ||
75 | |||
76 | static void cpu_affinity_set(struct spu *spu, int cpu) | ||
77 | { | ||
78 | return; | ||
79 | } | ||
80 | |||
81 | static u64 mfc_dar_get(struct spu *spu) | ||
82 | { | ||
83 | u64 dar; | ||
84 | beat_get_spe_privileged_state_1_registers( | ||
85 | spu->spe_id, | ||
86 | offsetof(struct spu_priv1, mfc_dar_RW), &dar); | ||
87 | return dar; | ||
88 | } | ||
89 | |||
90 | static u64 mfc_dsisr_get(struct spu *spu) | ||
91 | { | ||
92 | u64 dsisr; | ||
93 | beat_get_spe_privileged_state_1_registers( | ||
94 | spu->spe_id, | ||
95 | offsetof(struct spu_priv1, mfc_dsisr_RW), &dsisr); | ||
96 | return dsisr; | ||
97 | } | ||
98 | |||
99 | static void mfc_dsisr_set(struct spu *spu, u64 dsisr) | ||
100 | { | ||
101 | beat_set_spe_privileged_state_1_registers( | ||
102 | spu->spe_id, | ||
103 | offsetof(struct spu_priv1, mfc_dsisr_RW), dsisr); | ||
104 | } | ||
105 | |||
106 | static void mfc_sdr_setup(struct spu *spu) | ||
107 | { | ||
108 | return; | ||
109 | } | ||
110 | |||
111 | static void mfc_sr1_set(struct spu *spu, u64 sr1) | ||
112 | { | ||
113 | beat_set_spe_privileged_state_1_registers( | ||
114 | spu->spe_id, | ||
115 | offsetof(struct spu_priv1, mfc_sr1_RW), sr1); | ||
116 | } | ||
117 | |||
118 | static u64 mfc_sr1_get(struct spu *spu) | ||
119 | { | ||
120 | u64 sr1; | ||
121 | beat_get_spe_privileged_state_1_registers( | ||
122 | spu->spe_id, | ||
123 | offsetof(struct spu_priv1, mfc_sr1_RW), &sr1); | ||
124 | return sr1; | ||
125 | } | ||
126 | |||
127 | static void mfc_tclass_id_set(struct spu *spu, u64 tclass_id) | ||
128 | { | ||
129 | beat_set_spe_privileged_state_1_registers( | ||
130 | spu->spe_id, | ||
131 | offsetof(struct spu_priv1, mfc_tclass_id_RW), tclass_id); | ||
132 | } | ||
133 | |||
134 | static u64 mfc_tclass_id_get(struct spu *spu) | ||
135 | { | ||
136 | u64 tclass_id; | ||
137 | beat_get_spe_privileged_state_1_registers( | ||
138 | spu->spe_id, | ||
139 | offsetof(struct spu_priv1, mfc_tclass_id_RW), &tclass_id); | ||
140 | return tclass_id; | ||
141 | } | ||
142 | |||
143 | static void tlb_invalidate(struct spu *spu) | ||
144 | { | ||
145 | beat_set_spe_privileged_state_1_registers( | ||
146 | spu->spe_id, | ||
147 | offsetof(struct spu_priv1, tlb_invalidate_entry_W), 0ul); | ||
148 | } | ||
149 | |||
150 | static void resource_allocation_groupID_set(struct spu *spu, u64 id) | ||
151 | { | ||
152 | beat_set_spe_privileged_state_1_registers( | ||
153 | spu->spe_id, | ||
154 | offsetof(struct spu_priv1, resource_allocation_groupID_RW), | ||
155 | id); | ||
156 | } | ||
157 | |||
158 | static u64 resource_allocation_groupID_get(struct spu *spu) | ||
159 | { | ||
160 | u64 id; | ||
161 | beat_get_spe_privileged_state_1_registers( | ||
162 | spu->spe_id, | ||
163 | offsetof(struct spu_priv1, resource_allocation_groupID_RW), | ||
164 | &id); | ||
165 | return id; | ||
166 | } | ||
167 | |||
168 | static void resource_allocation_enable_set(struct spu *spu, u64 enable) | ||
169 | { | ||
170 | beat_set_spe_privileged_state_1_registers( | ||
171 | spu->spe_id, | ||
172 | offsetof(struct spu_priv1, resource_allocation_enable_RW), | ||
173 | enable); | ||
174 | } | ||
175 | |||
176 | static u64 resource_allocation_enable_get(struct spu *spu) | ||
177 | { | ||
178 | u64 enable; | ||
179 | beat_get_spe_privileged_state_1_registers( | ||
180 | spu->spe_id, | ||
181 | offsetof(struct spu_priv1, resource_allocation_enable_RW), | ||
182 | &enable); | ||
183 | return enable; | ||
184 | } | ||
185 | |||
186 | const struct spu_priv1_ops spu_priv1_beat_ops = | ||
187 | { | ||
188 | .int_mask_and = int_mask_and, | ||
189 | .int_mask_or = int_mask_or, | ||
190 | .int_mask_set = int_mask_set, | ||
191 | .int_mask_get = int_mask_get, | ||
192 | .int_stat_clear = int_stat_clear, | ||
193 | .int_stat_get = int_stat_get, | ||
194 | .cpu_affinity_set = cpu_affinity_set, | ||
195 | .mfc_dar_get = mfc_dar_get, | ||
196 | .mfc_dsisr_get = mfc_dsisr_get, | ||
197 | .mfc_dsisr_set = mfc_dsisr_set, | ||
198 | .mfc_sdr_setup = mfc_sdr_setup, | ||
199 | .mfc_sr1_set = mfc_sr1_set, | ||
200 | .mfc_sr1_get = mfc_sr1_get, | ||
201 | .mfc_tclass_id_set = mfc_tclass_id_set, | ||
202 | .mfc_tclass_id_get = mfc_tclass_id_get, | ||
203 | .tlb_invalidate = tlb_invalidate, | ||
204 | .resource_allocation_groupID_set = resource_allocation_groupID_set, | ||
205 | .resource_allocation_groupID_get = resource_allocation_groupID_get, | ||
206 | .resource_allocation_enable_set = resource_allocation_enable_set, | ||
207 | .resource_allocation_enable_get = resource_allocation_enable_get, | ||
208 | }; | ||
diff --git a/arch/powerpc/platforms/celleb/udbg_beat.c b/arch/powerpc/platforms/celleb/udbg_beat.c new file mode 100644 index 000000000000..d888c4674c62 --- /dev/null +++ b/arch/powerpc/platforms/celleb/udbg_beat.c | |||
@@ -0,0 +1,97 @@ | |||
1 | /* | ||
2 | * udbg function for Beat | ||
3 | * | ||
4 | * (C) Copyright 2006 TOSHIBA CORPORATION | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License along | ||
17 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
19 | */ | ||
20 | |||
21 | #include <linux/kernel.h> | ||
22 | #include <linux/console.h> | ||
23 | |||
24 | #include <asm/machdep.h> | ||
25 | #include <asm/prom.h> | ||
26 | #include <asm/udbg.h> | ||
27 | |||
28 | #include "beat.h" | ||
29 | |||
30 | #define celleb_vtermno 0 | ||
31 | |||
32 | static void udbg_putc_beat(char c) | ||
33 | { | ||
34 | unsigned long rc; | ||
35 | |||
36 | if (c == '\n') | ||
37 | udbg_putc_beat('\r'); | ||
38 | |||
39 | rc = beat_put_term_char(celleb_vtermno, 1, (uint64_t)c << 56, 0); | ||
40 | } | ||
41 | |||
42 | /* Buffered chars getc */ | ||
43 | static long inbuflen; | ||
44 | static long inbuf[2]; /* must be 2 longs */ | ||
45 | |||
46 | static int udbg_getc_poll_beat(void) | ||
47 | { | ||
48 | /* The interface is tricky because it may return up to 16 chars. | ||
49 | * We save them statically for future calls to udbg_getc(). | ||
50 | */ | ||
51 | char ch, *buf = (char *)inbuf; | ||
52 | int i; | ||
53 | long rc; | ||
54 | if (inbuflen == 0) { | ||
55 | /* get some more chars. */ | ||
56 | inbuflen = 0; | ||
57 | rc = beat_get_term_char(celleb_vtermno, &inbuflen, inbuf+0, inbuf+1); | ||
58 | if (rc != 0) | ||
59 | inbuflen = 0; /* otherwise inbuflen is garbage */ | ||
60 | } | ||
61 | if (inbuflen <= 0 || inbuflen > 16) { | ||
62 | /* Catch error case as well as other oddities (corruption) */ | ||
63 | inbuflen = 0; | ||
64 | return -1; | ||
65 | } | ||
66 | ch = buf[0]; | ||
67 | for (i = 1; i < inbuflen; i++) /* shuffle them down. */ | ||
68 | buf[i-1] = buf[i]; | ||
69 | inbuflen--; | ||
70 | return ch; | ||
71 | } | ||
72 | |||
73 | static int udbg_getc_beat(void) | ||
74 | { | ||
75 | int ch; | ||
76 | for (;;) { | ||
77 | ch = udbg_getc_poll_beat(); | ||
78 | if (ch == -1) { | ||
79 | /* This shouldn't be needed...but... */ | ||
80 | volatile unsigned long delay; | ||
81 | for (delay=0; delay < 2000000; delay++) | ||
82 | ; | ||
83 | } else { | ||
84 | return ch; | ||
85 | } | ||
86 | } | ||
87 | } | ||
88 | |||
89 | /* call this from early_init() for a working debug console on | ||
90 | * vterm capable LPAR machines | ||
91 | */ | ||
92 | void __init udbg_init_debug_beat(void) | ||
93 | { | ||
94 | udbg_putc = udbg_putc_beat; | ||
95 | udbg_getc = udbg_getc_beat; | ||
96 | udbg_getc_poll = udbg_getc_poll_beat; | ||
97 | } | ||
diff --git a/arch/powerpc/platforms/embedded6xx/Kconfig b/arch/powerpc/platforms/embedded6xx/Kconfig index b3c2ce4cb7a8..886c522d78e9 100644 --- a/arch/powerpc/platforms/embedded6xx/Kconfig +++ b/arch/powerpc/platforms/embedded6xx/Kconfig | |||
@@ -104,15 +104,6 @@ config RADSTONE_PPC7D | |||
104 | config PAL4 | 104 | config PAL4 |
105 | bool "SBS-Palomar4" | 105 | bool "SBS-Palomar4" |
106 | 106 | ||
107 | config GEMINI | ||
108 | bool "Synergy-Gemini" | ||
109 | select PPC_INDIRECT_PCI | ||
110 | depends on BROKEN | ||
111 | help | ||
112 | Select Gemini if configuring for a Synergy Microsystems' Gemini | ||
113 | series Single Board Computer. More information is available at: | ||
114 | <http://www.synergymicro.com/PressRel/97_10_15.html>. | ||
115 | |||
116 | config EST8260 | 107 | config EST8260 |
117 | bool "EST8260" | 108 | bool "EST8260" |
118 | ---help--- | 109 | ---help--- |
diff --git a/arch/powerpc/platforms/maple/pci.c b/arch/powerpc/platforms/maple/pci.c index 3f6a69f67195..73c59904697f 100644 --- a/arch/powerpc/platforms/maple/pci.c +++ b/arch/powerpc/platforms/maple/pci.c | |||
@@ -425,14 +425,6 @@ static void __init setup_u4_pcie(struct pci_controller* hose) | |||
425 | hose->cfg_addr = ioremap(0xf0000000 + 0x800000, 0x1000); | 425 | hose->cfg_addr = ioremap(0xf0000000 + 0x800000, 0x1000); |
426 | hose->cfg_data = ioremap(0xf0000000 + 0xc00000, 0x1000); | 426 | hose->cfg_data = ioremap(0xf0000000 + 0xc00000, 0x1000); |
427 | 427 | ||
428 | /* The bus contains a bridge from root -> device, we need to | ||
429 | * make it visible on bus 0 so that we pick the right type | ||
430 | * of config cycles. If we didn't, we would have to force all | ||
431 | * config cycles to be type 1. So we override the "bus-range" | ||
432 | * property here | ||
433 | */ | ||
434 | hose->first_busno = 0x00; | ||
435 | hose->last_busno = 0xff; | ||
436 | u4_pcie = hose; | 428 | u4_pcie = hose; |
437 | } | 429 | } |
438 | 430 | ||
@@ -560,13 +552,16 @@ void __init maple_pci_init(void) | |||
560 | return; | 552 | return; |
561 | } | 553 | } |
562 | for (np = NULL; (np = of_get_next_child(root, np)) != NULL;) { | 554 | for (np = NULL; (np = of_get_next_child(root, np)) != NULL;) { |
563 | if (np->name == NULL) | 555 | if (!np->type) |
564 | continue; | 556 | continue; |
565 | if (!strcmp(np->name, "pci") || !strcmp(np->name, "pcie")) { | 557 | if (strcmp(np->type, "pci") && strcmp(np->type, "ht")) |
566 | if (add_bridge(np) == 0) | 558 | continue; |
567 | of_node_get(np); | 559 | if ((device_is_compatible(np, "u4-pcie") || |
568 | } | 560 | device_is_compatible(np, "u3-agp")) && |
569 | if (strcmp(np->name, "ht") == 0) { | 561 | add_bridge(np) == 0) |
562 | of_node_get(np); | ||
563 | |||
564 | if (device_is_compatible(np, "u3-ht")) { | ||
570 | of_node_get(np); | 565 | of_node_get(np); |
571 | ht = np; | 566 | ht = np; |
572 | } | 567 | } |
diff --git a/arch/powerpc/platforms/maple/setup.c b/arch/powerpc/platforms/maple/setup.c index 50855d4fd5a0..82d3f9e28d7c 100644 --- a/arch/powerpc/platforms/maple/setup.c +++ b/arch/powerpc/platforms/maple/setup.c | |||
@@ -62,6 +62,7 @@ | |||
62 | #include <asm/mpic.h> | 62 | #include <asm/mpic.h> |
63 | #include <asm/rtas.h> | 63 | #include <asm/rtas.h> |
64 | #include <asm/udbg.h> | 64 | #include <asm/udbg.h> |
65 | #include <asm/nvram.h> | ||
65 | 66 | ||
66 | #include "maple.h" | 67 | #include "maple.h" |
67 | 68 | ||
@@ -195,6 +196,8 @@ void __init maple_setup_arch(void) | |||
195 | maple_use_rtas_reboot_and_halt_if_present(); | 196 | maple_use_rtas_reboot_and_halt_if_present(); |
196 | 197 | ||
197 | printk(KERN_DEBUG "Using native/NAP idle loop\n"); | 198 | printk(KERN_DEBUG "Using native/NAP idle loop\n"); |
199 | |||
200 | mmio_nvram_init(); | ||
198 | } | 201 | } |
199 | 202 | ||
200 | /* | 203 | /* |
diff --git a/arch/powerpc/platforms/pasemi/Kconfig b/arch/powerpc/platforms/pasemi/Kconfig new file mode 100644 index 000000000000..68dc529dfd2f --- /dev/null +++ b/arch/powerpc/platforms/pasemi/Kconfig | |||
@@ -0,0 +1,10 @@ | |||
1 | menu "PA Semi PWRficient options" | ||
2 | depends on PPC_PASEMI | ||
3 | |||
4 | config PPC_PASEMI_IOMMU | ||
5 | bool "PA Semi IOMMU support" | ||
6 | depends on PPC_PASEMI | ||
7 | help | ||
8 | IOMMU support for PA6T-1682M | ||
9 | |||
10 | endmenu | ||
diff --git a/arch/powerpc/platforms/pasemi/Makefile b/arch/powerpc/platforms/pasemi/Makefile index 1be1a993c5f5..e657ccae90a9 100644 --- a/arch/powerpc/platforms/pasemi/Makefile +++ b/arch/powerpc/platforms/pasemi/Makefile | |||
@@ -1 +1,2 @@ | |||
1 | obj-y += setup.o pci.o time.o | 1 | obj-y += setup.o pci.o time.o idle.o powersave.o iommu.o |
2 | |||
diff --git a/arch/powerpc/platforms/pasemi/idle.c b/arch/powerpc/platforms/pasemi/idle.c new file mode 100644 index 000000000000..1ca3ff381591 --- /dev/null +++ b/arch/powerpc/platforms/pasemi/idle.c | |||
@@ -0,0 +1,88 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2006-2007 PA Semi, Inc | ||
3 | * | ||
4 | * Maintained by: Olof Johansson <olof@lixom.net> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
18 | * | ||
19 | */ | ||
20 | |||
21 | #undef DEBUG | ||
22 | |||
23 | #include <linux/kernel.h> | ||
24 | #include <linux/string.h> | ||
25 | |||
26 | #include <asm/machdep.h> | ||
27 | #include <asm/reg.h> | ||
28 | |||
29 | #include "pasemi.h" | ||
30 | |||
31 | struct sleep_mode { | ||
32 | char *name; | ||
33 | void (*entry)(void); | ||
34 | }; | ||
35 | |||
36 | static struct sleep_mode modes[] = { | ||
37 | { .name = "spin", .entry = &idle_spin }, | ||
38 | { .name = "doze", .entry = &idle_doze }, | ||
39 | }; | ||
40 | |||
41 | static int current_mode = 0; | ||
42 | |||
43 | static int pasemi_system_reset_exception(struct pt_regs *regs) | ||
44 | { | ||
45 | /* If we were woken up from power savings, we need to return | ||
46 | * to the calling function, since nip is not saved across | ||
47 | * all modes. | ||
48 | */ | ||
49 | |||
50 | if (regs->msr & SRR1_WAKEMASK) | ||
51 | regs->nip = regs->link; | ||
52 | |||
53 | switch (regs->msr & SRR1_WAKEMASK) { | ||
54 | case SRR1_WAKEEE: | ||
55 | do_IRQ(regs); | ||
56 | break; | ||
57 | case SRR1_WAKEDEC: | ||
58 | timer_interrupt(regs); | ||
59 | break; | ||
60 | default: | ||
61 | /* do system reset */ | ||
62 | return 0; | ||
63 | } | ||
64 | /* everything handled */ | ||
65 | regs->msr |= MSR_RI; | ||
66 | return 1; | ||
67 | } | ||
68 | |||
69 | void __init pasemi_idle_init(void) | ||
70 | { | ||
71 | ppc_md.system_reset_exception = pasemi_system_reset_exception; | ||
72 | ppc_md.power_save = modes[current_mode].entry; | ||
73 | printk(KERN_INFO "Using PA6T idle loop (%s)\n", modes[current_mode].name); | ||
74 | } | ||
75 | |||
76 | static int __init idle_param(char *p) | ||
77 | { | ||
78 | int i; | ||
79 | for (i = 0; i < sizeof(modes)/sizeof(struct sleep_mode); i++) { | ||
80 | if (!strcmp(modes[i].name, p)) { | ||
81 | current_mode = i; | ||
82 | break; | ||
83 | } | ||
84 | } | ||
85 | return 0; | ||
86 | } | ||
87 | |||
88 | early_param("idle", idle_param); | ||
diff --git a/arch/powerpc/platforms/pasemi/iommu.c b/arch/powerpc/platforms/pasemi/iommu.c new file mode 100644 index 000000000000..459a53b7d24d --- /dev/null +++ b/arch/powerpc/platforms/pasemi/iommu.c | |||
@@ -0,0 +1,281 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2005-2007, PA Semi, Inc | ||
3 | * | ||
4 | * Maintained by: Olof Johansson <olof@lixom.net> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
18 | */ | ||
19 | |||
20 | #undef DEBUG | ||
21 | |||
22 | #include <linux/types.h> | ||
23 | #include <linux/spinlock.h> | ||
24 | #include <linux/pci.h> | ||
25 | #include <asm/iommu.h> | ||
26 | #include <asm/machdep.h> | ||
27 | #include <asm/abs_addr.h> | ||
28 | |||
29 | |||
30 | #define IOBMAP_PAGE_SHIFT 12 | ||
31 | #define IOBMAP_PAGE_SIZE (1 << IOBMAP_PAGE_SHIFT) | ||
32 | #define IOBMAP_PAGE_MASK (IOBMAP_PAGE_SIZE - 1) | ||
33 | |||
34 | #define IOBMAP_PAGE_FACTOR (PAGE_SHIFT - IOBMAP_PAGE_SHIFT) | ||
35 | |||
36 | #define IOB_BASE 0xe0000000 | ||
37 | #define IOB_SIZE 0x3000 | ||
38 | /* Configuration registers */ | ||
39 | #define IOBCAP_REG 0x10 | ||
40 | #define IOBCOM_REG 0x40 | ||
41 | /* Enable IOB address translation */ | ||
42 | #define IOBCOM_ATEN 0x00000100 | ||
43 | |||
44 | /* Address decode configuration register */ | ||
45 | #define IOB_AD_REG 0x53 | ||
46 | /* IOBCOM_AD_REG fields */ | ||
47 | #define IOB_AD_VGPRT 0x00000e00 | ||
48 | #define IOB_AD_VGAEN 0x00000100 | ||
49 | /* Direct mapping settings */ | ||
50 | #define IOB_AD_MPSEL_MASK 0x00000030 | ||
51 | #define IOB_AD_MPSEL_B38 0x00000000 | ||
52 | #define IOB_AD_MPSEL_B40 0x00000010 | ||
53 | #define IOB_AD_MPSEL_B42 0x00000020 | ||
54 | /* Translation window size / enable */ | ||
55 | #define IOB_AD_TRNG_MASK 0x00000003 | ||
56 | #define IOB_AD_TRNG_256M 0x00000000 | ||
57 | #define IOB_AD_TRNG_2G 0x00000001 | ||
58 | #define IOB_AD_TRNG_128G 0x00000003 | ||
59 | |||
60 | #define IOB_TABLEBASE_REG 0x55 | ||
61 | |||
62 | /* Base of the 64 4-byte L1 registers */ | ||
63 | #define IOB_XLT_L1_REGBASE 0xac0 | ||
64 | |||
65 | /* Register to invalidate TLB entries */ | ||
66 | #define IOB_AT_INVAL_TLB_REG 0xb40 | ||
67 | |||
68 | /* The top two bits of the level 1 entry contains valid and type flags */ | ||
69 | #define IOBMAP_L1E_V 0x40000000 | ||
70 | #define IOBMAP_L1E_V_B 0x80000000 | ||
71 | |||
72 | /* For big page entries, the bottom two bits contains flags */ | ||
73 | #define IOBMAP_L1E_BIG_CACHED 0x00000002 | ||
74 | #define IOBMAP_L1E_BIG_PRIORITY 0x00000001 | ||
75 | |||
76 | /* For regular level 2 entries, top 2 bits contain valid and cache flags */ | ||
77 | #define IOBMAP_L2E_V 0x80000000 | ||
78 | #define IOBMAP_L2E_V_CACHED 0xc0000000 | ||
79 | |||
80 | static u32 *iob; | ||
81 | static u32 iob_l1_emptyval; | ||
82 | static u32 iob_l2_emptyval; | ||
83 | static u32 *iob_l2_base; | ||
84 | |||
85 | static struct iommu_table iommu_table_iobmap; | ||
86 | static int iommu_table_iobmap_inited; | ||
87 | |||
88 | static void iobmap_build(struct iommu_table *tbl, long index, | ||
89 | long npages, unsigned long uaddr, | ||
90 | enum dma_data_direction direction) | ||
91 | { | ||
92 | u32 *ip; | ||
93 | u32 rpn; | ||
94 | unsigned long bus_addr; | ||
95 | |||
96 | pr_debug("iobmap: build at: %lx, %lx, addr: %lx\n", index, npages, uaddr); | ||
97 | |||
98 | bus_addr = (tbl->it_offset + index) << PAGE_SHIFT; | ||
99 | |||
100 | npages <<= IOBMAP_PAGE_FACTOR; | ||
101 | index <<= IOBMAP_PAGE_FACTOR; | ||
102 | |||
103 | ip = ((u32 *)tbl->it_base) + index; | ||
104 | |||
105 | while (npages--) { | ||
106 | rpn = virt_to_abs(uaddr) >> IOBMAP_PAGE_SHIFT; | ||
107 | |||
108 | *(ip++) = IOBMAP_L2E_V | rpn; | ||
109 | /* invalidate tlb, can be optimized more */ | ||
110 | out_le32(iob+IOB_AT_INVAL_TLB_REG, bus_addr >> 14); | ||
111 | |||
112 | uaddr += IOBMAP_PAGE_SIZE; | ||
113 | bus_addr += IOBMAP_PAGE_SIZE; | ||
114 | } | ||
115 | } | ||
116 | |||
117 | |||
118 | static void iobmap_free(struct iommu_table *tbl, long index, | ||
119 | long npages) | ||
120 | { | ||
121 | u32 *ip; | ||
122 | unsigned long bus_addr; | ||
123 | |||
124 | pr_debug("iobmap: free at: %lx, %lx\n", index, npages); | ||
125 | |||
126 | bus_addr = (tbl->it_offset + index) << PAGE_SHIFT; | ||
127 | |||
128 | npages <<= IOBMAP_PAGE_FACTOR; | ||
129 | index <<= IOBMAP_PAGE_FACTOR; | ||
130 | |||
131 | ip = ((u32 *)tbl->it_base) + index; | ||
132 | |||
133 | while (npages--) { | ||
134 | *(ip++) = iob_l2_emptyval; | ||
135 | /* invalidate tlb, can be optimized more */ | ||
136 | out_le32(iob+IOB_AT_INVAL_TLB_REG, bus_addr >> 14); | ||
137 | bus_addr += IOBMAP_PAGE_SIZE; | ||
138 | } | ||
139 | } | ||
140 | |||
141 | |||
142 | static void iommu_table_iobmap_setup(void) | ||
143 | { | ||
144 | pr_debug(" -> %s\n", __func__); | ||
145 | iommu_table_iobmap.it_busno = 0; | ||
146 | iommu_table_iobmap.it_offset = 0; | ||
147 | /* it_size is in number of entries */ | ||
148 | iommu_table_iobmap.it_size = 0x80000000 >> PAGE_SHIFT; | ||
149 | |||
150 | /* Initialize the common IOMMU code */ | ||
151 | iommu_table_iobmap.it_base = (unsigned long)iob_l2_base; | ||
152 | iommu_table_iobmap.it_index = 0; | ||
153 | /* XXXOJN tune this to avoid IOB cache invals. | ||
154 | * Should probably be 8 (64 bytes) | ||
155 | */ | ||
156 | iommu_table_iobmap.it_blocksize = 4; | ||
157 | iommu_init_table(&iommu_table_iobmap, 0); | ||
158 | pr_debug(" <- %s\n", __func__); | ||
159 | } | ||
160 | |||
161 | |||
162 | |||
163 | static void pci_dma_bus_setup_pasemi(struct pci_bus *bus) | ||
164 | { | ||
165 | struct device_node *dn; | ||
166 | |||
167 | pr_debug("pci_dma_bus_setup, bus %p, bus->self %p\n", bus, bus->self); | ||
168 | |||
169 | if (!iommu_table_iobmap_inited) { | ||
170 | iommu_table_iobmap_inited = 1; | ||
171 | iommu_table_iobmap_setup(); | ||
172 | } | ||
173 | |||
174 | dn = pci_bus_to_OF_node(bus); | ||
175 | |||
176 | if (dn) | ||
177 | PCI_DN(dn)->iommu_table = &iommu_table_iobmap; | ||
178 | |||
179 | } | ||
180 | |||
181 | |||
182 | static void pci_dma_dev_setup_pasemi(struct pci_dev *dev) | ||
183 | { | ||
184 | pr_debug("pci_dma_dev_setup, dev %p (%s)\n", dev, pci_name(dev)); | ||
185 | |||
186 | /* DMA device is untranslated, but all other PCI-e goes through | ||
187 | * the IOMMU | ||
188 | */ | ||
189 | if (dev->vendor == 0x1959 && dev->device == 0xa007) | ||
190 | dev->dev.archdata.dma_ops = &dma_direct_ops; | ||
191 | else | ||
192 | dev->dev.archdata.dma_data = &iommu_table_iobmap; | ||
193 | } | ||
194 | |||
195 | static void pci_dma_bus_setup_null(struct pci_bus *b) { } | ||
196 | static void pci_dma_dev_setup_null(struct pci_dev *d) { } | ||
197 | |||
198 | int iob_init(struct device_node *dn) | ||
199 | { | ||
200 | unsigned long tmp; | ||
201 | u32 regword; | ||
202 | int i; | ||
203 | |||
204 | pr_debug(" -> %s\n", __func__); | ||
205 | |||
206 | /* Allocate a spare page to map all invalid IOTLB pages. */ | ||
207 | tmp = lmb_alloc(IOBMAP_PAGE_SIZE, IOBMAP_PAGE_SIZE); | ||
208 | if (!tmp) | ||
209 | panic("IOBMAP: Cannot allocate spare page!"); | ||
210 | /* Empty l1 is marked invalid */ | ||
211 | iob_l1_emptyval = 0; | ||
212 | /* Empty l2 is mapped to dummy page */ | ||
213 | iob_l2_emptyval = IOBMAP_L2E_V | (tmp >> IOBMAP_PAGE_SHIFT); | ||
214 | |||
215 | iob = ioremap(IOB_BASE, IOB_SIZE); | ||
216 | if (!iob) | ||
217 | panic("IOBMAP: Cannot map registers!"); | ||
218 | |||
219 | /* setup direct mapping of the L1 entries */ | ||
220 | for (i = 0; i < 64; i++) { | ||
221 | /* Each L1 covers 32MB, i.e. 8K entries = 32K of ram */ | ||
222 | regword = IOBMAP_L1E_V | (__pa(iob_l2_base + i*0x2000) >> 12); | ||
223 | out_le32(iob+IOB_XLT_L1_REGBASE+i, regword); | ||
224 | } | ||
225 | |||
226 | /* set 2GB translation window, based at 0 */ | ||
227 | regword = in_le32(iob+IOB_AD_REG); | ||
228 | regword &= ~IOB_AD_TRNG_MASK; | ||
229 | regword |= IOB_AD_TRNG_2G; | ||
230 | out_le32(iob+IOB_AD_REG, regword); | ||
231 | |||
232 | /* Enable translation */ | ||
233 | regword = in_le32(iob+IOBCOM_REG); | ||
234 | regword |= IOBCOM_ATEN; | ||
235 | out_le32(iob+IOBCOM_REG, regword); | ||
236 | |||
237 | pr_debug(" <- %s\n", __func__); | ||
238 | |||
239 | return 0; | ||
240 | } | ||
241 | |||
242 | |||
243 | /* These are called very early. */ | ||
244 | void iommu_init_early_pasemi(void) | ||
245 | { | ||
246 | int iommu_off; | ||
247 | |||
248 | #ifndef CONFIG_PPC_PASEMI_IOMMU | ||
249 | iommu_off = 1; | ||
250 | #else | ||
251 | iommu_off = of_chosen && | ||
252 | get_property(of_chosen, "linux,iommu-off", NULL); | ||
253 | #endif | ||
254 | if (iommu_off) { | ||
255 | /* Direct I/O, IOMMU off */ | ||
256 | ppc_md.pci_dma_dev_setup = pci_dma_dev_setup_null; | ||
257 | ppc_md.pci_dma_bus_setup = pci_dma_bus_setup_null; | ||
258 | pci_dma_ops = &dma_direct_ops; | ||
259 | |||
260 | return; | ||
261 | } | ||
262 | |||
263 | iob_init(NULL); | ||
264 | |||
265 | ppc_md.pci_dma_dev_setup = pci_dma_dev_setup_pasemi; | ||
266 | ppc_md.pci_dma_bus_setup = pci_dma_bus_setup_pasemi; | ||
267 | ppc_md.tce_build = iobmap_build; | ||
268 | ppc_md.tce_free = iobmap_free; | ||
269 | pci_dma_ops = &dma_iommu_ops; | ||
270 | } | ||
271 | |||
272 | void __init alloc_iobmap_l2(void) | ||
273 | { | ||
274 | #ifndef CONFIG_PPC_PASEMI_IOMMU | ||
275 | return; | ||
276 | #endif | ||
277 | /* For 2G space, 8x64 pages (2^21 bytes) is max total l2 size */ | ||
278 | iob_l2_base = (u32 *)abs_to_virt(lmb_alloc_base(1UL<<21, 1UL<<21, 0x80000000)); | ||
279 | |||
280 | printk(KERN_INFO "IOBMAP L2 allocated at: %p\n", iob_l2_base); | ||
281 | } | ||
diff --git a/arch/powerpc/platforms/pasemi/pasemi.h b/arch/powerpc/platforms/pasemi/pasemi.h index 51c2a2397ecf..2d3927e6edb0 100644 --- a/arch/powerpc/platforms/pasemi/pasemi.h +++ b/arch/powerpc/platforms/pasemi/pasemi.h | |||
@@ -3,5 +3,17 @@ | |||
3 | 3 | ||
4 | extern unsigned long pas_get_boot_time(void); | 4 | extern unsigned long pas_get_boot_time(void); |
5 | extern void pas_pci_init(void); | 5 | extern void pas_pci_init(void); |
6 | extern void __devinit pas_pci_irq_fixup(struct pci_dev *dev); | ||
7 | extern void __devinit pas_pci_dma_dev_setup(struct pci_dev *dev); | ||
8 | |||
9 | extern void __init alloc_iobmap_l2(void); | ||
10 | |||
11 | extern void __init pasemi_idle_init(void); | ||
12 | |||
13 | /* Power savings modes, implemented in asm */ | ||
14 | extern void idle_spin(void); | ||
15 | extern void idle_doze(void); | ||
16 | |||
17 | |||
6 | 18 | ||
7 | #endif /* _PASEMI_PASEMI_H */ | 19 | #endif /* _PASEMI_PASEMI_H */ |
diff --git a/arch/powerpc/platforms/pasemi/pci.c b/arch/powerpc/platforms/pasemi/pci.c index faa618e04047..7ecb2ba24db9 100644 --- a/arch/powerpc/platforms/pasemi/pci.c +++ b/arch/powerpc/platforms/pasemi/pci.c | |||
@@ -163,6 +163,19 @@ static void __init pas_fixup_phb_resources(void) | |||
163 | } | 163 | } |
164 | 164 | ||
165 | 165 | ||
166 | void __devinit pas_pci_irq_fixup(struct pci_dev *dev) | ||
167 | { | ||
168 | /* DMA is special, 84 interrupts (128 -> 211), all but 128 | ||
169 | * need to be mapped by hand here. | ||
170 | */ | ||
171 | if (dev->vendor == 0x1959 && dev->device == 0xa007) { | ||
172 | int i; | ||
173 | for (i = 129; i < 212; i++) | ||
174 | irq_create_mapping(NULL, i); | ||
175 | } | ||
176 | } | ||
177 | |||
178 | |||
166 | void __init pas_pci_init(void) | 179 | void __init pas_pci_init(void) |
167 | { | 180 | { |
168 | struct device_node *np, *root; | 181 | struct device_node *np, *root; |
diff --git a/arch/powerpc/platforms/pasemi/powersave.S b/arch/powerpc/platforms/pasemi/powersave.S new file mode 100644 index 000000000000..6d0fba6aab17 --- /dev/null +++ b/arch/powerpc/platforms/pasemi/powersave.S | |||
@@ -0,0 +1,80 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2006-2007 PA Semi, Inc | ||
3 | * | ||
4 | * Maintained by: Olof Johansson <olof@lixom.net> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
18 | * | ||
19 | */ | ||
20 | |||
21 | #include <asm/processor.h> | ||
22 | #include <asm/page.h> | ||
23 | #include <asm/ppc_asm.h> | ||
24 | #include <asm/cputable.h> | ||
25 | #include <asm/cache.h> | ||
26 | #include <asm/thread_info.h> | ||
27 | #include <asm/asm-offsets.h> | ||
28 | |||
29 | /* Power savings opcodes since not all binutils have them at this time */ | ||
30 | #define DOZE .long 0x4c000324 | ||
31 | #define NAP .long 0x4c000364 | ||
32 | #define SLEEP .long 0x4c0003a4 | ||
33 | #define RVW .long 0x4c0003e4 | ||
34 | |||
35 | /* Common sequence to do before going to any of the | ||
36 | * powersavings modes. | ||
37 | */ | ||
38 | |||
39 | #define PRE_SLEEP_SEQUENCE \ | ||
40 | std r3,8(r1); \ | ||
41 | ptesync ; \ | ||
42 | ld r3,8(r1); \ | ||
43 | 1: cmpd r3,r3; \ | ||
44 | bne 1b | ||
45 | |||
46 | _doze: | ||
47 | PRE_SLEEP_SEQUENCE | ||
48 | DOZE | ||
49 | b . | ||
50 | |||
51 | |||
52 | _GLOBAL(idle_spin) | ||
53 | blr | ||
54 | |||
55 | _GLOBAL(idle_doze) | ||
56 | LOAD_REG_ADDR(r3, _doze) | ||
57 | b sleep_common | ||
58 | |||
59 | /* Add more modes here later */ | ||
60 | |||
61 | sleep_common: | ||
62 | mflr r0 | ||
63 | std r0, 16(r1) | ||
64 | stdu r1,-64(r1) | ||
65 | |||
66 | LOAD_REG_IMMEDIATE(r6,MSR_DR|MSR_IR|MSR_ME|MSR_EE) | ||
67 | mfmsr r4 | ||
68 | andc r5,r4,r6 | ||
69 | mtmsrd r5,0 | ||
70 | |||
71 | mtctr r3 | ||
72 | bctrl | ||
73 | |||
74 | mtmsrd r4,0 | ||
75 | |||
76 | addi r1,r1,64 | ||
77 | ld r0,16(r1) | ||
78 | mtlr r0 | ||
79 | blr | ||
80 | |||
diff --git a/arch/powerpc/platforms/pasemi/setup.c b/arch/powerpc/platforms/pasemi/setup.c index bea7d1bb1a3b..449cf1a08291 100644 --- a/arch/powerpc/platforms/pasemi/setup.c +++ b/arch/powerpc/platforms/pasemi/setup.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (C) 2006 PA Semi, Inc | 2 | * Copyright (C) 2006-2007 PA Semi, Inc |
3 | * | 3 | * |
4 | * Authors: Kip Walker, PA Semi | 4 | * Authors: Kip Walker, PA Semi |
5 | * Olof Johansson, PA Semi | 5 | * Olof Johansson, PA Semi |
@@ -38,31 +38,46 @@ | |||
38 | 38 | ||
39 | #include "pasemi.h" | 39 | #include "pasemi.h" |
40 | 40 | ||
41 | static void __iomem *reset_reg; | ||
42 | |||
41 | static void pas_restart(char *cmd) | 43 | static void pas_restart(char *cmd) |
42 | { | 44 | { |
43 | printk("restart unimplemented, looping...\n"); | 45 | printk("Restarting...\n"); |
44 | for (;;) ; | 46 | while (1) |
47 | out_le32(reset_reg, 0x6000000); | ||
45 | } | 48 | } |
46 | 49 | ||
47 | static void pas_power_off(void) | 50 | #ifdef CONFIG_SMP |
51 | static DEFINE_SPINLOCK(timebase_lock); | ||
52 | |||
53 | static void __devinit pas_give_timebase(void) | ||
48 | { | 54 | { |
49 | printk("power off unimplemented, looping...\n"); | 55 | unsigned long tb; |
50 | for (;;) ; | 56 | |
57 | spin_lock(&timebase_lock); | ||
58 | mtspr(SPRN_TBCTL, TBCTL_FREEZE); | ||
59 | tb = mftb(); | ||
60 | mtspr(SPRN_TBCTL, TBCTL_UPDATE_LOWER | (tb & 0xffffffff)); | ||
61 | mtspr(SPRN_TBCTL, TBCTL_UPDATE_UPPER | (tb >> 32)); | ||
62 | mtspr(SPRN_TBCTL, TBCTL_RESTART); | ||
63 | spin_unlock(&timebase_lock); | ||
64 | pr_debug("pas_give_timebase: cpu %d gave tb %lx\n", | ||
65 | smp_processor_id(), tb); | ||
51 | } | 66 | } |
52 | 67 | ||
53 | static void pas_halt(void) | 68 | static void __devinit pas_take_timebase(void) |
54 | { | 69 | { |
55 | pas_power_off(); | 70 | pr_debug("pas_take_timebase: cpu %d has tb %lx\n", |
71 | smp_processor_id(), mftb()); | ||
56 | } | 72 | } |
57 | 73 | ||
58 | #ifdef CONFIG_SMP | ||
59 | struct smp_ops_t pas_smp_ops = { | 74 | struct smp_ops_t pas_smp_ops = { |
60 | .probe = smp_mpic_probe, | 75 | .probe = smp_mpic_probe, |
61 | .message_pass = smp_mpic_message_pass, | 76 | .message_pass = smp_mpic_message_pass, |
62 | .kick_cpu = smp_generic_kick_cpu, | 77 | .kick_cpu = smp_generic_kick_cpu, |
63 | .setup_cpu = smp_mpic_setup_cpu, | 78 | .setup_cpu = smp_mpic_setup_cpu, |
64 | .give_timebase = smp_generic_give_timebase, | 79 | .give_timebase = pas_give_timebase, |
65 | .take_timebase = smp_generic_take_timebase, | 80 | .take_timebase = pas_take_timebase, |
66 | }; | 81 | }; |
67 | #endif /* CONFIG_SMP */ | 82 | #endif /* CONFIG_SMP */ |
68 | 83 | ||
@@ -72,9 +87,6 @@ void __init pas_setup_arch(void) | |||
72 | /* Setup SMP callback */ | 87 | /* Setup SMP callback */ |
73 | smp_ops = &pas_smp_ops; | 88 | smp_ops = &pas_smp_ops; |
74 | #endif | 89 | #endif |
75 | /* no iommu yet */ | ||
76 | pci_dma_ops = &dma_direct_ops; | ||
77 | |||
78 | /* Lookup PCI hosts */ | 90 | /* Lookup PCI hosts */ |
79 | pas_pci_init(); | 91 | pas_pci_init(); |
80 | 92 | ||
@@ -82,7 +94,11 @@ void __init pas_setup_arch(void) | |||
82 | conswitchp = &dummy_con; | 94 | conswitchp = &dummy_con; |
83 | #endif | 95 | #endif |
84 | 96 | ||
85 | printk(KERN_DEBUG "Using default idle loop\n"); | 97 | /* Remap SDC register for doing reset */ |
98 | /* XXXOJN This should maybe come out of the device tree */ | ||
99 | reset_reg = ioremap(0xfc101100, 4); | ||
100 | |||
101 | pasemi_idle_init(); | ||
86 | } | 102 | } |
87 | 103 | ||
88 | /* No legacy IO on our parts */ | 104 | /* No legacy IO on our parts */ |
@@ -130,8 +146,9 @@ static __init void pas_init_IRQ(void) | |||
130 | openpic_addr = of_read_number(opprop, naddr); | 146 | openpic_addr = of_read_number(opprop, naddr); |
131 | printk(KERN_DEBUG "OpenPIC addr: %lx\n", openpic_addr); | 147 | printk(KERN_DEBUG "OpenPIC addr: %lx\n", openpic_addr); |
132 | 148 | ||
133 | mpic = mpic_alloc(mpic_node, openpic_addr, MPIC_PRIMARY, 0, 0, | 149 | mpic = mpic_alloc(mpic_node, openpic_addr, |
134 | " PAS-OPIC "); | 150 | MPIC_PRIMARY|MPIC_LARGE_VECTORS, |
151 | 0, 0, " PAS-OPIC "); | ||
135 | BUG_ON(!mpic); | 152 | BUG_ON(!mpic); |
136 | 153 | ||
137 | mpic_assign_isu(mpic, 0, openpic_addr + 0x10000); | 154 | mpic_assign_isu(mpic, 0, openpic_addr + 0x10000); |
@@ -146,6 +163,53 @@ static void __init pas_progress(char *s, unsigned short hex) | |||
146 | } | 163 | } |
147 | 164 | ||
148 | 165 | ||
166 | static int pas_machine_check_handler(struct pt_regs *regs) | ||
167 | { | ||
168 | int cpu = smp_processor_id(); | ||
169 | unsigned long srr0, srr1, dsisr; | ||
170 | |||
171 | srr0 = regs->nip; | ||
172 | srr1 = regs->msr; | ||
173 | dsisr = mfspr(SPRN_DSISR); | ||
174 | printk(KERN_ERR "Machine Check on CPU %d\n", cpu); | ||
175 | printk(KERN_ERR "SRR0 0x%016lx SRR1 0x%016lx\n", srr0, srr1); | ||
176 | printk(KERN_ERR "DSISR 0x%016lx DAR 0x%016lx\n", dsisr, regs->dar); | ||
177 | printk(KERN_ERR "Cause:\n"); | ||
178 | |||
179 | if (srr1 & 0x200000) | ||
180 | printk(KERN_ERR "Signalled by SDC\n"); | ||
181 | if (srr1 & 0x100000) { | ||
182 | printk(KERN_ERR "Load/Store detected error:\n"); | ||
183 | if (dsisr & 0x8000) | ||
184 | printk(KERN_ERR "D-cache ECC double-bit error or bus error\n"); | ||
185 | if (dsisr & 0x4000) | ||
186 | printk(KERN_ERR "LSU snoop response error\n"); | ||
187 | if (dsisr & 0x2000) | ||
188 | printk(KERN_ERR "MMU SLB multi-hit or invalid B field\n"); | ||
189 | if (dsisr & 0x1000) | ||
190 | printk(KERN_ERR "Recoverable Duptags\n"); | ||
191 | if (dsisr & 0x800) | ||
192 | printk(KERN_ERR "Recoverable D-cache parity error count overflow\n"); | ||
193 | if (dsisr & 0x400) | ||
194 | printk(KERN_ERR "TLB parity error count overflow\n"); | ||
195 | } | ||
196 | if (srr1 & 0x80000) | ||
197 | printk(KERN_ERR "Bus Error\n"); | ||
198 | if (srr1 & 0x40000) | ||
199 | printk(KERN_ERR "I-side SLB multiple hit\n"); | ||
200 | if (srr1 & 0x20000) | ||
201 | printk(KERN_ERR "I-cache parity error hit\n"); | ||
202 | |||
203 | /* SRR1[62] is from MSR[62] if recoverable, so pass that back */ | ||
204 | return !!(srr1 & 0x2); | ||
205 | } | ||
206 | |||
207 | static void __init pas_init_early(void) | ||
208 | { | ||
209 | iommu_init_early_pasemi(); | ||
210 | } | ||
211 | |||
212 | |||
149 | /* | 213 | /* |
150 | * Called very early, MMU is off, device-tree isn't unflattened | 214 | * Called very early, MMU is off, device-tree isn't unflattened |
151 | */ | 215 | */ |
@@ -158,6 +222,8 @@ static int __init pas_probe(void) | |||
158 | 222 | ||
159 | hpte_init_native(); | 223 | hpte_init_native(); |
160 | 224 | ||
225 | alloc_iobmap_l2(); | ||
226 | |||
161 | return 1; | 227 | return 1; |
162 | } | 228 | } |
163 | 229 | ||
@@ -165,13 +231,14 @@ define_machine(pas) { | |||
165 | .name = "PA Semi PA6T-1682M", | 231 | .name = "PA Semi PA6T-1682M", |
166 | .probe = pas_probe, | 232 | .probe = pas_probe, |
167 | .setup_arch = pas_setup_arch, | 233 | .setup_arch = pas_setup_arch, |
234 | .init_early = pas_init_early, | ||
168 | .init_IRQ = pas_init_IRQ, | 235 | .init_IRQ = pas_init_IRQ, |
169 | .get_irq = mpic_get_irq, | 236 | .get_irq = mpic_get_irq, |
170 | .restart = pas_restart, | 237 | .restart = pas_restart, |
171 | .power_off = pas_power_off, | ||
172 | .halt = pas_halt, | ||
173 | .get_boot_time = pas_get_boot_time, | 238 | .get_boot_time = pas_get_boot_time, |
174 | .calibrate_decr = generic_calibrate_decr, | 239 | .calibrate_decr = generic_calibrate_decr, |
175 | .check_legacy_ioport = pas_check_legacy_ioport, | 240 | .check_legacy_ioport = pas_check_legacy_ioport, |
176 | .progress = pas_progress, | 241 | .progress = pas_progress, |
242 | .machine_check_exception = pas_machine_check_handler, | ||
243 | .pci_irq_fixup = pas_pci_irq_fixup, | ||
177 | }; | 244 | }; |
diff --git a/arch/powerpc/platforms/powermac/smp.c b/arch/powerpc/platforms/powermac/smp.c index eeb2ae5ffc58..d73fb73802bb 100644 --- a/arch/powerpc/platforms/powermac/smp.c +++ b/arch/powerpc/platforms/powermac/smp.c | |||
@@ -795,7 +795,6 @@ static void __devinit smp_core99_kick_cpu(int nr) | |||
795 | ppc_md.progress("smp_core99_kick_cpu", 0x346); | 795 | ppc_md.progress("smp_core99_kick_cpu", 0x346); |
796 | 796 | ||
797 | local_irq_save(flags); | 797 | local_irq_save(flags); |
798 | local_irq_disable(); | ||
799 | 798 | ||
800 | /* Save reset vector */ | 799 | /* Save reset vector */ |
801 | save_vector = *vector; | 800 | save_vector = *vector; |
diff --git a/arch/powerpc/platforms/ps3/Makefile b/arch/powerpc/platforms/ps3/Makefile index 1994904f580f..a0048fcf0866 100644 --- a/arch/powerpc/platforms/ps3/Makefile +++ b/arch/powerpc/platforms/ps3/Makefile | |||
@@ -1,5 +1,6 @@ | |||
1 | obj-y += setup.o mm.o time.o hvcall.o htab.o repository.o | 1 | obj-y += setup.o mm.o time.o hvcall.o htab.o repository.o |
2 | obj-y += interrupt.o exports.o os-area.o | 2 | obj-y += interrupt.o exports.o os-area.o |
3 | obj-y += system-bus.o | ||
3 | 4 | ||
4 | obj-$(CONFIG_SMP) += smp.o | 5 | obj-$(CONFIG_SMP) += smp.o |
5 | obj-$(CONFIG_SPU_BASE) += spu.o | 6 | obj-$(CONFIG_SPU_BASE) += spu.o |
diff --git a/arch/powerpc/platforms/ps3/htab.c b/arch/powerpc/platforms/ps3/htab.c index 8fe1769655a3..a4b5a1bc60f4 100644 --- a/arch/powerpc/platforms/ps3/htab.c +++ b/arch/powerpc/platforms/ps3/htab.c | |||
@@ -23,7 +23,6 @@ | |||
23 | #include <asm/machdep.h> | 23 | #include <asm/machdep.h> |
24 | #include <asm/lmb.h> | 24 | #include <asm/lmb.h> |
25 | #include <asm/udbg.h> | 25 | #include <asm/udbg.h> |
26 | #include <asm/ps3.h> | ||
27 | #include <asm/lv1call.h> | 26 | #include <asm/lv1call.h> |
28 | 27 | ||
29 | #include "platform.h" | 28 | #include "platform.h" |
diff --git a/arch/powerpc/platforms/ps3/interrupt.c b/arch/powerpc/platforms/ps3/interrupt.c index 6f5de438b980..bb17283275aa 100644 --- a/arch/powerpc/platforms/ps3/interrupt.c +++ b/arch/powerpc/platforms/ps3/interrupt.c | |||
@@ -24,7 +24,6 @@ | |||
24 | 24 | ||
25 | #include <asm/machdep.h> | 25 | #include <asm/machdep.h> |
26 | #include <asm/udbg.h> | 26 | #include <asm/udbg.h> |
27 | #include <asm/ps3.h> | ||
28 | #include <asm/lv1call.h> | 27 | #include <asm/lv1call.h> |
29 | 28 | ||
30 | #include "platform.h" | 29 | #include "platform.h" |
@@ -36,15 +35,148 @@ | |||
36 | #endif | 35 | #endif |
37 | 36 | ||
38 | /** | 37 | /** |
38 | * struct ps3_bmp - a per cpu irq status and mask bitmap structure | ||
39 | * @status: 256 bit status bitmap indexed by plug | ||
40 | * @unused_1: | ||
41 | * @mask: 256 bit mask bitmap indexed by plug | ||
42 | * @unused_2: | ||
43 | * @lock: | ||
44 | * @ipi_debug_brk_mask: | ||
45 | * | ||
46 | * The HV mantains per SMT thread mappings of HV outlet to HV plug on | ||
47 | * behalf of the guest. These mappings are implemented as 256 bit guest | ||
48 | * supplied bitmaps indexed by plug number. The addresses of the bitmaps | ||
49 | * are registered with the HV through lv1_configure_irq_state_bitmap(). | ||
50 | * The HV requires that the 512 bits of status + mask not cross a page | ||
51 | * boundary. PS3_BMP_MINALIGN is used to define this minimal 64 byte | ||
52 | * alignment. | ||
53 | * | ||
54 | * The HV supports 256 plugs per thread, assigned as {0..255}, for a total | ||
55 | * of 512 plugs supported on a processor. To simplify the logic this | ||
56 | * implementation equates HV plug value to Linux virq value, constrains each | ||
57 | * interrupt to have a system wide unique plug number, and limits the range | ||
58 | * of the plug values to map into the first dword of the bitmaps. This | ||
59 | * gives a usable range of plug values of {NUM_ISA_INTERRUPTS..63}. Note | ||
60 | * that there is no constraint on how many in this set an individual thread | ||
61 | * can acquire. | ||
62 | */ | ||
63 | |||
64 | #define PS3_BMP_MINALIGN 64 | ||
65 | |||
66 | struct ps3_bmp { | ||
67 | struct { | ||
68 | u64 status; | ||
69 | u64 unused_1[3]; | ||
70 | u64 mask; | ||
71 | u64 unused_2[3]; | ||
72 | }; | ||
73 | u64 ipi_debug_brk_mask; | ||
74 | spinlock_t lock; | ||
75 | }; | ||
76 | |||
77 | /** | ||
78 | * struct ps3_private - a per cpu data structure | ||
79 | * @bmp: ps3_bmp structure | ||
80 | * @node: HV logical_ppe_id | ||
81 | * @cpu: HV thread_id | ||
82 | */ | ||
83 | |||
84 | struct ps3_private { | ||
85 | struct ps3_bmp bmp __attribute__ ((aligned (PS3_BMP_MINALIGN))); | ||
86 | u64 node; | ||
87 | unsigned int cpu; | ||
88 | }; | ||
89 | |||
90 | static DEFINE_PER_CPU(struct ps3_private, ps3_private); | ||
91 | |||
92 | int ps3_alloc_irq(enum ps3_cpu_binding cpu, unsigned long outlet, | ||
93 | unsigned int *virq) | ||
94 | { | ||
95 | int result; | ||
96 | struct ps3_private *pd; | ||
97 | |||
98 | /* This defines the default interrupt distribution policy. */ | ||
99 | |||
100 | if (cpu == PS3_BINDING_CPU_ANY) | ||
101 | cpu = 0; | ||
102 | |||
103 | pd = &per_cpu(ps3_private, cpu); | ||
104 | |||
105 | *virq = irq_create_mapping(NULL, outlet); | ||
106 | |||
107 | if (*virq == NO_IRQ) { | ||
108 | pr_debug("%s:%d: irq_create_mapping failed: outlet %lu\n", | ||
109 | __func__, __LINE__, outlet); | ||
110 | result = -ENOMEM; | ||
111 | goto fail_create; | ||
112 | } | ||
113 | |||
114 | /* Binds outlet to cpu + virq. */ | ||
115 | |||
116 | result = lv1_connect_irq_plug_ext(pd->node, pd->cpu, *virq, outlet, 0); | ||
117 | |||
118 | if (result) { | ||
119 | pr_info("%s:%d: lv1_connect_irq_plug_ext failed: %s\n", | ||
120 | __func__, __LINE__, ps3_result(result)); | ||
121 | result = -EPERM; | ||
122 | goto fail_connect; | ||
123 | } | ||
124 | |||
125 | pr_debug("%s:%d: outlet %lu => cpu %u, virq %u\n", __func__, __LINE__, | ||
126 | outlet, cpu, *virq); | ||
127 | |||
128 | result = set_irq_chip_data(*virq, pd); | ||
129 | |||
130 | if (result) { | ||
131 | pr_debug("%s:%d: set_irq_chip_data failed\n", | ||
132 | __func__, __LINE__); | ||
133 | goto fail_set; | ||
134 | } | ||
135 | |||
136 | return result; | ||
137 | |||
138 | fail_set: | ||
139 | lv1_disconnect_irq_plug_ext(pd->node, pd->cpu, *virq); | ||
140 | fail_connect: | ||
141 | irq_dispose_mapping(*virq); | ||
142 | fail_create: | ||
143 | return result; | ||
144 | } | ||
145 | EXPORT_SYMBOL_GPL(ps3_alloc_irq); | ||
146 | |||
147 | int ps3_free_irq(unsigned int virq) | ||
148 | { | ||
149 | int result; | ||
150 | const struct ps3_private *pd = get_irq_chip_data(virq); | ||
151 | |||
152 | pr_debug("%s:%d: node %lu, cpu %d, virq %u\n", __func__, __LINE__, | ||
153 | pd->node, pd->cpu, virq); | ||
154 | |||
155 | result = lv1_disconnect_irq_plug_ext(pd->node, pd->cpu, virq); | ||
156 | |||
157 | if (result) | ||
158 | pr_info("%s:%d: lv1_disconnect_irq_plug_ext failed: %s\n", | ||
159 | __func__, __LINE__, ps3_result(result)); | ||
160 | |||
161 | set_irq_chip_data(virq, NULL); | ||
162 | irq_dispose_mapping(virq); | ||
163 | return result; | ||
164 | } | ||
165 | EXPORT_SYMBOL_GPL(ps3_free_irq); | ||
166 | |||
167 | /** | ||
39 | * ps3_alloc_io_irq - Assign a virq to a system bus device. | 168 | * ps3_alloc_io_irq - Assign a virq to a system bus device. |
40 | * interrupt_id: The device interrupt id read from the system repository. | 169 | * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be |
170 | * serviced on. | ||
171 | * @interrupt_id: The device interrupt id read from the system repository. | ||
41 | * @virq: The assigned Linux virq. | 172 | * @virq: The assigned Linux virq. |
42 | * | 173 | * |
43 | * An io irq represents a non-virtualized device interrupt. interrupt_id | 174 | * An io irq represents a non-virtualized device interrupt. interrupt_id |
44 | * coresponds to the interrupt number of the interrupt controller. | 175 | * coresponds to the interrupt number of the interrupt controller. |
45 | */ | 176 | */ |
46 | 177 | ||
47 | int ps3_alloc_io_irq(unsigned int interrupt_id, unsigned int *virq) | 178 | int ps3_alloc_io_irq(enum ps3_cpu_binding cpu, unsigned int interrupt_id, |
179 | unsigned int *virq) | ||
48 | { | 180 | { |
49 | int result; | 181 | int result; |
50 | unsigned long outlet; | 182 | unsigned long outlet; |
@@ -57,12 +189,10 @@ int ps3_alloc_io_irq(unsigned int interrupt_id, unsigned int *virq) | |||
57 | return result; | 189 | return result; |
58 | } | 190 | } |
59 | 191 | ||
60 | *virq = irq_create_mapping(NULL, outlet); | 192 | result = ps3_alloc_irq(cpu, outlet, virq); |
61 | 193 | BUG_ON(result); | |
62 | pr_debug("%s:%d: interrupt_id %u => outlet %lu, virq %u\n", | ||
63 | __func__, __LINE__, interrupt_id, outlet, *virq); | ||
64 | 194 | ||
65 | return 0; | 195 | return result; |
66 | } | 196 | } |
67 | 197 | ||
68 | int ps3_free_io_irq(unsigned int virq) | 198 | int ps3_free_io_irq(unsigned int virq) |
@@ -75,13 +205,15 @@ int ps3_free_io_irq(unsigned int virq) | |||
75 | pr_debug("%s:%d: lv1_destruct_io_irq_outlet failed: %s\n", | 205 | pr_debug("%s:%d: lv1_destruct_io_irq_outlet failed: %s\n", |
76 | __func__, __LINE__, ps3_result(result)); | 206 | __func__, __LINE__, ps3_result(result)); |
77 | 207 | ||
78 | irq_dispose_mapping(virq); | 208 | ps3_free_irq(virq); |
79 | 209 | ||
80 | return result; | 210 | return result; |
81 | } | 211 | } |
82 | 212 | ||
83 | /** | 213 | /** |
84 | * ps3_alloc_event_irq - Allocate a virq for use with a system event. | 214 | * ps3_alloc_event_irq - Allocate a virq for use with a system event. |
215 | * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be | ||
216 | * serviced on. | ||
85 | * @virq: The assigned Linux virq. | 217 | * @virq: The assigned Linux virq. |
86 | * | 218 | * |
87 | * The virq can be used with lv1_connect_interrupt_event_receive_port() to | 219 | * The virq can be used with lv1_connect_interrupt_event_receive_port() to |
@@ -89,7 +221,7 @@ int ps3_free_io_irq(unsigned int virq) | |||
89 | * events. | 221 | * events. |
90 | */ | 222 | */ |
91 | 223 | ||
92 | int ps3_alloc_event_irq(unsigned int *virq) | 224 | int ps3_alloc_event_irq(enum ps3_cpu_binding cpu, unsigned int *virq) |
93 | { | 225 | { |
94 | int result; | 226 | int result; |
95 | unsigned long outlet; | 227 | unsigned long outlet; |
@@ -103,12 +235,10 @@ int ps3_alloc_event_irq(unsigned int *virq) | |||
103 | return result; | 235 | return result; |
104 | } | 236 | } |
105 | 237 | ||
106 | *virq = irq_create_mapping(NULL, outlet); | 238 | result = ps3_alloc_irq(cpu, outlet, virq); |
107 | 239 | BUG_ON(result); | |
108 | pr_debug("%s:%d: outlet %lu, virq %u\n", __func__, __LINE__, outlet, | ||
109 | *virq); | ||
110 | 240 | ||
111 | return 0; | 241 | return result; |
112 | } | 242 | } |
113 | 243 | ||
114 | int ps3_free_event_irq(unsigned int virq) | 244 | int ps3_free_event_irq(unsigned int virq) |
@@ -123,7 +253,7 @@ int ps3_free_event_irq(unsigned int virq) | |||
123 | pr_debug("%s:%d: lv1_destruct_event_receive_port failed: %s\n", | 253 | pr_debug("%s:%d: lv1_destruct_event_receive_port failed: %s\n", |
124 | __func__, __LINE__, ps3_result(result)); | 254 | __func__, __LINE__, ps3_result(result)); |
125 | 255 | ||
126 | irq_dispose_mapping(virq); | 256 | ps3_free_irq(virq); |
127 | 257 | ||
128 | pr_debug(" <- %s:%d\n", __func__, __LINE__); | 258 | pr_debug(" <- %s:%d\n", __func__, __LINE__); |
129 | return result; | 259 | return result; |
@@ -136,6 +266,8 @@ int ps3_send_event_locally(unsigned int virq) | |||
136 | 266 | ||
137 | /** | 267 | /** |
138 | * ps3_connect_event_irq - Assign a virq to a system bus device. | 268 | * ps3_connect_event_irq - Assign a virq to a system bus device. |
269 | * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be | ||
270 | * serviced on. | ||
139 | * @did: The HV device identifier read from the system repository. | 271 | * @did: The HV device identifier read from the system repository. |
140 | * @interrupt_id: The device interrupt id read from the system repository. | 272 | * @interrupt_id: The device interrupt id read from the system repository. |
141 | * @virq: The assigned Linux virq. | 273 | * @virq: The assigned Linux virq. |
@@ -144,12 +276,13 @@ int ps3_send_event_locally(unsigned int virq) | |||
144 | * coresponds to the software interrupt number. | 276 | * coresponds to the software interrupt number. |
145 | */ | 277 | */ |
146 | 278 | ||
147 | int ps3_connect_event_irq(const struct ps3_device_id *did, | 279 | int ps3_connect_event_irq(enum ps3_cpu_binding cpu, |
148 | unsigned int interrupt_id, unsigned int *virq) | 280 | const struct ps3_device_id *did, unsigned int interrupt_id, |
281 | unsigned int *virq) | ||
149 | { | 282 | { |
150 | int result; | 283 | int result; |
151 | 284 | ||
152 | result = ps3_alloc_event_irq(virq); | 285 | result = ps3_alloc_event_irq(cpu, virq); |
153 | 286 | ||
154 | if (result) | 287 | if (result) |
155 | return result; | 288 | return result; |
@@ -196,6 +329,8 @@ int ps3_disconnect_event_irq(const struct ps3_device_id *did, | |||
196 | 329 | ||
197 | /** | 330 | /** |
198 | * ps3_alloc_vuart_irq - Configure the system virtual uart virq. | 331 | * ps3_alloc_vuart_irq - Configure the system virtual uart virq. |
332 | * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be | ||
333 | * serviced on. | ||
199 | * @virt_addr_bmp: The caller supplied virtual uart interrupt bitmap. | 334 | * @virt_addr_bmp: The caller supplied virtual uart interrupt bitmap. |
200 | * @virq: The assigned Linux virq. | 335 | * @virq: The assigned Linux virq. |
201 | * | 336 | * |
@@ -203,13 +338,14 @@ int ps3_disconnect_event_irq(const struct ps3_device_id *did, | |||
203 | * freeing the interrupt will return a wrong state error. | 338 | * freeing the interrupt will return a wrong state error. |
204 | */ | 339 | */ |
205 | 340 | ||
206 | int ps3_alloc_vuart_irq(void* virt_addr_bmp, unsigned int *virq) | 341 | int ps3_alloc_vuart_irq(enum ps3_cpu_binding cpu, void* virt_addr_bmp, |
342 | unsigned int *virq) | ||
207 | { | 343 | { |
208 | int result; | 344 | int result; |
209 | unsigned long outlet; | 345 | unsigned long outlet; |
210 | unsigned long lpar_addr; | 346 | u64 lpar_addr; |
211 | 347 | ||
212 | BUG_ON(!is_kernel_addr((unsigned long)virt_addr_bmp)); | 348 | BUG_ON(!is_kernel_addr((u64)virt_addr_bmp)); |
213 | 349 | ||
214 | lpar_addr = ps3_mm_phys_to_lpar(__pa(virt_addr_bmp)); | 350 | lpar_addr = ps3_mm_phys_to_lpar(__pa(virt_addr_bmp)); |
215 | 351 | ||
@@ -221,12 +357,10 @@ int ps3_alloc_vuart_irq(void* virt_addr_bmp, unsigned int *virq) | |||
221 | return result; | 357 | return result; |
222 | } | 358 | } |
223 | 359 | ||
224 | *virq = irq_create_mapping(NULL, outlet); | 360 | result = ps3_alloc_irq(cpu, outlet, virq); |
225 | 361 | BUG_ON(result); | |
226 | pr_debug("%s:%d: outlet %lu, virq %u\n", __func__, __LINE__, | ||
227 | outlet, *virq); | ||
228 | 362 | ||
229 | return 0; | 363 | return result; |
230 | } | 364 | } |
231 | 365 | ||
232 | int ps3_free_vuart_irq(unsigned int virq) | 366 | int ps3_free_vuart_irq(unsigned int virq) |
@@ -241,21 +375,23 @@ int ps3_free_vuart_irq(unsigned int virq) | |||
241 | return result; | 375 | return result; |
242 | } | 376 | } |
243 | 377 | ||
244 | irq_dispose_mapping(virq); | 378 | ps3_free_irq(virq); |
245 | 379 | ||
246 | return result; | 380 | return result; |
247 | } | 381 | } |
248 | 382 | ||
249 | /** | 383 | /** |
250 | * ps3_alloc_spe_irq - Configure an spe virq. | 384 | * ps3_alloc_spe_irq - Configure an spe virq. |
385 | * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be | ||
386 | * serviced on. | ||
251 | * @spe_id: The spe_id returned from lv1_construct_logical_spe(). | 387 | * @spe_id: The spe_id returned from lv1_construct_logical_spe(). |
252 | * @class: The spe interrupt class {0,1,2}. | 388 | * @class: The spe interrupt class {0,1,2}. |
253 | * @virq: The assigned Linux virq. | 389 | * @virq: The assigned Linux virq. |
254 | * | 390 | * |
255 | */ | 391 | */ |
256 | 392 | ||
257 | int ps3_alloc_spe_irq(unsigned long spe_id, unsigned int class, | 393 | int ps3_alloc_spe_irq(enum ps3_cpu_binding cpu, unsigned long spe_id, |
258 | unsigned int *virq) | 394 | unsigned int class, unsigned int *virq) |
259 | { | 395 | { |
260 | int result; | 396 | int result; |
261 | unsigned long outlet; | 397 | unsigned long outlet; |
@@ -270,73 +406,24 @@ int ps3_alloc_spe_irq(unsigned long spe_id, unsigned int class, | |||
270 | return result; | 406 | return result; |
271 | } | 407 | } |
272 | 408 | ||
273 | *virq = irq_create_mapping(NULL, outlet); | 409 | result = ps3_alloc_irq(cpu, outlet, virq); |
274 | 410 | BUG_ON(result); | |
275 | pr_debug("%s:%d: spe_id %lu, class %u, outlet %lu, virq %u\n", | ||
276 | __func__, __LINE__, spe_id, class, outlet, *virq); | ||
277 | 411 | ||
278 | return 0; | 412 | return result; |
279 | } | 413 | } |
280 | 414 | ||
281 | int ps3_free_spe_irq(unsigned int virq) | 415 | int ps3_free_spe_irq(unsigned int virq) |
282 | { | 416 | { |
283 | irq_dispose_mapping(virq); | 417 | ps3_free_irq(virq); |
284 | return 0; | 418 | return 0; |
285 | } | 419 | } |
286 | 420 | ||
421 | |||
287 | #define PS3_INVALID_OUTLET ((irq_hw_number_t)-1) | 422 | #define PS3_INVALID_OUTLET ((irq_hw_number_t)-1) |
288 | #define PS3_PLUG_MAX 63 | 423 | #define PS3_PLUG_MAX 63 |
289 | 424 | ||
290 | /** | ||
291 | * struct bmp - a per cpu irq status and mask bitmap structure | ||
292 | * @status: 256 bit status bitmap indexed by plug | ||
293 | * @unused_1: | ||
294 | * @mask: 256 bit mask bitmap indexed by plug | ||
295 | * @unused_2: | ||
296 | * @lock: | ||
297 | * @ipi_debug_brk_mask: | ||
298 | * | ||
299 | * The HV mantains per SMT thread mappings of HV outlet to HV plug on | ||
300 | * behalf of the guest. These mappings are implemented as 256 bit guest | ||
301 | * supplied bitmaps indexed by plug number. The address of the bitmaps are | ||
302 | * registered with the HV through lv1_configure_irq_state_bitmap(). | ||
303 | * | ||
304 | * The HV supports 256 plugs per thread, assigned as {0..255}, for a total | ||
305 | * of 512 plugs supported on a processor. To simplify the logic this | ||
306 | * implementation equates HV plug value to linux virq value, constrains each | ||
307 | * interrupt to have a system wide unique plug number, and limits the range | ||
308 | * of the plug values to map into the first dword of the bitmaps. This | ||
309 | * gives a usable range of plug values of {NUM_ISA_INTERRUPTS..63}. Note | ||
310 | * that there is no constraint on how many in this set an individual thread | ||
311 | * can aquire. | ||
312 | */ | ||
313 | |||
314 | struct bmp { | ||
315 | struct { | ||
316 | unsigned long status; | ||
317 | unsigned long unused_1[3]; | ||
318 | unsigned long mask; | ||
319 | unsigned long unused_2[3]; | ||
320 | } __attribute__ ((packed)); | ||
321 | spinlock_t lock; | ||
322 | unsigned long ipi_debug_brk_mask; | ||
323 | }; | ||
324 | |||
325 | /** | ||
326 | * struct private - a per cpu data structure | ||
327 | * @node: HV node id | ||
328 | * @cpu: HV thread id | ||
329 | * @bmp: an HV bmp structure | ||
330 | */ | ||
331 | |||
332 | struct private { | ||
333 | unsigned long node; | ||
334 | unsigned int cpu; | ||
335 | struct bmp bmp; | ||
336 | }; | ||
337 | |||
338 | #if defined(DEBUG) | 425 | #if defined(DEBUG) |
339 | static void _dump_64_bmp(const char *header, const unsigned long *p, unsigned cpu, | 426 | static void _dump_64_bmp(const char *header, const u64 *p, unsigned cpu, |
340 | const char* func, int line) | 427 | const char* func, int line) |
341 | { | 428 | { |
342 | pr_debug("%s:%d: %s %u {%04lx_%04lx_%04lx_%04lx}\n", | 429 | pr_debug("%s:%d: %s %u {%04lx_%04lx_%04lx_%04lx}\n", |
@@ -346,14 +433,14 @@ static void _dump_64_bmp(const char *header, const unsigned long *p, unsigned cp | |||
346 | } | 433 | } |
347 | 434 | ||
348 | static void __attribute__ ((unused)) _dump_256_bmp(const char *header, | 435 | static void __attribute__ ((unused)) _dump_256_bmp(const char *header, |
349 | const unsigned long *p, unsigned cpu, const char* func, int line) | 436 | const u64 *p, unsigned cpu, const char* func, int line) |
350 | { | 437 | { |
351 | pr_debug("%s:%d: %s %u {%016lx:%016lx:%016lx:%016lx}\n", | 438 | pr_debug("%s:%d: %s %u {%016lx:%016lx:%016lx:%016lx}\n", |
352 | func, line, header, cpu, p[0], p[1], p[2], p[3]); | 439 | func, line, header, cpu, p[0], p[1], p[2], p[3]); |
353 | } | 440 | } |
354 | 441 | ||
355 | #define dump_bmp(_x) _dump_bmp(_x, __func__, __LINE__) | 442 | #define dump_bmp(_x) _dump_bmp(_x, __func__, __LINE__) |
356 | static void _dump_bmp(struct private* pd, const char* func, int line) | 443 | static void _dump_bmp(struct ps3_private* pd, const char* func, int line) |
357 | { | 444 | { |
358 | unsigned long flags; | 445 | unsigned long flags; |
359 | 446 | ||
@@ -364,7 +451,7 @@ static void _dump_bmp(struct private* pd, const char* func, int line) | |||
364 | } | 451 | } |
365 | 452 | ||
366 | #define dump_mask(_x) _dump_mask(_x, __func__, __LINE__) | 453 | #define dump_mask(_x) _dump_mask(_x, __func__, __LINE__) |
367 | static void __attribute__ ((unused)) _dump_mask(struct private* pd, | 454 | static void __attribute__ ((unused)) _dump_mask(struct ps3_private* pd, |
368 | const char* func, int line) | 455 | const char* func, int line) |
369 | { | 456 | { |
370 | unsigned long flags; | 457 | unsigned long flags; |
@@ -374,109 +461,94 @@ static void __attribute__ ((unused)) _dump_mask(struct private* pd, | |||
374 | spin_unlock_irqrestore(&pd->bmp.lock, flags); | 461 | spin_unlock_irqrestore(&pd->bmp.lock, flags); |
375 | } | 462 | } |
376 | #else | 463 | #else |
377 | static void dump_bmp(struct private* pd) {}; | 464 | static void dump_bmp(struct ps3_private* pd) {}; |
378 | #endif /* defined(DEBUG) */ | 465 | #endif /* defined(DEBUG) */ |
379 | 466 | ||
380 | static void chip_mask(unsigned int virq) | 467 | static void ps3_chip_mask(unsigned int virq) |
381 | { | 468 | { |
469 | struct ps3_private *pd = get_irq_chip_data(virq); | ||
470 | u64 bit = 0x8000000000000000UL >> virq; | ||
471 | u64 *p = &pd->bmp.mask; | ||
472 | u64 old; | ||
382 | unsigned long flags; | 473 | unsigned long flags; |
383 | struct private *pd = get_irq_chip_data(virq); | ||
384 | 474 | ||
385 | pr_debug("%s:%d: cpu %u, virq %d\n", __func__, __LINE__, pd->cpu, virq); | 475 | pr_debug("%s:%d: cpu %u, virq %d\n", __func__, __LINE__, pd->cpu, virq); |
386 | 476 | ||
387 | BUG_ON(virq < NUM_ISA_INTERRUPTS); | 477 | local_irq_save(flags); |
388 | BUG_ON(virq > PS3_PLUG_MAX); | 478 | asm volatile( |
389 | 479 | "1: ldarx %0,0,%3\n" | |
390 | spin_lock_irqsave(&pd->bmp.lock, flags); | 480 | "andc %0,%0,%2\n" |
391 | pd->bmp.mask &= ~(0x8000000000000000UL >> virq); | 481 | "stdcx. %0,0,%3\n" |
392 | spin_unlock_irqrestore(&pd->bmp.lock, flags); | 482 | "bne- 1b" |
483 | : "=&r" (old), "+m" (*p) | ||
484 | : "r" (bit), "r" (p) | ||
485 | : "cc" ); | ||
393 | 486 | ||
394 | lv1_did_update_interrupt_mask(pd->node, pd->cpu); | 487 | lv1_did_update_interrupt_mask(pd->node, pd->cpu); |
488 | local_irq_restore(flags); | ||
395 | } | 489 | } |
396 | 490 | ||
397 | static void chip_unmask(unsigned int virq) | 491 | static void ps3_chip_unmask(unsigned int virq) |
398 | { | 492 | { |
493 | struct ps3_private *pd = get_irq_chip_data(virq); | ||
494 | u64 bit = 0x8000000000000000UL >> virq; | ||
495 | u64 *p = &pd->bmp.mask; | ||
496 | u64 old; | ||
399 | unsigned long flags; | 497 | unsigned long flags; |
400 | struct private *pd = get_irq_chip_data(virq); | ||
401 | 498 | ||
402 | pr_debug("%s:%d: cpu %u, virq %d\n", __func__, __LINE__, pd->cpu, virq); | 499 | pr_debug("%s:%d: cpu %u, virq %d\n", __func__, __LINE__, pd->cpu, virq); |
403 | 500 | ||
404 | BUG_ON(virq < NUM_ISA_INTERRUPTS); | 501 | local_irq_save(flags); |
405 | BUG_ON(virq > PS3_PLUG_MAX); | 502 | asm volatile( |
406 | 503 | "1: ldarx %0,0,%3\n" | |
407 | spin_lock_irqsave(&pd->bmp.lock, flags); | 504 | "or %0,%0,%2\n" |
408 | pd->bmp.mask |= (0x8000000000000000UL >> virq); | 505 | "stdcx. %0,0,%3\n" |
409 | spin_unlock_irqrestore(&pd->bmp.lock, flags); | 506 | "bne- 1b" |
507 | : "=&r" (old), "+m" (*p) | ||
508 | : "r" (bit), "r" (p) | ||
509 | : "cc" ); | ||
410 | 510 | ||
411 | lv1_did_update_interrupt_mask(pd->node, pd->cpu); | 511 | lv1_did_update_interrupt_mask(pd->node, pd->cpu); |
512 | local_irq_restore(flags); | ||
412 | } | 513 | } |
413 | 514 | ||
414 | static void chip_eoi(unsigned int virq) | 515 | static void ps3_chip_eoi(unsigned int virq) |
415 | { | 516 | { |
416 | lv1_end_of_interrupt(virq); | 517 | const struct ps3_private *pd = get_irq_chip_data(virq); |
518 | lv1_end_of_interrupt_ext(pd->node, pd->cpu, virq); | ||
417 | } | 519 | } |
418 | 520 | ||
419 | static struct irq_chip irq_chip = { | 521 | static struct irq_chip irq_chip = { |
420 | .typename = "ps3", | 522 | .typename = "ps3", |
421 | .mask = chip_mask, | 523 | .mask = ps3_chip_mask, |
422 | .unmask = chip_unmask, | 524 | .unmask = ps3_chip_unmask, |
423 | .eoi = chip_eoi, | 525 | .eoi = ps3_chip_eoi, |
424 | }; | 526 | }; |
425 | 527 | ||
426 | static void host_unmap(struct irq_host *h, unsigned int virq) | 528 | static void ps3_host_unmap(struct irq_host *h, unsigned int virq) |
427 | { | 529 | { |
428 | int result; | 530 | set_irq_chip_data(virq, NULL); |
429 | |||
430 | pr_debug("%s:%d: virq %d\n", __func__, __LINE__, virq); | ||
431 | |||
432 | lv1_disconnect_irq_plug(virq); | ||
433 | |||
434 | result = set_irq_chip_data(virq, NULL); | ||
435 | BUG_ON(result); | ||
436 | } | 531 | } |
437 | 532 | ||
438 | static DEFINE_PER_CPU(struct private, private); | 533 | static int ps3_host_map(struct irq_host *h, unsigned int virq, |
439 | |||
440 | static int host_map(struct irq_host *h, unsigned int virq, | ||
441 | irq_hw_number_t hwirq) | 534 | irq_hw_number_t hwirq) |
442 | { | 535 | { |
443 | int result; | 536 | pr_debug("%s:%d: hwirq %lu, virq %u\n", __func__, __LINE__, hwirq, |
444 | unsigned int cpu; | ||
445 | |||
446 | pr_debug(" -> %s:%d\n", __func__, __LINE__); | ||
447 | pr_debug("%s:%d: hwirq %lu => virq %u\n", __func__, __LINE__, hwirq, | ||
448 | virq); | 537 | virq); |
449 | 538 | ||
450 | /* bind this virq to a cpu */ | ||
451 | |||
452 | preempt_disable(); | ||
453 | cpu = smp_processor_id(); | ||
454 | result = lv1_connect_irq_plug(virq, hwirq); | ||
455 | preempt_enable(); | ||
456 | |||
457 | if (result) { | ||
458 | pr_info("%s:%d: lv1_connect_irq_plug failed:" | ||
459 | " %s\n", __func__, __LINE__, ps3_result(result)); | ||
460 | return -EPERM; | ||
461 | } | ||
462 | |||
463 | result = set_irq_chip_data(virq, &per_cpu(private, cpu)); | ||
464 | BUG_ON(result); | ||
465 | |||
466 | set_irq_chip_and_handler(virq, &irq_chip, handle_fasteoi_irq); | 539 | set_irq_chip_and_handler(virq, &irq_chip, handle_fasteoi_irq); |
467 | 540 | ||
468 | pr_debug(" <- %s:%d\n", __func__, __LINE__); | 541 | return 0; |
469 | return result; | ||
470 | } | 542 | } |
471 | 543 | ||
472 | static struct irq_host_ops host_ops = { | 544 | static struct irq_host_ops ps3_host_ops = { |
473 | .map = host_map, | 545 | .map = ps3_host_map, |
474 | .unmap = host_unmap, | 546 | .unmap = ps3_host_unmap, |
475 | }; | 547 | }; |
476 | 548 | ||
477 | void __init ps3_register_ipi_debug_brk(unsigned int cpu, unsigned int virq) | 549 | void __init ps3_register_ipi_debug_brk(unsigned int cpu, unsigned int virq) |
478 | { | 550 | { |
479 | struct private *pd = &per_cpu(private, cpu); | 551 | struct ps3_private *pd = &per_cpu(ps3_private, cpu); |
480 | 552 | ||
481 | pd->bmp.ipi_debug_brk_mask = 0x8000000000000000UL >> virq; | 553 | pd->bmp.ipi_debug_brk_mask = 0x8000000000000000UL >> virq; |
482 | 554 | ||
@@ -484,57 +556,32 @@ void __init ps3_register_ipi_debug_brk(unsigned int cpu, unsigned int virq) | |||
484 | cpu, virq, pd->bmp.ipi_debug_brk_mask); | 556 | cpu, virq, pd->bmp.ipi_debug_brk_mask); |
485 | } | 557 | } |
486 | 558 | ||
487 | static int bmp_get_and_clear_status_bit(struct bmp *m) | 559 | unsigned int ps3_get_irq(void) |
488 | { | 560 | { |
489 | unsigned long flags; | 561 | struct ps3_private *pd = &__get_cpu_var(ps3_private); |
490 | unsigned int bit; | 562 | u64 x = (pd->bmp.status & pd->bmp.mask); |
491 | unsigned long x; | 563 | unsigned int plug; |
492 | |||
493 | spin_lock_irqsave(&m->lock, flags); | ||
494 | 564 | ||
495 | /* check for ipi break first to stop this cpu ASAP */ | 565 | /* check for ipi break first to stop this cpu ASAP */ |
496 | 566 | ||
497 | if (m->status & m->ipi_debug_brk_mask) { | 567 | if (x & pd->bmp.ipi_debug_brk_mask) |
498 | m->status &= ~m->ipi_debug_brk_mask; | 568 | x &= pd->bmp.ipi_debug_brk_mask; |
499 | spin_unlock_irqrestore(&m->lock, flags); | ||
500 | return __ilog2(m->ipi_debug_brk_mask); | ||
501 | } | ||
502 | |||
503 | x = (m->status & m->mask); | ||
504 | 569 | ||
505 | for (bit = NUM_ISA_INTERRUPTS, x <<= bit; x; bit++, x <<= 1) | 570 | asm volatile("cntlzd %0,%1" : "=r" (plug) : "r" (x)); |
506 | if (x & 0x8000000000000000UL) { | 571 | plug &= 0x3f; |
507 | m->status &= ~(0x8000000000000000UL >> bit); | ||
508 | spin_unlock_irqrestore(&m->lock, flags); | ||
509 | return bit; | ||
510 | } | ||
511 | 572 | ||
512 | spin_unlock_irqrestore(&m->lock, flags); | 573 | if (unlikely(plug) == NO_IRQ) { |
513 | |||
514 | pr_debug("%s:%d: not found\n", __func__, __LINE__); | ||
515 | return -1; | ||
516 | } | ||
517 | |||
518 | unsigned int ps3_get_irq(void) | ||
519 | { | ||
520 | int plug; | ||
521 | |||
522 | struct private *pd = &__get_cpu_var(private); | ||
523 | |||
524 | plug = bmp_get_and_clear_status_bit(&pd->bmp); | ||
525 | |||
526 | if (plug < 1) { | ||
527 | pr_debug("%s:%d: no plug found: cpu %u\n", __func__, __LINE__, | 574 | pr_debug("%s:%d: no plug found: cpu %u\n", __func__, __LINE__, |
528 | pd->cpu); | 575 | pd->cpu); |
529 | dump_bmp(&per_cpu(private, 0)); | 576 | dump_bmp(&per_cpu(ps3_private, 0)); |
530 | dump_bmp(&per_cpu(private, 1)); | 577 | dump_bmp(&per_cpu(ps3_private, 1)); |
531 | return NO_IRQ; | 578 | return NO_IRQ; |
532 | } | 579 | } |
533 | 580 | ||
534 | #if defined(DEBUG) | 581 | #if defined(DEBUG) |
535 | if (plug < NUM_ISA_INTERRUPTS || plug > PS3_PLUG_MAX) { | 582 | if (unlikely(plug < NUM_ISA_INTERRUPTS || plug > PS3_PLUG_MAX)) { |
536 | dump_bmp(&per_cpu(private, 0)); | 583 | dump_bmp(&per_cpu(ps3_private, 0)); |
537 | dump_bmp(&per_cpu(private, 1)); | 584 | dump_bmp(&per_cpu(ps3_private, 1)); |
538 | BUG(); | 585 | BUG(); |
539 | } | 586 | } |
540 | #endif | 587 | #endif |
@@ -544,26 +591,27 @@ unsigned int ps3_get_irq(void) | |||
544 | void __init ps3_init_IRQ(void) | 591 | void __init ps3_init_IRQ(void) |
545 | { | 592 | { |
546 | int result; | 593 | int result; |
547 | unsigned long node; | ||
548 | unsigned cpu; | 594 | unsigned cpu; |
549 | struct irq_host *host; | 595 | struct irq_host *host; |
550 | 596 | ||
551 | lv1_get_logical_ppe_id(&node); | 597 | host = irq_alloc_host(IRQ_HOST_MAP_NOMAP, 0, &ps3_host_ops, |
552 | |||
553 | host = irq_alloc_host(IRQ_HOST_MAP_NOMAP, 0, &host_ops, | ||
554 | PS3_INVALID_OUTLET); | 598 | PS3_INVALID_OUTLET); |
555 | irq_set_default_host(host); | 599 | irq_set_default_host(host); |
556 | irq_set_virq_count(PS3_PLUG_MAX + 1); | 600 | irq_set_virq_count(PS3_PLUG_MAX + 1); |
557 | 601 | ||
558 | for_each_possible_cpu(cpu) { | 602 | for_each_possible_cpu(cpu) { |
559 | struct private *pd = &per_cpu(private, cpu); | 603 | struct ps3_private *pd = &per_cpu(ps3_private, cpu); |
560 | 604 | ||
561 | pd->node = node; | 605 | lv1_get_logical_ppe_id(&pd->node); |
562 | pd->cpu = cpu; | 606 | pd->cpu = get_hard_smp_processor_id(cpu); |
563 | spin_lock_init(&pd->bmp.lock); | 607 | spin_lock_init(&pd->bmp.lock); |
564 | 608 | ||
565 | result = lv1_configure_irq_state_bitmap(node, cpu, | 609 | pr_debug("%s:%d: node %lu, cpu %d, bmp %lxh\n", __func__, |
566 | ps3_mm_phys_to_lpar(__pa(&pd->bmp.status))); | 610 | __LINE__, pd->node, pd->cpu, |
611 | ps3_mm_phys_to_lpar(__pa(&pd->bmp))); | ||
612 | |||
613 | result = lv1_configure_irq_state_bitmap(pd->node, pd->cpu, | ||
614 | ps3_mm_phys_to_lpar(__pa(&pd->bmp))); | ||
567 | 615 | ||
568 | if (result) | 616 | if (result) |
569 | pr_debug("%s:%d: lv1_configure_irq_state_bitmap failed:" | 617 | pr_debug("%s:%d: lv1_configure_irq_state_bitmap failed:" |
diff --git a/arch/powerpc/platforms/ps3/mm.c b/arch/powerpc/platforms/ps3/mm.c index 49c0d010d491..42354de3f557 100644 --- a/arch/powerpc/platforms/ps3/mm.c +++ b/arch/powerpc/platforms/ps3/mm.c | |||
@@ -25,7 +25,6 @@ | |||
25 | #include <asm/firmware.h> | 25 | #include <asm/firmware.h> |
26 | #include <asm/lmb.h> | 26 | #include <asm/lmb.h> |
27 | #include <asm/udbg.h> | 27 | #include <asm/udbg.h> |
28 | #include <asm/ps3.h> | ||
29 | #include <asm/lv1call.h> | 28 | #include <asm/lv1call.h> |
30 | 29 | ||
31 | #include "platform.h" | 30 | #include "platform.h" |
diff --git a/arch/powerpc/platforms/ps3/os-area.c b/arch/powerpc/platforms/ps3/os-area.c index 58358305dc10..5c3da08bc0c4 100644 --- a/arch/powerpc/platforms/ps3/os-area.c +++ b/arch/powerpc/platforms/ps3/os-area.c | |||
@@ -22,7 +22,6 @@ | |||
22 | #include <linux/io.h> | 22 | #include <linux/io.h> |
23 | 23 | ||
24 | #include <asm/lmb.h> | 24 | #include <asm/lmb.h> |
25 | #include <asm/ps3.h> | ||
26 | 25 | ||
27 | #include "platform.h" | 26 | #include "platform.h" |
28 | 27 | ||
@@ -59,7 +58,7 @@ struct os_area_header { | |||
59 | u32 ldr_format; | 58 | u32 ldr_format; |
60 | u32 ldr_size; | 59 | u32 ldr_size; |
61 | u32 _reserved_2[6]; | 60 | u32 _reserved_2[6]; |
62 | } __attribute__ ((packed)); | 61 | }; |
63 | 62 | ||
64 | enum { | 63 | enum { |
65 | PARAM_BOOT_FLAG_GAME_OS = 0, | 64 | PARAM_BOOT_FLAG_GAME_OS = 0, |
@@ -67,13 +66,6 @@ enum { | |||
67 | }; | 66 | }; |
68 | 67 | ||
69 | enum { | 68 | enum { |
70 | PARAM_AV_MULTI_OUT_NTSC = 0, | ||
71 | PARAM_AV_MULTI_OUT_PAL_RGB = 1, | ||
72 | PARAM_AV_MULTI_OUT_PAL_YCBCR = 2, | ||
73 | PARAM_AV_MULTI_OUT_SECAM = 3, | ||
74 | }; | ||
75 | |||
76 | enum { | ||
77 | PARAM_CTRL_BUTTON_O_IS_YES = 0, | 69 | PARAM_CTRL_BUTTON_O_IS_YES = 0, |
78 | PARAM_CTRL_BUTTON_X_IS_YES = 1, | 70 | PARAM_CTRL_BUTTON_X_IS_YES = 1, |
79 | }; | 71 | }; |
@@ -114,7 +106,7 @@ struct os_area_params { | |||
114 | u8 dns_primary[4]; | 106 | u8 dns_primary[4]; |
115 | u8 dns_secondary[4]; | 107 | u8 dns_secondary[4]; |
116 | u8 _reserved_5[8]; | 108 | u8 _reserved_5[8]; |
117 | } __attribute__ ((packed)); | 109 | }; |
118 | 110 | ||
119 | /** | 111 | /** |
120 | * struct saved_params - Static working copies of data from the 'Other OS' area. | 112 | * struct saved_params - Static working copies of data from the 'Other OS' area. |
@@ -257,3 +249,13 @@ u64 ps3_os_area_rtc_diff(void) | |||
257 | { | 249 | { |
258 | return saved_params.rtc_diff ? saved_params.rtc_diff : 946684800UL; | 250 | return saved_params.rtc_diff ? saved_params.rtc_diff : 946684800UL; |
259 | } | 251 | } |
252 | |||
253 | /** | ||
254 | * ps3_os_area_get_av_multi_out - Returns the default video mode. | ||
255 | */ | ||
256 | |||
257 | enum ps3_param_av_multi_out ps3_os_area_get_av_multi_out(void) | ||
258 | { | ||
259 | return saved_params.av_multi_out; | ||
260 | } | ||
261 | EXPORT_SYMBOL_GPL(ps3_os_area_get_av_multi_out); | ||
diff --git a/arch/powerpc/platforms/ps3/platform.h b/arch/powerpc/platforms/ps3/platform.h index 23b111bea9d0..ca04f03305c7 100644 --- a/arch/powerpc/platforms/ps3/platform.h +++ b/arch/powerpc/platforms/ps3/platform.h | |||
@@ -22,6 +22,9 @@ | |||
22 | #define _PS3_PLATFORM_H | 22 | #define _PS3_PLATFORM_H |
23 | 23 | ||
24 | #include <linux/rtc.h> | 24 | #include <linux/rtc.h> |
25 | #include <scsi/scsi.h> | ||
26 | |||
27 | #include <asm/ps3.h> | ||
25 | 28 | ||
26 | /* htab */ | 29 | /* htab */ |
27 | 30 | ||
@@ -65,4 +68,152 @@ void ps3_spu_set_platform (void); | |||
65 | static inline void ps3_spu_set_platform (void) {} | 68 | static inline void ps3_spu_set_platform (void) {} |
66 | #endif | 69 | #endif |
67 | 70 | ||
71 | /* repository bus info */ | ||
72 | |||
73 | enum ps3_bus_type { | ||
74 | PS3_BUS_TYPE_SB = 4, | ||
75 | PS3_BUS_TYPE_STORAGE = 5, | ||
76 | }; | ||
77 | |||
78 | enum ps3_dev_type { | ||
79 | PS3_DEV_TYPE_STOR_DISK = TYPE_DISK, /* 0 */ | ||
80 | PS3_DEV_TYPE_SB_GELIC = 3, | ||
81 | PS3_DEV_TYPE_SB_USB = 4, | ||
82 | PS3_DEV_TYPE_STOR_ROM = TYPE_ROM, /* 5 */ | ||
83 | PS3_DEV_TYPE_SB_GPIO = 6, | ||
84 | PS3_DEV_TYPE_STOR_FLASH = TYPE_RBC, /* 14 */ | ||
85 | }; | ||
86 | |||
87 | int ps3_repository_read_bus_str(unsigned int bus_index, const char *bus_str, | ||
88 | u64 *value); | ||
89 | int ps3_repository_read_bus_id(unsigned int bus_index, unsigned int *bus_id); | ||
90 | int ps3_repository_read_bus_type(unsigned int bus_index, | ||
91 | enum ps3_bus_type *bus_type); | ||
92 | int ps3_repository_read_bus_num_dev(unsigned int bus_index, | ||
93 | unsigned int *num_dev); | ||
94 | |||
95 | /* repository bus device info */ | ||
96 | |||
97 | enum ps3_interrupt_type { | ||
98 | PS3_INTERRUPT_TYPE_EVENT_PORT = 2, | ||
99 | PS3_INTERRUPT_TYPE_SB_OHCI = 3, | ||
100 | PS3_INTERRUPT_TYPE_SB_EHCI = 4, | ||
101 | PS3_INTERRUPT_TYPE_OTHER = 5, | ||
102 | }; | ||
103 | |||
104 | enum ps3_reg_type { | ||
105 | PS3_REG_TYPE_SB_OHCI = 3, | ||
106 | PS3_REG_TYPE_SB_EHCI = 4, | ||
107 | PS3_REG_TYPE_SB_GPIO = 5, | ||
108 | }; | ||
109 | |||
110 | int ps3_repository_read_dev_str(unsigned int bus_index, | ||
111 | unsigned int dev_index, const char *dev_str, u64 *value); | ||
112 | int ps3_repository_read_dev_id(unsigned int bus_index, unsigned int dev_index, | ||
113 | unsigned int *dev_id); | ||
114 | int ps3_repository_read_dev_type(unsigned int bus_index, | ||
115 | unsigned int dev_index, enum ps3_dev_type *dev_type); | ||
116 | int ps3_repository_read_dev_intr(unsigned int bus_index, | ||
117 | unsigned int dev_index, unsigned int intr_index, | ||
118 | enum ps3_interrupt_type *intr_type, unsigned int *interrupt_id); | ||
119 | int ps3_repository_read_dev_reg_type(unsigned int bus_index, | ||
120 | unsigned int dev_index, unsigned int reg_index, | ||
121 | enum ps3_reg_type *reg_type); | ||
122 | int ps3_repository_read_dev_reg_addr(unsigned int bus_index, | ||
123 | unsigned int dev_index, unsigned int reg_index, u64 *bus_addr, | ||
124 | u64 *len); | ||
125 | int ps3_repository_read_dev_reg(unsigned int bus_index, | ||
126 | unsigned int dev_index, unsigned int reg_index, | ||
127 | enum ps3_reg_type *reg_type, u64 *bus_addr, u64 *len); | ||
128 | |||
129 | /* repository bus enumerators */ | ||
130 | |||
131 | struct ps3_repository_device { | ||
132 | unsigned int bus_index; | ||
133 | unsigned int dev_index; | ||
134 | struct ps3_device_id did; | ||
135 | }; | ||
136 | |||
137 | int ps3_repository_find_device(enum ps3_bus_type bus_type, | ||
138 | enum ps3_dev_type dev_type, | ||
139 | const struct ps3_repository_device *start_dev, | ||
140 | struct ps3_repository_device *dev); | ||
141 | static inline int ps3_repository_find_first_device( | ||
142 | enum ps3_bus_type bus_type, enum ps3_dev_type dev_type, | ||
143 | struct ps3_repository_device *dev) | ||
144 | { | ||
145 | return ps3_repository_find_device(bus_type, dev_type, NULL, dev); | ||
146 | } | ||
147 | int ps3_repository_find_interrupt(const struct ps3_repository_device *dev, | ||
148 | enum ps3_interrupt_type intr_type, unsigned int *interrupt_id); | ||
149 | int ps3_repository_find_reg(const struct ps3_repository_device *dev, | ||
150 | enum ps3_reg_type reg_type, u64 *bus_addr, u64 *len); | ||
151 | |||
152 | /* repository block device info */ | ||
153 | |||
154 | int ps3_repository_read_stor_dev_port(unsigned int bus_index, | ||
155 | unsigned int dev_index, u64 *port); | ||
156 | int ps3_repository_read_stor_dev_blk_size(unsigned int bus_index, | ||
157 | unsigned int dev_index, u64 *blk_size); | ||
158 | int ps3_repository_read_stor_dev_num_blocks(unsigned int bus_index, | ||
159 | unsigned int dev_index, u64 *num_blocks); | ||
160 | int ps3_repository_read_stor_dev_num_regions(unsigned int bus_index, | ||
161 | unsigned int dev_index, unsigned int *num_regions); | ||
162 | int ps3_repository_read_stor_dev_region_id(unsigned int bus_index, | ||
163 | unsigned int dev_index, unsigned int region_index, | ||
164 | unsigned int *region_id); | ||
165 | int ps3_repository_read_stor_dev_region_size(unsigned int bus_index, | ||
166 | unsigned int dev_index, unsigned int region_index, u64 *region_size); | ||
167 | int ps3_repository_read_stor_dev_region_start(unsigned int bus_index, | ||
168 | unsigned int dev_index, unsigned int region_index, u64 *region_start); | ||
169 | int ps3_repository_read_stor_dev_info(unsigned int bus_index, | ||
170 | unsigned int dev_index, u64 *port, u64 *blk_size, | ||
171 | u64 *num_blocks, unsigned int *num_regions); | ||
172 | int ps3_repository_read_stor_dev_region(unsigned int bus_index, | ||
173 | unsigned int dev_index, unsigned int region_index, | ||
174 | unsigned int *region_id, u64 *region_start, u64 *region_size); | ||
175 | |||
176 | /* repository pu and memory info */ | ||
177 | |||
178 | int ps3_repository_read_num_pu(unsigned int *num_pu); | ||
179 | int ps3_repository_read_ppe_id(unsigned int *pu_index, unsigned int *ppe_id); | ||
180 | int ps3_repository_read_rm_base(unsigned int ppe_id, u64 *rm_base); | ||
181 | int ps3_repository_read_rm_size(unsigned int ppe_id, u64 *rm_size); | ||
182 | int ps3_repository_read_region_total(u64 *region_total); | ||
183 | int ps3_repository_read_mm_info(u64 *rm_base, u64 *rm_size, | ||
184 | u64 *region_total); | ||
185 | |||
186 | /* repository pme info */ | ||
187 | |||
188 | int ps3_repository_read_num_be(unsigned int *num_be); | ||
189 | int ps3_repository_read_be_node_id(unsigned int be_index, u64 *node_id); | ||
190 | int ps3_repository_read_tb_freq(u64 node_id, u64 *tb_freq); | ||
191 | int ps3_repository_read_be_tb_freq(unsigned int be_index, u64 *tb_freq); | ||
192 | |||
193 | /* repository 'Other OS' area */ | ||
194 | |||
195 | int ps3_repository_read_boot_dat_addr(u64 *lpar_addr); | ||
196 | int ps3_repository_read_boot_dat_size(unsigned int *size); | ||
197 | int ps3_repository_read_boot_dat_info(u64 *lpar_addr, unsigned int *size); | ||
198 | |||
199 | /* repository spu info */ | ||
200 | |||
201 | /** | ||
202 | * enum spu_resource_type - Type of spu resource. | ||
203 | * @spu_resource_type_shared: Logical spu is shared with other partions. | ||
204 | * @spu_resource_type_exclusive: Logical spu is not shared with other partions. | ||
205 | * | ||
206 | * Returned by ps3_repository_read_spu_resource_id(). | ||
207 | */ | ||
208 | |||
209 | enum ps3_spu_resource_type { | ||
210 | PS3_SPU_RESOURCE_TYPE_SHARED = 0, | ||
211 | PS3_SPU_RESOURCE_TYPE_EXCLUSIVE = 0x8000000000000000UL, | ||
212 | }; | ||
213 | |||
214 | int ps3_repository_read_num_spu_reserved(unsigned int *num_spu_reserved); | ||
215 | int ps3_repository_read_num_spu_resource_id(unsigned int *num_resource_id); | ||
216 | int ps3_repository_read_spu_resource_id(unsigned int res_index, | ||
217 | enum ps3_spu_resource_type* resource_type, unsigned int *resource_id); | ||
218 | |||
68 | #endif | 219 | #endif |
diff --git a/arch/powerpc/platforms/ps3/repository.c b/arch/powerpc/platforms/ps3/repository.c index 273a0d621bdd..ae586a0e5d3f 100644 --- a/arch/powerpc/platforms/ps3/repository.c +++ b/arch/powerpc/platforms/ps3/repository.c | |||
@@ -18,9 +18,10 @@ | |||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
19 | */ | 19 | */ |
20 | 20 | ||
21 | #include <asm/ps3.h> | ||
22 | #include <asm/lv1call.h> | 21 | #include <asm/lv1call.h> |
23 | 22 | ||
23 | #include "platform.h" | ||
24 | |||
24 | enum ps3_vendor_id { | 25 | enum ps3_vendor_id { |
25 | PS3_VENDOR_ID_NONE = 0, | 26 | PS3_VENDOR_ID_NONE = 0, |
26 | PS3_VENDOR_ID_SONY = 0x8000000000000000UL, | 27 | PS3_VENDOR_ID_SONY = 0x8000000000000000UL, |
@@ -257,7 +258,7 @@ int ps3_repository_read_dev_type(unsigned int bus_index, | |||
257 | 258 | ||
258 | int ps3_repository_read_dev_intr(unsigned int bus_index, | 259 | int ps3_repository_read_dev_intr(unsigned int bus_index, |
259 | unsigned int dev_index, unsigned int intr_index, | 260 | unsigned int dev_index, unsigned int intr_index, |
260 | unsigned int *intr_type, unsigned int* interrupt_id) | 261 | enum ps3_interrupt_type *intr_type, unsigned int* interrupt_id) |
261 | { | 262 | { |
262 | int result; | 263 | int result; |
263 | u64 v1; | 264 | u64 v1; |
@@ -275,7 +276,8 @@ int ps3_repository_read_dev_intr(unsigned int bus_index, | |||
275 | } | 276 | } |
276 | 277 | ||
277 | int ps3_repository_read_dev_reg_type(unsigned int bus_index, | 278 | int ps3_repository_read_dev_reg_type(unsigned int bus_index, |
278 | unsigned int dev_index, unsigned int reg_index, unsigned int *reg_type) | 279 | unsigned int dev_index, unsigned int reg_index, |
280 | enum ps3_reg_type *reg_type) | ||
279 | { | 281 | { |
280 | int result; | 282 | int result; |
281 | u64 v1; | 283 | u64 v1; |
@@ -302,8 +304,8 @@ int ps3_repository_read_dev_reg_addr(unsigned int bus_index, | |||
302 | } | 304 | } |
303 | 305 | ||
304 | int ps3_repository_read_dev_reg(unsigned int bus_index, | 306 | int ps3_repository_read_dev_reg(unsigned int bus_index, |
305 | unsigned int dev_index, unsigned int reg_index, unsigned int *reg_type, | 307 | unsigned int dev_index, unsigned int reg_index, |
306 | u64 *bus_addr, u64 *len) | 308 | enum ps3_reg_type *reg_type, u64 *bus_addr, u64 *len) |
307 | { | 309 | { |
308 | int result = ps3_repository_read_dev_reg_type(bus_index, dev_index, | 310 | int result = ps3_repository_read_dev_reg_type(bus_index, dev_index, |
309 | reg_index, reg_type); | 311 | reg_index, reg_type); |
@@ -343,7 +345,7 @@ int ps3_repository_dump_resource_info(unsigned int bus_index, | |||
343 | } | 345 | } |
344 | 346 | ||
345 | for (res_index = 0; res_index < 10; res_index++) { | 347 | for (res_index = 0; res_index < 10; res_index++) { |
346 | enum ps3_region_type reg_type; | 348 | enum ps3_reg_type reg_type; |
347 | u64 bus_addr; | 349 | u64 bus_addr; |
348 | u64 len; | 350 | u64 len; |
349 | 351 | ||
@@ -367,7 +369,55 @@ int ps3_repository_dump_resource_info(unsigned int bus_index, | |||
367 | return result; | 369 | return result; |
368 | } | 370 | } |
369 | 371 | ||
370 | static int dump_device_info(unsigned int bus_index, unsigned int num_dev) | 372 | static int dump_stor_dev_info(unsigned int bus_index, unsigned int dev_index) |
373 | { | ||
374 | int result = 0; | ||
375 | unsigned int num_regions, region_index; | ||
376 | u64 port, blk_size, num_blocks; | ||
377 | |||
378 | pr_debug(" -> %s:%d: (%u:%u)\n", __func__, __LINE__, | ||
379 | bus_index, dev_index); | ||
380 | |||
381 | result = ps3_repository_read_stor_dev_info(bus_index, dev_index, &port, | ||
382 | &blk_size, &num_blocks, &num_regions); | ||
383 | if (result) { | ||
384 | pr_debug("%s:%d ps3_repository_read_stor_dev_info" | ||
385 | " (%u:%u) failed\n", __func__, __LINE__, | ||
386 | bus_index, dev_index); | ||
387 | goto out; | ||
388 | } | ||
389 | |||
390 | pr_debug("%s:%d (%u:%u): port %lu, blk_size %lu, num_blocks " | ||
391 | "%lu, num_regions %u\n", | ||
392 | __func__, __LINE__, bus_index, dev_index, port, | ||
393 | blk_size, num_blocks, num_regions); | ||
394 | |||
395 | for (region_index = 0; region_index < num_regions; region_index++) { | ||
396 | unsigned int region_id; | ||
397 | u64 region_start, region_size; | ||
398 | |||
399 | result = ps3_repository_read_stor_dev_region(bus_index, | ||
400 | dev_index, region_index, ®ion_id, ®ion_start, | ||
401 | ®ion_size); | ||
402 | if (result) { | ||
403 | pr_debug("%s:%d ps3_repository_read_stor_dev_region" | ||
404 | " (%u:%u) failed\n", __func__, __LINE__, | ||
405 | bus_index, dev_index); | ||
406 | break; | ||
407 | } | ||
408 | |||
409 | pr_debug("%s:%d (%u:%u) region_id %u, start %lxh, size %lxh\n", | ||
410 | __func__, __LINE__, bus_index, dev_index, region_id, | ||
411 | region_start, region_size); | ||
412 | } | ||
413 | |||
414 | out: | ||
415 | pr_debug(" <- %s:%d\n", __func__, __LINE__); | ||
416 | return result; | ||
417 | } | ||
418 | |||
419 | static int dump_device_info(unsigned int bus_index, enum ps3_bus_type bus_type, | ||
420 | unsigned int num_dev) | ||
371 | { | 421 | { |
372 | int result = 0; | 422 | int result = 0; |
373 | unsigned int dev_index; | 423 | unsigned int dev_index; |
@@ -402,6 +452,9 @@ static int dump_device_info(unsigned int bus_index, unsigned int num_dev) | |||
402 | __LINE__, bus_index, dev_index, dev_type, dev_id); | 452 | __LINE__, bus_index, dev_index, dev_type, dev_id); |
403 | 453 | ||
404 | ps3_repository_dump_resource_info(bus_index, dev_index); | 454 | ps3_repository_dump_resource_info(bus_index, dev_index); |
455 | |||
456 | if (bus_type == PS3_BUS_TYPE_STORAGE) | ||
457 | dump_stor_dev_info(bus_index, dev_index); | ||
405 | } | 458 | } |
406 | 459 | ||
407 | pr_debug(" <- %s:%d\n", __func__, __LINE__); | 460 | pr_debug(" <- %s:%d\n", __func__, __LINE__); |
@@ -452,7 +505,7 @@ int ps3_repository_dump_bus_info(void) | |||
452 | __func__, __LINE__, bus_index, bus_type, bus_id, | 505 | __func__, __LINE__, bus_index, bus_type, bus_id, |
453 | num_dev); | 506 | num_dev); |
454 | 507 | ||
455 | dump_device_info(bus_index, num_dev); | 508 | dump_device_info(bus_index, bus_type, num_dev); |
456 | } | 509 | } |
457 | 510 | ||
458 | pr_debug(" <- %s:%d\n", __func__, __LINE__); | 511 | pr_debug(" <- %s:%d\n", __func__, __LINE__); |
@@ -487,7 +540,8 @@ static int find_device(unsigned int bus_index, unsigned int num_dev, | |||
487 | break; | 540 | break; |
488 | } | 541 | } |
489 | 542 | ||
490 | BUG_ON(dev_index == num_dev); | 543 | if (dev_index == num_dev) |
544 | return -1; | ||
491 | 545 | ||
492 | pr_debug("%s:%d: found dev_type %u at dev_index %u\n", | 546 | pr_debug("%s:%d: found dev_type %u at dev_index %u\n", |
493 | __func__, __LINE__, dev_type, dev_index); | 547 | __func__, __LINE__, dev_type, dev_index); |
@@ -521,7 +575,7 @@ int ps3_repository_find_device (enum ps3_bus_type bus_type, | |||
521 | pr_debug("%s:%d: find bus_type %u, dev_type %u\n", __func__, __LINE__, | 575 | pr_debug("%s:%d: find bus_type %u, dev_type %u\n", __func__, __LINE__, |
522 | bus_type, dev_type); | 576 | bus_type, dev_type); |
523 | 577 | ||
524 | dev->bus_index = UINT_MAX; | 578 | BUG_ON(start_dev && start_dev->bus_index > 10); |
525 | 579 | ||
526 | for (bus_index = start_dev ? start_dev->bus_index : 0; bus_index < 10; | 580 | for (bus_index = start_dev ? start_dev->bus_index : 0; bus_index < 10; |
527 | bus_index++) { | 581 | bus_index++) { |
@@ -532,13 +586,15 @@ int ps3_repository_find_device (enum ps3_bus_type bus_type, | |||
532 | if (result) { | 586 | if (result) { |
533 | pr_debug("%s:%d read_bus_type failed\n", | 587 | pr_debug("%s:%d read_bus_type failed\n", |
534 | __func__, __LINE__); | 588 | __func__, __LINE__); |
589 | dev->bus_index = UINT_MAX; | ||
535 | return result; | 590 | return result; |
536 | } | 591 | } |
537 | if (x == bus_type) | 592 | if (x == bus_type) |
538 | break; | 593 | break; |
539 | } | 594 | } |
540 | 595 | ||
541 | BUG_ON(bus_index == 10); | 596 | if (bus_index >= 10) |
597 | return -ENODEV; | ||
542 | 598 | ||
543 | pr_debug("%s:%d: found bus_type %u at bus_index %u\n", | 599 | pr_debug("%s:%d: found bus_type %u at bus_index %u\n", |
544 | __func__, __LINE__, bus_type, bus_index); | 600 | __func__, __LINE__, bus_type, bus_index); |
@@ -604,7 +660,8 @@ int ps3_repository_find_interrupt(const struct ps3_repository_device *dev, | |||
604 | } | 660 | } |
605 | } | 661 | } |
606 | 662 | ||
607 | BUG_ON(res_index == 10); | 663 | if (res_index == 10) |
664 | return -ENODEV; | ||
608 | 665 | ||
609 | pr_debug("%s:%d: found intr_type %u at res_index %u\n", | 666 | pr_debug("%s:%d: found intr_type %u at res_index %u\n", |
610 | __func__, __LINE__, intr_type, res_index); | 667 | __func__, __LINE__, intr_type, res_index); |
@@ -612,8 +669,8 @@ int ps3_repository_find_interrupt(const struct ps3_repository_device *dev, | |||
612 | return result; | 669 | return result; |
613 | } | 670 | } |
614 | 671 | ||
615 | int ps3_repository_find_region(const struct ps3_repository_device *dev, | 672 | int ps3_repository_find_reg(const struct ps3_repository_device *dev, |
616 | enum ps3_region_type reg_type, u64 *bus_addr, u64 *len) | 673 | enum ps3_reg_type reg_type, u64 *bus_addr, u64 *len) |
617 | { | 674 | { |
618 | int result = 0; | 675 | int result = 0; |
619 | unsigned int res_index; | 676 | unsigned int res_index; |
@@ -623,7 +680,7 @@ int ps3_repository_find_region(const struct ps3_repository_device *dev, | |||
623 | *bus_addr = *len = 0; | 680 | *bus_addr = *len = 0; |
624 | 681 | ||
625 | for (res_index = 0; res_index < 10; res_index++) { | 682 | for (res_index = 0; res_index < 10; res_index++) { |
626 | enum ps3_region_type t; | 683 | enum ps3_reg_type t; |
627 | u64 a; | 684 | u64 a; |
628 | u64 l; | 685 | u64 l; |
629 | 686 | ||
@@ -643,7 +700,8 @@ int ps3_repository_find_region(const struct ps3_repository_device *dev, | |||
643 | } | 700 | } |
644 | } | 701 | } |
645 | 702 | ||
646 | BUG_ON(res_index == 10); | 703 | if (res_index == 10) |
704 | return -ENODEV; | ||
647 | 705 | ||
648 | pr_debug("%s:%d: found reg_type %u at res_index %u\n", | 706 | pr_debug("%s:%d: found reg_type %u at res_index %u\n", |
649 | __func__, __LINE__, reg_type, res_index); | 707 | __func__, __LINE__, reg_type, res_index); |
@@ -651,6 +709,136 @@ int ps3_repository_find_region(const struct ps3_repository_device *dev, | |||
651 | return result; | 709 | return result; |
652 | } | 710 | } |
653 | 711 | ||
712 | int ps3_repository_read_stor_dev_port(unsigned int bus_index, | ||
713 | unsigned int dev_index, u64 *port) | ||
714 | { | ||
715 | return read_node(PS3_LPAR_ID_PME, | ||
716 | make_first_field("bus", bus_index), | ||
717 | make_field("dev", dev_index), | ||
718 | make_field("port", 0), | ||
719 | 0, port, 0); | ||
720 | } | ||
721 | |||
722 | int ps3_repository_read_stor_dev_blk_size(unsigned int bus_index, | ||
723 | unsigned int dev_index, u64 *blk_size) | ||
724 | { | ||
725 | return read_node(PS3_LPAR_ID_PME, | ||
726 | make_first_field("bus", bus_index), | ||
727 | make_field("dev", dev_index), | ||
728 | make_field("blk_size", 0), | ||
729 | 0, blk_size, 0); | ||
730 | } | ||
731 | |||
732 | int ps3_repository_read_stor_dev_num_blocks(unsigned int bus_index, | ||
733 | unsigned int dev_index, u64 *num_blocks) | ||
734 | { | ||
735 | return read_node(PS3_LPAR_ID_PME, | ||
736 | make_first_field("bus", bus_index), | ||
737 | make_field("dev", dev_index), | ||
738 | make_field("n_blocks", 0), | ||
739 | 0, num_blocks, 0); | ||
740 | } | ||
741 | |||
742 | int ps3_repository_read_stor_dev_num_regions(unsigned int bus_index, | ||
743 | unsigned int dev_index, unsigned int *num_regions) | ||
744 | { | ||
745 | int result; | ||
746 | u64 v1; | ||
747 | |||
748 | result = read_node(PS3_LPAR_ID_PME, | ||
749 | make_first_field("bus", bus_index), | ||
750 | make_field("dev", dev_index), | ||
751 | make_field("n_regs", 0), | ||
752 | 0, &v1, 0); | ||
753 | *num_regions = v1; | ||
754 | return result; | ||
755 | } | ||
756 | |||
757 | int ps3_repository_read_stor_dev_region_id(unsigned int bus_index, | ||
758 | unsigned int dev_index, unsigned int region_index, | ||
759 | unsigned int *region_id) | ||
760 | { | ||
761 | int result; | ||
762 | u64 v1; | ||
763 | |||
764 | result = read_node(PS3_LPAR_ID_PME, | ||
765 | make_first_field("bus", bus_index), | ||
766 | make_field("dev", dev_index), | ||
767 | make_field("region", region_index), | ||
768 | make_field("id", 0), | ||
769 | &v1, 0); | ||
770 | *region_id = v1; | ||
771 | return result; | ||
772 | } | ||
773 | |||
774 | int ps3_repository_read_stor_dev_region_size(unsigned int bus_index, | ||
775 | unsigned int dev_index, unsigned int region_index, u64 *region_size) | ||
776 | { | ||
777 | return read_node(PS3_LPAR_ID_PME, | ||
778 | make_first_field("bus", bus_index), | ||
779 | make_field("dev", dev_index), | ||
780 | make_field("region", region_index), | ||
781 | make_field("size", 0), | ||
782 | region_size, 0); | ||
783 | } | ||
784 | |||
785 | int ps3_repository_read_stor_dev_region_start(unsigned int bus_index, | ||
786 | unsigned int dev_index, unsigned int region_index, u64 *region_start) | ||
787 | { | ||
788 | return read_node(PS3_LPAR_ID_PME, | ||
789 | make_first_field("bus", bus_index), | ||
790 | make_field("dev", dev_index), | ||
791 | make_field("region", region_index), | ||
792 | make_field("start", 0), | ||
793 | region_start, 0); | ||
794 | } | ||
795 | |||
796 | int ps3_repository_read_stor_dev_info(unsigned int bus_index, | ||
797 | unsigned int dev_index, u64 *port, u64 *blk_size, | ||
798 | u64 *num_blocks, unsigned int *num_regions) | ||
799 | { | ||
800 | int result; | ||
801 | |||
802 | result = ps3_repository_read_stor_dev_port(bus_index, dev_index, port); | ||
803 | if (result) | ||
804 | return result; | ||
805 | |||
806 | result = ps3_repository_read_stor_dev_blk_size(bus_index, dev_index, | ||
807 | blk_size); | ||
808 | if (result) | ||
809 | return result; | ||
810 | |||
811 | result = ps3_repository_read_stor_dev_num_blocks(bus_index, dev_index, | ||
812 | num_blocks); | ||
813 | if (result) | ||
814 | return result; | ||
815 | |||
816 | result = ps3_repository_read_stor_dev_num_regions(bus_index, dev_index, | ||
817 | num_regions); | ||
818 | return result; | ||
819 | } | ||
820 | |||
821 | int ps3_repository_read_stor_dev_region(unsigned int bus_index, | ||
822 | unsigned int dev_index, unsigned int region_index, | ||
823 | unsigned int *region_id, u64 *region_start, u64 *region_size) | ||
824 | { | ||
825 | int result; | ||
826 | |||
827 | result = ps3_repository_read_stor_dev_region_id(bus_index, dev_index, | ||
828 | region_index, region_id); | ||
829 | if (result) | ||
830 | return result; | ||
831 | |||
832 | result = ps3_repository_read_stor_dev_region_start(bus_index, dev_index, | ||
833 | region_index, region_start); | ||
834 | if (result) | ||
835 | return result; | ||
836 | |||
837 | result = ps3_repository_read_stor_dev_region_size(bus_index, dev_index, | ||
838 | region_index, region_size); | ||
839 | return result; | ||
840 | } | ||
841 | |||
654 | int ps3_repository_read_rm_size(unsigned int ppe_id, u64 *rm_size) | 842 | int ps3_repository_read_rm_size(unsigned int ppe_id, u64 *rm_size) |
655 | { | 843 | { |
656 | return read_node(PS3_LPAR_ID_CURRENT, | 844 | return read_node(PS3_LPAR_ID_CURRENT, |
diff --git a/arch/powerpc/platforms/ps3/setup.c b/arch/powerpc/platforms/ps3/setup.c index d8b5cadbe80e..e62505e18813 100644 --- a/arch/powerpc/platforms/ps3/setup.c +++ b/arch/powerpc/platforms/ps3/setup.c | |||
@@ -41,10 +41,18 @@ | |||
41 | #define DBG(fmt...) do{if(0)printk(fmt);}while(0) | 41 | #define DBG(fmt...) do{if(0)printk(fmt);}while(0) |
42 | #endif | 42 | #endif |
43 | 43 | ||
44 | static void ps3_show_cpuinfo(struct seq_file *m) | 44 | int ps3_get_firmware_version(union ps3_firmware_version *v) |
45 | { | 45 | { |
46 | seq_printf(m, "machine\t\t: %s\n", ppc_md.name); | 46 | int result = lv1_get_version_info(&v->raw); |
47 | |||
48 | if (result) { | ||
49 | v->raw = 0; | ||
50 | return -1; | ||
51 | } | ||
52 | |||
53 | return result; | ||
47 | } | 54 | } |
55 | EXPORT_SYMBOL_GPL(ps3_get_firmware_version); | ||
48 | 56 | ||
49 | static void ps3_power_save(void) | 57 | static void ps3_power_save(void) |
50 | { | 58 | { |
@@ -74,8 +82,14 @@ static void ps3_panic(char *str) | |||
74 | 82 | ||
75 | static void __init ps3_setup_arch(void) | 83 | static void __init ps3_setup_arch(void) |
76 | { | 84 | { |
85 | union ps3_firmware_version v; | ||
86 | |||
77 | DBG(" -> %s:%d\n", __func__, __LINE__); | 87 | DBG(" -> %s:%d\n", __func__, __LINE__); |
78 | 88 | ||
89 | ps3_get_firmware_version(&v); | ||
90 | printk(KERN_INFO "PS3 firmware version %u.%u.%u\n", v.major, v.minor, | ||
91 | v.rev); | ||
92 | |||
79 | ps3_spu_set_platform(); | 93 | ps3_spu_set_platform(); |
80 | ps3_map_htab(); | 94 | ps3_map_htab(); |
81 | 95 | ||
@@ -156,7 +170,6 @@ define_machine(ps3) { | |||
156 | .name = "PS3", | 170 | .name = "PS3", |
157 | .probe = ps3_probe, | 171 | .probe = ps3_probe, |
158 | .setup_arch = ps3_setup_arch, | 172 | .setup_arch = ps3_setup_arch, |
159 | .show_cpuinfo = ps3_show_cpuinfo, | ||
160 | .init_IRQ = ps3_init_IRQ, | 173 | .init_IRQ = ps3_init_IRQ, |
161 | .panic = ps3_panic, | 174 | .panic = ps3_panic, |
162 | .get_boot_time = ps3_get_boot_time, | 175 | .get_boot_time = ps3_get_boot_time, |
diff --git a/arch/powerpc/platforms/ps3/smp.c b/arch/powerpc/platforms/ps3/smp.c index 11d2080607ed..6fb887961a6d 100644 --- a/arch/powerpc/platforms/ps3/smp.c +++ b/arch/powerpc/platforms/ps3/smp.c | |||
@@ -23,7 +23,6 @@ | |||
23 | 23 | ||
24 | #include <asm/machdep.h> | 24 | #include <asm/machdep.h> |
25 | #include <asm/udbg.h> | 25 | #include <asm/udbg.h> |
26 | #include <asm/ps3.h> | ||
27 | 26 | ||
28 | #include "platform.h" | 27 | #include "platform.h" |
29 | 28 | ||
@@ -111,7 +110,7 @@ static void __init ps3_smp_setup_cpu(int cpu) | |||
111 | BUILD_BUG_ON(PPC_MSG_DEBUGGER_BREAK != 3); | 110 | BUILD_BUG_ON(PPC_MSG_DEBUGGER_BREAK != 3); |
112 | 111 | ||
113 | for (i = 0; i < MSG_COUNT; i++) { | 112 | for (i = 0; i < MSG_COUNT; i++) { |
114 | result = ps3_alloc_event_irq(&virqs[i]); | 113 | result = ps3_alloc_event_irq(cpu, &virqs[i]); |
115 | 114 | ||
116 | if (result) | 115 | if (result) |
117 | continue; | 116 | continue; |
diff --git a/arch/powerpc/platforms/ps3/spu.c b/arch/powerpc/platforms/ps3/spu.c index 644532c3b7c4..d1929721b0e4 100644 --- a/arch/powerpc/platforms/ps3/spu.c +++ b/arch/powerpc/platforms/ps3/spu.c | |||
@@ -26,9 +26,10 @@ | |||
26 | 26 | ||
27 | #include <asm/spu.h> | 27 | #include <asm/spu.h> |
28 | #include <asm/spu_priv1.h> | 28 | #include <asm/spu_priv1.h> |
29 | #include <asm/ps3.h> | ||
30 | #include <asm/lv1call.h> | 29 | #include <asm/lv1call.h> |
31 | 30 | ||
31 | #include "platform.h" | ||
32 | |||
32 | /* spu_management_ops */ | 33 | /* spu_management_ops */ |
33 | 34 | ||
34 | /** | 35 | /** |
@@ -50,7 +51,7 @@ enum spe_type { | |||
50 | */ | 51 | */ |
51 | 52 | ||
52 | struct spe_shadow { | 53 | struct spe_shadow { |
53 | u8 padding_0000[0x0140]; | 54 | u8 padding_0140[0x0140]; |
54 | u64 int_status_class0_RW; /* 0x0140 */ | 55 | u64 int_status_class0_RW; /* 0x0140 */ |
55 | u64 int_status_class1_RW; /* 0x0148 */ | 56 | u64 int_status_class1_RW; /* 0x0148 */ |
56 | u64 int_status_class2_RW; /* 0x0150 */ | 57 | u64 int_status_class2_RW; /* 0x0150 */ |
@@ -67,8 +68,7 @@ struct spe_shadow { | |||
67 | u8 padding_0c08[0x0f00-0x0c08]; | 68 | u8 padding_0c08[0x0f00-0x0c08]; |
68 | u64 spe_execution_status; /* 0x0f00 */ | 69 | u64 spe_execution_status; /* 0x0f00 */ |
69 | u8 padding_0f08[0x1000-0x0f08]; | 70 | u8 padding_0f08[0x1000-0x0f08]; |
70 | } __attribute__ ((packed)); | 71 | }; |
71 | |||
72 | 72 | ||
73 | /** | 73 | /** |
74 | * enum spe_ex_state - Logical spe execution state. | 74 | * enum spe_ex_state - Logical spe execution state. |
@@ -268,20 +268,20 @@ static int __init setup_interrupts(struct spu *spu) | |||
268 | { | 268 | { |
269 | int result; | 269 | int result; |
270 | 270 | ||
271 | result = ps3_alloc_spe_irq(spu_pdata(spu)->spe_id, 0, | 271 | result = ps3_alloc_spe_irq(PS3_BINDING_CPU_ANY, spu_pdata(spu)->spe_id, |
272 | &spu->irqs[0]); | 272 | 0, &spu->irqs[0]); |
273 | 273 | ||
274 | if (result) | 274 | if (result) |
275 | goto fail_alloc_0; | 275 | goto fail_alloc_0; |
276 | 276 | ||
277 | result = ps3_alloc_spe_irq(spu_pdata(spu)->spe_id, 1, | 277 | result = ps3_alloc_spe_irq(PS3_BINDING_CPU_ANY, spu_pdata(spu)->spe_id, |
278 | &spu->irqs[1]); | 278 | 1, &spu->irqs[1]); |
279 | 279 | ||
280 | if (result) | 280 | if (result) |
281 | goto fail_alloc_1; | 281 | goto fail_alloc_1; |
282 | 282 | ||
283 | result = ps3_alloc_spe_irq(spu_pdata(spu)->spe_id, 2, | 283 | result = ps3_alloc_spe_irq(PS3_BINDING_CPU_ANY, spu_pdata(spu)->spe_id, |
284 | &spu->irqs[2]); | 284 | 2, &spu->irqs[2]); |
285 | 285 | ||
286 | if (result) | 286 | if (result) |
287 | goto fail_alloc_2; | 287 | goto fail_alloc_2; |
diff --git a/arch/powerpc/platforms/ps3/system-bus.c b/arch/powerpc/platforms/ps3/system-bus.c new file mode 100644 index 000000000000..bce6136cbce7 --- /dev/null +++ b/arch/powerpc/platforms/ps3/system-bus.c | |||
@@ -0,0 +1,382 @@ | |||
1 | /* | ||
2 | * PS3 system bus driver. | ||
3 | * | ||
4 | * Copyright (C) 2006 Sony Computer Entertainment Inc. | ||
5 | * Copyright 2006 Sony Corp. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; version 2 of the License. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
19 | */ | ||
20 | |||
21 | #include <linux/kernel.h> | ||
22 | #include <linux/init.h> | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/dma-mapping.h> | ||
25 | #include <linux/err.h> | ||
26 | |||
27 | #include <asm/udbg.h> | ||
28 | #include <asm/lv1call.h> | ||
29 | #include <asm/firmware.h> | ||
30 | |||
31 | #include "platform.h" | ||
32 | |||
33 | #define dump_mmio_region(_a) _dump_mmio_region(_a, __func__, __LINE__) | ||
34 | static void _dump_mmio_region(const struct ps3_mmio_region* r, | ||
35 | const char* func, int line) | ||
36 | { | ||
37 | pr_debug("%s:%d: dev %u:%u\n", func, line, r->did.bus_id, | ||
38 | r->did.dev_id); | ||
39 | pr_debug("%s:%d: bus_addr %lxh\n", func, line, r->bus_addr); | ||
40 | pr_debug("%s:%d: len %lxh\n", func, line, r->len); | ||
41 | pr_debug("%s:%d: lpar_addr %lxh\n", func, line, r->lpar_addr); | ||
42 | } | ||
43 | |||
44 | int ps3_mmio_region_create(struct ps3_mmio_region *r) | ||
45 | { | ||
46 | int result; | ||
47 | |||
48 | result = lv1_map_device_mmio_region(r->did.bus_id, r->did.dev_id, | ||
49 | r->bus_addr, r->len, r->page_size, &r->lpar_addr); | ||
50 | |||
51 | if (result) { | ||
52 | pr_debug("%s:%d: lv1_map_device_mmio_region failed: %s\n", | ||
53 | __func__, __LINE__, ps3_result(result)); | ||
54 | r->lpar_addr = 0; | ||
55 | } | ||
56 | |||
57 | dump_mmio_region(r); | ||
58 | return result; | ||
59 | } | ||
60 | |||
61 | int ps3_free_mmio_region(struct ps3_mmio_region *r) | ||
62 | { | ||
63 | int result; | ||
64 | |||
65 | result = lv1_unmap_device_mmio_region(r->did.bus_id, r->did.dev_id, | ||
66 | r->lpar_addr); | ||
67 | |||
68 | if (result) | ||
69 | pr_debug("%s:%d: lv1_unmap_device_mmio_region failed: %s\n", | ||
70 | __func__, __LINE__, ps3_result(result)); | ||
71 | |||
72 | r->lpar_addr = 0; | ||
73 | return result; | ||
74 | } | ||
75 | |||
76 | static int ps3_system_bus_match(struct device *_dev, | ||
77 | struct device_driver *_drv) | ||
78 | { | ||
79 | int result; | ||
80 | struct ps3_system_bus_driver *drv = to_ps3_system_bus_driver(_drv); | ||
81 | struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev); | ||
82 | |||
83 | result = dev->match_id == drv->match_id; | ||
84 | |||
85 | pr_info("%s:%d: dev=%u(%s), drv=%u(%s): %s\n", __func__, __LINE__, | ||
86 | dev->match_id, dev->core.bus_id, drv->match_id, drv->core.name, | ||
87 | (result ? "match" : "miss")); | ||
88 | return result; | ||
89 | } | ||
90 | |||
91 | static int ps3_system_bus_probe(struct device *_dev) | ||
92 | { | ||
93 | int result; | ||
94 | struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev); | ||
95 | struct ps3_system_bus_driver *drv = | ||
96 | to_ps3_system_bus_driver(_dev->driver); | ||
97 | |||
98 | result = lv1_open_device(dev->did.bus_id, dev->did.dev_id, 0); | ||
99 | |||
100 | if (result) { | ||
101 | pr_debug("%s:%d: lv1_open_device failed (%d)\n", | ||
102 | __func__, __LINE__, result); | ||
103 | result = -EACCES; | ||
104 | goto clean_none; | ||
105 | } | ||
106 | |||
107 | if (dev->d_region->did.bus_id) { | ||
108 | result = ps3_dma_region_create(dev->d_region); | ||
109 | |||
110 | if (result) { | ||
111 | pr_debug("%s:%d: ps3_dma_region_create failed (%d)\n", | ||
112 | __func__, __LINE__, result); | ||
113 | BUG_ON("check region type"); | ||
114 | result = -EINVAL; | ||
115 | goto clean_device; | ||
116 | } | ||
117 | } | ||
118 | |||
119 | BUG_ON(!drv); | ||
120 | |||
121 | if (drv->probe) | ||
122 | result = drv->probe(dev); | ||
123 | else | ||
124 | pr_info("%s:%d: %s no probe method\n", __func__, __LINE__, | ||
125 | dev->core.bus_id); | ||
126 | |||
127 | if (result) { | ||
128 | pr_debug("%s:%d: drv->probe failed\n", __func__, __LINE__); | ||
129 | goto clean_dma; | ||
130 | } | ||
131 | |||
132 | return result; | ||
133 | |||
134 | clean_dma: | ||
135 | ps3_dma_region_free(dev->d_region); | ||
136 | clean_device: | ||
137 | lv1_close_device(dev->did.bus_id, dev->did.dev_id); | ||
138 | clean_none: | ||
139 | return result; | ||
140 | } | ||
141 | |||
142 | static int ps3_system_bus_remove(struct device *_dev) | ||
143 | { | ||
144 | struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev); | ||
145 | struct ps3_system_bus_driver *drv = | ||
146 | to_ps3_system_bus_driver(_dev->driver); | ||
147 | |||
148 | if (drv->remove) | ||
149 | drv->remove(dev); | ||
150 | else | ||
151 | pr_info("%s:%d: %s no remove method\n", __func__, __LINE__, | ||
152 | dev->core.bus_id); | ||
153 | |||
154 | ps3_dma_region_free(dev->d_region); | ||
155 | ps3_free_mmio_region(dev->m_region); | ||
156 | lv1_close_device(dev->did.bus_id, dev->did.dev_id); | ||
157 | |||
158 | return 0; | ||
159 | } | ||
160 | |||
161 | struct bus_type ps3_system_bus_type = { | ||
162 | .name = "ps3_system_bus", | ||
163 | .match = ps3_system_bus_match, | ||
164 | .probe = ps3_system_bus_probe, | ||
165 | .remove = ps3_system_bus_remove, | ||
166 | }; | ||
167 | |||
168 | int __init ps3_system_bus_init(void) | ||
169 | { | ||
170 | int result; | ||
171 | |||
172 | if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) | ||
173 | return 0; | ||
174 | |||
175 | result = bus_register(&ps3_system_bus_type); | ||
176 | BUG_ON(result); | ||
177 | return result; | ||
178 | } | ||
179 | |||
180 | core_initcall(ps3_system_bus_init); | ||
181 | |||
182 | /* Allocates a contiguous real buffer and creates mappings over it. | ||
183 | * Returns the virtual address of the buffer and sets dma_handle | ||
184 | * to the dma address (mapping) of the first page. | ||
185 | */ | ||
186 | |||
187 | static void * ps3_alloc_coherent(struct device *_dev, size_t size, | ||
188 | dma_addr_t *dma_handle, gfp_t flag) | ||
189 | { | ||
190 | int result; | ||
191 | struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev); | ||
192 | unsigned long virt_addr; | ||
193 | |||
194 | BUG_ON(!dev->d_region->bus_addr); | ||
195 | |||
196 | flag &= ~(__GFP_DMA | __GFP_HIGHMEM); | ||
197 | flag |= __GFP_ZERO; | ||
198 | |||
199 | virt_addr = __get_free_pages(flag, get_order(size)); | ||
200 | |||
201 | if (!virt_addr) { | ||
202 | pr_debug("%s:%d: get_free_pages failed\n", __func__, __LINE__); | ||
203 | goto clean_none; | ||
204 | } | ||
205 | |||
206 | result = ps3_dma_map(dev->d_region, virt_addr, size, dma_handle); | ||
207 | |||
208 | if (result) { | ||
209 | pr_debug("%s:%d: ps3_dma_map failed (%d)\n", | ||
210 | __func__, __LINE__, result); | ||
211 | BUG_ON("check region type"); | ||
212 | goto clean_alloc; | ||
213 | } | ||
214 | |||
215 | return (void*)virt_addr; | ||
216 | |||
217 | clean_alloc: | ||
218 | free_pages(virt_addr, get_order(size)); | ||
219 | clean_none: | ||
220 | dma_handle = NULL; | ||
221 | return NULL; | ||
222 | } | ||
223 | |||
224 | static void ps3_free_coherent(struct device *_dev, size_t size, void *vaddr, | ||
225 | dma_addr_t dma_handle) | ||
226 | { | ||
227 | struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev); | ||
228 | |||
229 | ps3_dma_unmap(dev->d_region, dma_handle, size); | ||
230 | free_pages((unsigned long)vaddr, get_order(size)); | ||
231 | } | ||
232 | |||
233 | /* Creates TCEs for a user provided buffer. The user buffer must be | ||
234 | * contiguous real kernel storage (not vmalloc). The address of the buffer | ||
235 | * passed here is the kernel (virtual) address of the buffer. The buffer | ||
236 | * need not be page aligned, the dma_addr_t returned will point to the same | ||
237 | * byte within the page as vaddr. | ||
238 | */ | ||
239 | |||
240 | static dma_addr_t ps3_map_single(struct device *_dev, void *ptr, size_t size, | ||
241 | enum dma_data_direction direction) | ||
242 | { | ||
243 | struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev); | ||
244 | int result; | ||
245 | unsigned long bus_addr; | ||
246 | |||
247 | result = ps3_dma_map(dev->d_region, (unsigned long)ptr, size, | ||
248 | &bus_addr); | ||
249 | |||
250 | if (result) { | ||
251 | pr_debug("%s:%d: ps3_dma_map failed (%d)\n", | ||
252 | __func__, __LINE__, result); | ||
253 | } | ||
254 | |||
255 | return bus_addr; | ||
256 | } | ||
257 | |||
258 | static void ps3_unmap_single(struct device *_dev, dma_addr_t dma_addr, | ||
259 | size_t size, enum dma_data_direction direction) | ||
260 | { | ||
261 | struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev); | ||
262 | int result; | ||
263 | |||
264 | result = ps3_dma_unmap(dev->d_region, dma_addr, size); | ||
265 | |||
266 | if (result) { | ||
267 | pr_debug("%s:%d: ps3_dma_unmap failed (%d)\n", | ||
268 | __func__, __LINE__, result); | ||
269 | } | ||
270 | } | ||
271 | |||
272 | static int ps3_map_sg(struct device *_dev, struct scatterlist *sg, int nents, | ||
273 | enum dma_data_direction direction) | ||
274 | { | ||
275 | struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev); | ||
276 | int i; | ||
277 | |||
278 | #if defined(CONFIG_PS3_DYNAMIC_DMA) | ||
279 | BUG_ON("do"); | ||
280 | return -EPERM; | ||
281 | #else | ||
282 | for (i = 0; i < nents; i++, sg++) { | ||
283 | int result = ps3_dma_map(dev->d_region, | ||
284 | page_to_phys(sg->page) + sg->offset, sg->length, | ||
285 | &sg->dma_address); | ||
286 | |||
287 | if (result) { | ||
288 | pr_debug("%s:%d: ps3_dma_map failed (%d)\n", | ||
289 | __func__, __LINE__, result); | ||
290 | return -EINVAL; | ||
291 | } | ||
292 | |||
293 | sg->dma_length = sg->length; | ||
294 | } | ||
295 | |||
296 | return nents; | ||
297 | #endif | ||
298 | } | ||
299 | |||
300 | static void ps3_unmap_sg(struct device *_dev, struct scatterlist *sg, | ||
301 | int nents, enum dma_data_direction direction) | ||
302 | { | ||
303 | #if defined(CONFIG_PS3_DYNAMIC_DMA) | ||
304 | BUG_ON("do"); | ||
305 | #endif | ||
306 | } | ||
307 | |||
308 | static int ps3_dma_supported(struct device *_dev, u64 mask) | ||
309 | { | ||
310 | return mask >= DMA_32BIT_MASK; | ||
311 | } | ||
312 | |||
313 | static struct dma_mapping_ops ps3_dma_ops = { | ||
314 | .alloc_coherent = ps3_alloc_coherent, | ||
315 | .free_coherent = ps3_free_coherent, | ||
316 | .map_single = ps3_map_single, | ||
317 | .unmap_single = ps3_unmap_single, | ||
318 | .map_sg = ps3_map_sg, | ||
319 | .unmap_sg = ps3_unmap_sg, | ||
320 | .dma_supported = ps3_dma_supported | ||
321 | }; | ||
322 | |||
323 | /** | ||
324 | * ps3_system_bus_release_device - remove a device from the system bus | ||
325 | */ | ||
326 | |||
327 | static void ps3_system_bus_release_device(struct device *_dev) | ||
328 | { | ||
329 | struct ps3_system_bus_device *dev = to_ps3_system_bus_device(_dev); | ||
330 | kfree(dev); | ||
331 | } | ||
332 | |||
333 | /** | ||
334 | * ps3_system_bus_device_register - add a device to the system bus | ||
335 | * | ||
336 | * ps3_system_bus_device_register() expects the dev object to be allocated | ||
337 | * dynamically by the caller. The system bus takes ownership of the dev | ||
338 | * object and frees the object in ps3_system_bus_release_device(). | ||
339 | */ | ||
340 | |||
341 | int ps3_system_bus_device_register(struct ps3_system_bus_device *dev) | ||
342 | { | ||
343 | int result; | ||
344 | static unsigned int dev_count = 1; | ||
345 | |||
346 | dev->core.parent = NULL; | ||
347 | dev->core.bus = &ps3_system_bus_type; | ||
348 | dev->core.release = ps3_system_bus_release_device; | ||
349 | |||
350 | dev->core.archdata.of_node = NULL; | ||
351 | dev->core.archdata.dma_ops = &ps3_dma_ops; | ||
352 | dev->core.archdata.numa_node = 0; | ||
353 | |||
354 | snprintf(dev->core.bus_id, sizeof(dev->core.bus_id), "sb_%02x", | ||
355 | dev_count++); | ||
356 | |||
357 | pr_debug("%s:%d add %s\n", __func__, __LINE__, dev->core.bus_id); | ||
358 | |||
359 | result = device_register(&dev->core); | ||
360 | return result; | ||
361 | } | ||
362 | |||
363 | EXPORT_SYMBOL_GPL(ps3_system_bus_device_register); | ||
364 | |||
365 | int ps3_system_bus_driver_register(struct ps3_system_bus_driver *drv) | ||
366 | { | ||
367 | int result; | ||
368 | |||
369 | drv->core.bus = &ps3_system_bus_type; | ||
370 | |||
371 | result = driver_register(&drv->core); | ||
372 | return result; | ||
373 | } | ||
374 | |||
375 | EXPORT_SYMBOL_GPL(ps3_system_bus_driver_register); | ||
376 | |||
377 | void ps3_system_bus_driver_unregister(struct ps3_system_bus_driver *drv) | ||
378 | { | ||
379 | driver_unregister(&drv->core); | ||
380 | } | ||
381 | |||
382 | EXPORT_SYMBOL_GPL(ps3_system_bus_driver_unregister); | ||
diff --git a/arch/powerpc/platforms/pseries/eeh.c b/arch/powerpc/platforms/pseries/eeh.c index da6e5362e7cd..9437f48cc9e7 100644 --- a/arch/powerpc/platforms/pseries/eeh.c +++ b/arch/powerpc/platforms/pseries/eeh.c | |||
@@ -747,6 +747,7 @@ struct eeh_early_enable_info { | |||
747 | /* Enable eeh for the given device node. */ | 747 | /* Enable eeh for the given device node. */ |
748 | static void *early_enable_eeh(struct device_node *dn, void *data) | 748 | static void *early_enable_eeh(struct device_node *dn, void *data) |
749 | { | 749 | { |
750 | unsigned int rets[3]; | ||
750 | struct eeh_early_enable_info *info = data; | 751 | struct eeh_early_enable_info *info = data; |
751 | int ret; | 752 | int ret; |
752 | const char *status = get_property(dn, "status", NULL); | 753 | const char *status = get_property(dn, "status", NULL); |
@@ -803,16 +804,14 @@ static void *early_enable_eeh(struct device_node *dn, void *data) | |||
803 | regs[0], info->buid_hi, info->buid_lo, | 804 | regs[0], info->buid_hi, info->buid_lo, |
804 | EEH_ENABLE); | 805 | EEH_ENABLE); |
805 | 806 | ||
807 | enable = 0; | ||
806 | if (ret == 0) { | 808 | if (ret == 0) { |
807 | eeh_subsystem_enabled = 1; | ||
808 | pdn->eeh_mode |= EEH_MODE_SUPPORTED; | ||
809 | pdn->eeh_config_addr = regs[0]; | 809 | pdn->eeh_config_addr = regs[0]; |
810 | 810 | ||
811 | /* If the newer, better, ibm,get-config-addr-info is supported, | 811 | /* If the newer, better, ibm,get-config-addr-info is supported, |
812 | * then use that instead. */ | 812 | * then use that instead. */ |
813 | pdn->eeh_pe_config_addr = 0; | 813 | pdn->eeh_pe_config_addr = 0; |
814 | if (ibm_get_config_addr_info != RTAS_UNKNOWN_SERVICE) { | 814 | if (ibm_get_config_addr_info != RTAS_UNKNOWN_SERVICE) { |
815 | unsigned int rets[2]; | ||
816 | ret = rtas_call (ibm_get_config_addr_info, 4, 2, rets, | 815 | ret = rtas_call (ibm_get_config_addr_info, 4, 2, rets, |
817 | pdn->eeh_config_addr, | 816 | pdn->eeh_config_addr, |
818 | info->buid_hi, info->buid_lo, | 817 | info->buid_hi, info->buid_lo, |
@@ -820,6 +819,20 @@ static void *early_enable_eeh(struct device_node *dn, void *data) | |||
820 | if (ret == 0) | 819 | if (ret == 0) |
821 | pdn->eeh_pe_config_addr = rets[0]; | 820 | pdn->eeh_pe_config_addr = rets[0]; |
822 | } | 821 | } |
822 | |||
823 | /* Some older systems (Power4) allow the | ||
824 | * ibm,set-eeh-option call to succeed even on nodes | ||
825 | * where EEH is not supported. Verify support | ||
826 | * explicitly. */ | ||
827 | ret = read_slot_reset_state(pdn, rets); | ||
828 | if ((ret == 0) && (rets[1] == 1)) | ||
829 | enable = 1; | ||
830 | } | ||
831 | |||
832 | if (enable) { | ||
833 | eeh_subsystem_enabled = 1; | ||
834 | pdn->eeh_mode |= EEH_MODE_SUPPORTED; | ||
835 | |||
823 | #ifdef DEBUG | 836 | #ifdef DEBUG |
824 | printk(KERN_DEBUG "EEH: %s: eeh enabled, config=%x pe_config=%x\n", | 837 | printk(KERN_DEBUG "EEH: %s: eeh enabled, config=%x pe_config=%x\n", |
825 | dn->full_name, pdn->eeh_config_addr, pdn->eeh_pe_config_addr); | 838 | dn->full_name, pdn->eeh_config_addr, pdn->eeh_pe_config_addr); |
diff --git a/arch/powerpc/platforms/pseries/eeh_driver.c b/arch/powerpc/platforms/pseries/eeh_driver.c index cbd6b0711ab4..a4c0bf84ef2e 100644 --- a/arch/powerpc/platforms/pseries/eeh_driver.c +++ b/arch/powerpc/platforms/pseries/eeh_driver.c | |||
@@ -446,7 +446,8 @@ excess_failures: | |||
446 | */ | 446 | */ |
447 | printk(KERN_ERR | 447 | printk(KERN_ERR |
448 | "EEH: PCI device at location=%s driver=%s pci addr=%s \n" | 448 | "EEH: PCI device at location=%s driver=%s pci addr=%s \n" |
449 | "has failed %d times and has been permanently disabled. \n" | 449 | "has failed %d times in the last hour " |
450 | "and has been permanently disabled. \n" | ||
450 | "Please try reseating this device or replacing it.\n", | 451 | "Please try reseating this device or replacing it.\n", |
451 | location, drv_str, pci_str, frozen_pdn->eeh_freeze_count); | 452 | location, drv_str, pci_str, frozen_pdn->eeh_freeze_count); |
452 | goto perm_error; | 453 | goto perm_error; |
diff --git a/arch/powerpc/platforms/pseries/firmware.c b/arch/powerpc/platforms/pseries/firmware.c index 1c7b2baa5f73..90522e3c9d46 100644 --- a/arch/powerpc/platforms/pseries/firmware.c +++ b/arch/powerpc/platforms/pseries/firmware.c | |||
@@ -59,6 +59,7 @@ firmware_features_table[FIRMWARE_MAX_FEATURES] = { | |||
59 | {FW_FEATURE_XDABR, "hcall-xdabr"}, | 59 | {FW_FEATURE_XDABR, "hcall-xdabr"}, |
60 | {FW_FEATURE_MULTITCE, "hcall-multi-tce"}, | 60 | {FW_FEATURE_MULTITCE, "hcall-multi-tce"}, |
61 | {FW_FEATURE_SPLPAR, "hcall-splpar"}, | 61 | {FW_FEATURE_SPLPAR, "hcall-splpar"}, |
62 | {FW_FEATURE_BULK_REMOVE, "hcall-bulk"}, | ||
62 | }; | 63 | }; |
63 | 64 | ||
64 | /* Build up the firmware features bitmask using the contents of | 65 | /* Build up the firmware features bitmask using the contents of |
diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c index 721436db3ef0..7496005566ef 100644 --- a/arch/powerpc/platforms/pseries/lpar.c +++ b/arch/powerpc/platforms/pseries/lpar.c | |||
@@ -502,23 +502,70 @@ static void pSeries_lpar_hpte_invalidate(unsigned long slot, unsigned long va, | |||
502 | BUG_ON(lpar_rc != H_SUCCESS); | 502 | BUG_ON(lpar_rc != H_SUCCESS); |
503 | } | 503 | } |
504 | 504 | ||
505 | /* Flag bits for H_BULK_REMOVE */ | ||
506 | #define HBR_REQUEST 0x4000000000000000UL | ||
507 | #define HBR_RESPONSE 0x8000000000000000UL | ||
508 | #define HBR_END 0xc000000000000000UL | ||
509 | #define HBR_AVPN 0x0200000000000000UL | ||
510 | #define HBR_ANDCOND 0x0100000000000000UL | ||
511 | |||
505 | /* | 512 | /* |
506 | * Take a spinlock around flushes to avoid bouncing the hypervisor tlbie | 513 | * Take a spinlock around flushes to avoid bouncing the hypervisor tlbie |
507 | * lock. | 514 | * lock. |
508 | */ | 515 | */ |
509 | static void pSeries_lpar_flush_hash_range(unsigned long number, int local) | 516 | static void pSeries_lpar_flush_hash_range(unsigned long number, int local) |
510 | { | 517 | { |
511 | int i; | 518 | unsigned long i, pix, rc; |
512 | unsigned long flags = 0; | 519 | unsigned long flags = 0; |
513 | struct ppc64_tlb_batch *batch = &__get_cpu_var(ppc64_tlb_batch); | 520 | struct ppc64_tlb_batch *batch = &__get_cpu_var(ppc64_tlb_batch); |
514 | int lock_tlbie = !cpu_has_feature(CPU_FTR_LOCKLESS_TLBIE); | 521 | int lock_tlbie = !cpu_has_feature(CPU_FTR_LOCKLESS_TLBIE); |
522 | unsigned long param[9]; | ||
523 | unsigned long va; | ||
524 | unsigned long hash, index, shift, hidx, slot; | ||
525 | real_pte_t pte; | ||
526 | int psize; | ||
515 | 527 | ||
516 | if (lock_tlbie) | 528 | if (lock_tlbie) |
517 | spin_lock_irqsave(&pSeries_lpar_tlbie_lock, flags); | 529 | spin_lock_irqsave(&pSeries_lpar_tlbie_lock, flags); |
518 | 530 | ||
519 | for (i = 0; i < number; i++) | 531 | psize = batch->psize; |
520 | flush_hash_page(batch->vaddr[i], batch->pte[i], | 532 | pix = 0; |
521 | batch->psize, local); | 533 | for (i = 0; i < number; i++) { |
534 | va = batch->vaddr[i]; | ||
535 | pte = batch->pte[i]; | ||
536 | pte_iterate_hashed_subpages(pte, psize, va, index, shift) { | ||
537 | hash = hpt_hash(va, shift); | ||
538 | hidx = __rpte_to_hidx(pte, index); | ||
539 | if (hidx & _PTEIDX_SECONDARY) | ||
540 | hash = ~hash; | ||
541 | slot = (hash & htab_hash_mask) * HPTES_PER_GROUP; | ||
542 | slot += hidx & _PTEIDX_GROUP_IX; | ||
543 | if (!firmware_has_feature(FW_FEATURE_BULK_REMOVE)) { | ||
544 | pSeries_lpar_hpte_invalidate(slot, va, psize, | ||
545 | local); | ||
546 | } else { | ||
547 | param[pix] = HBR_REQUEST | HBR_AVPN | slot; | ||
548 | param[pix+1] = hpte_encode_v(va, psize) & | ||
549 | HPTE_V_AVPN; | ||
550 | pix += 2; | ||
551 | if (pix == 8) { | ||
552 | rc = plpar_hcall9(H_BULK_REMOVE, param, | ||
553 | param[0], param[1], param[2], | ||
554 | param[3], param[4], param[5], | ||
555 | param[6], param[7]); | ||
556 | BUG_ON(rc != H_SUCCESS); | ||
557 | pix = 0; | ||
558 | } | ||
559 | } | ||
560 | } pte_iterate_hashed_end(); | ||
561 | } | ||
562 | if (pix) { | ||
563 | param[pix] = HBR_END; | ||
564 | rc = plpar_hcall9(H_BULK_REMOVE, param, param[0], param[1], | ||
565 | param[2], param[3], param[4], param[5], | ||
566 | param[6], param[7]); | ||
567 | BUG_ON(rc != H_SUCCESS); | ||
568 | } | ||
522 | 569 | ||
523 | if (lock_tlbie) | 570 | if (lock_tlbie) |
524 | spin_unlock_irqrestore(&pSeries_lpar_tlbie_lock, flags); | 571 | spin_unlock_irqrestore(&pSeries_lpar_tlbie_lock, flags); |
diff --git a/arch/powerpc/platforms/pseries/pci.c b/arch/powerpc/platforms/pseries/pci.c index 715db5c89908..c69bd15ced9c 100644 --- a/arch/powerpc/platforms/pseries/pci.c +++ b/arch/powerpc/platforms/pseries/pci.c | |||
@@ -77,7 +77,7 @@ void __init pSeries_final_fixup(void) | |||
77 | 77 | ||
78 | /* | 78 | /* |
79 | * Assume the winbond 82c105 is the IDE controller on a | 79 | * Assume the winbond 82c105 is the IDE controller on a |
80 | * p610. We should probably be more careful in case | 80 | * p610/p615/p630. We should probably be more careful in case |
81 | * someone tries to plug in a similar adapter. | 81 | * someone tries to plug in a similar adapter. |
82 | */ | 82 | */ |
83 | static void fixup_winbond_82c105(struct pci_dev* dev) | 83 | static void fixup_winbond_82c105(struct pci_dev* dev) |