diff options
author | Grant Likely <grant.likely@secretlab.ca> | 2006-12-12 17:13:19 -0500 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2007-02-07 23:29:22 -0500 |
commit | f42963f8646540ac7e5ba016a0ec1cc2e7386b57 (patch) | |
tree | 88820e08dec5442db3da832401095f78c399d3be /arch/powerpc/platforms/52xx/mpc52xx_pci.c | |
parent | d5112a4f31a361409d3c57dc9d58dd69f8014bef (diff) |
[POWERPC] Add mpc52xx/lite5200 PCI support
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
Acked-by: Sylvain Munaut <tnt@246tNt.com>
Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch/powerpc/platforms/52xx/mpc52xx_pci.c')
-rw-r--r-- | arch/powerpc/platforms/52xx/mpc52xx_pci.c | 412 |
1 files changed, 412 insertions, 0 deletions
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 | } | ||