blob: 313c96ec7eb1a636e664786d8eef76f88c42c522 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * arch/ppc/syslib/mpc52xx_pci.c
3 *
4 * PCI code for the Freescale MPC52xx embedded CPU.
5 *
6 *
7 * Maintainer : Sylvain Munaut <tnt@246tNt.com>
8 *
9 * Copyright (C) 2004 Sylvain Munaut <tnt@246tNt.com>
10 *
11 * This file is licensed under the terms of the GNU General Public License
12 * version 2. This program is licensed "as is" without any warranty of any
13 * kind, whether express or implied.
14 */
15
16#include <linux/config.h>
17
18#include <asm/pci.h>
19
20#include <asm/mpc52xx.h>
21#include "mpc52xx_pci.h"
22
23#include <asm/delay.h>
Paul Mackerrasfd582ec2005-10-11 22:08:12 +100024#include <asm/machdep.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070025
26
Sylvain Munautdb674ed2006-01-06 00:11:36 -080027/* This macro is defined to activate the workaround for the bug
28 435 of the MPC5200 (L25R). With it activated, we don't do any
29 32 bits configuration access during type-1 cycles */
30#define MPC5200_BUG_435_WORKAROUND
31
32
Linus Torvalds1da177e2005-04-16 15:20:36 -070033static int
34mpc52xx_pci_read_config(struct pci_bus *bus, unsigned int devfn,
35 int offset, int len, u32 *val)
36{
37 struct pci_controller *hose = bus->sysdata;
38 u32 value;
39
40 if (ppc_md.pci_exclude_device)
41 if (ppc_md.pci_exclude_device(bus->number, devfn))
42 return PCIBIOS_DEVICE_NOT_FOUND;
43
44 out_be32(hose->cfg_addr,
45 (1 << 31) |
46 ((bus->number - hose->bus_offset) << 16) |
47 (devfn << 8) |
48 (offset & 0xfc));
Sylvain Munautdb674ed2006-01-06 00:11:36 -080049 mb();
Linus Torvalds1da177e2005-04-16 15:20:36 -070050
Sylvain Munautdb674ed2006-01-06 00:11:36 -080051#ifdef MPC5200_BUG_435_WORKAROUND
52 if (bus->number != hose->bus_offset) {
53 switch (len) {
54 case 1:
55 value = in_8(((u8 __iomem *)hose->cfg_data) + (offset & 3));
56 break;
57 case 2:
58 value = in_le16(((u16 __iomem *)hose->cfg_data) + ((offset>>1) & 1));
59 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -070060
Sylvain Munautdb674ed2006-01-06 00:11:36 -080061 default:
62 value = in_le16((u16 __iomem *)hose->cfg_data) |
63 (in_le16(((u16 __iomem *)hose->cfg_data) + 1) << 16);
64 break;
65 }
66 }
67 else
68#endif
69 {
70 value = in_le32(hose->cfg_data);
71
72 if (len != 4) {
73 value >>= ((offset & 0x3) << 3);
74 value &= 0xffffffff >> (32 - (len << 3));
75 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070076 }
77
78 *val = value;
79
80 out_be32(hose->cfg_addr, 0);
Sylvain Munautdb674ed2006-01-06 00:11:36 -080081 mb();
Linus Torvalds1da177e2005-04-16 15:20:36 -070082
83 return PCIBIOS_SUCCESSFUL;
84}
85
86static int
87mpc52xx_pci_write_config(struct pci_bus *bus, unsigned int devfn,
88 int offset, int len, u32 val)
89{
90 struct pci_controller *hose = bus->sysdata;
91 u32 value, mask;
92
93 if (ppc_md.pci_exclude_device)
94 if (ppc_md.pci_exclude_device(bus->number, devfn))
95 return PCIBIOS_DEVICE_NOT_FOUND;
96
97 out_be32(hose->cfg_addr,
98 (1 << 31) |
99 ((bus->number - hose->bus_offset) << 16) |
100 (devfn << 8) |
101 (offset & 0xfc));
Sylvain Munautdb674ed2006-01-06 00:11:36 -0800102 mb();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103
Sylvain Munautdb674ed2006-01-06 00:11:36 -0800104#ifdef MPC5200_BUG_435_WORKAROUND
105 if (bus->number != hose->bus_offset) {
106 switch (len) {
107 case 1:
108 out_8(((u8 __iomem *)hose->cfg_data) +
109 (offset & 3), val);
110 break;
111 case 2:
112 out_le16(((u16 __iomem *)hose->cfg_data) +
113 ((offset>>1) & 1), val);
114 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115
Sylvain Munautdb674ed2006-01-06 00:11:36 -0800116 default:
117 out_le16((u16 __iomem *)hose->cfg_data,
118 (u16)val);
119 out_le16(((u16 __iomem *)hose->cfg_data) + 1,
120 (u16)(val>>16));
121 break;
122 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123 }
Sylvain Munautdb674ed2006-01-06 00:11:36 -0800124 else
125#endif
126 {
127 if (len != 4) {
128 value = in_le32(hose->cfg_data);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129
Sylvain Munautdb674ed2006-01-06 00:11:36 -0800130 offset = (offset & 0x3) << 3;
131 mask = (0xffffffff >> (32 - (len << 3)));
132 mask <<= offset;
133
134 value &= ~mask;
135 val = value | ((val << offset) & mask);
136 }
137
138 out_le32(hose->cfg_data, val);
139 }
140 mb();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141
142 out_be32(hose->cfg_addr, 0);
Sylvain Munautdb674ed2006-01-06 00:11:36 -0800143 mb();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144
145 return PCIBIOS_SUCCESSFUL;
146}
147
148static struct pci_ops mpc52xx_pci_ops = {
149 .read = mpc52xx_pci_read_config,
150 .write = mpc52xx_pci_write_config
151};
152
153
154static void __init
155mpc52xx_pci_setup(struct mpc52xx_pci __iomem *pci_regs)
156{
Sylvain Munaut041cb622006-01-06 00:11:37 -0800157 u32 tmp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158
159 /* Setup control regs */
Sylvain Munaut041cb622006-01-06 00:11:37 -0800160 tmp = in_be32(&pci_regs->scr);
161 tmp |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
162 out_be32(&pci_regs->scr, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163
164 /* Setup windows */
165 out_be32(&pci_regs->iw0btar, MPC52xx_PCI_IWBTAR_TRANSLATION(
166 MPC52xx_PCI_MEM_START + MPC52xx_PCI_MEM_OFFSET,
167 MPC52xx_PCI_MEM_START,
168 MPC52xx_PCI_MEM_SIZE ));
169
170 out_be32(&pci_regs->iw1btar, MPC52xx_PCI_IWBTAR_TRANSLATION(
171 MPC52xx_PCI_MMIO_START + MPC52xx_PCI_MEM_OFFSET,
172 MPC52xx_PCI_MMIO_START,
173 MPC52xx_PCI_MMIO_SIZE ));
174
175 out_be32(&pci_regs->iw2btar, MPC52xx_PCI_IWBTAR_TRANSLATION(
176 MPC52xx_PCI_IO_BASE,
177 MPC52xx_PCI_IO_START,
178 MPC52xx_PCI_IO_SIZE ));
179
180 out_be32(&pci_regs->iwcr, MPC52xx_PCI_IWCR_PACK(
181 ( MPC52xx_PCI_IWCR_ENABLE | /* iw0btar */
182 MPC52xx_PCI_IWCR_READ_MULTI |
183 MPC52xx_PCI_IWCR_MEM ),
184 ( MPC52xx_PCI_IWCR_ENABLE | /* iw1btar */
185 MPC52xx_PCI_IWCR_READ |
186 MPC52xx_PCI_IWCR_MEM ),
187 ( MPC52xx_PCI_IWCR_ENABLE | /* iw2btar */
188 MPC52xx_PCI_IWCR_IO )
189 ));
190
191
192 out_be32(&pci_regs->tbatr0,
193 MPC52xx_PCI_TBATR_ENABLE | MPC52xx_PCI_TARGET_IO );
194 out_be32(&pci_regs->tbatr1,
195 MPC52xx_PCI_TBATR_ENABLE | MPC52xx_PCI_TARGET_MEM );
196
197 out_be32(&pci_regs->tcr, MPC52xx_PCI_TCR_LD);
198
199 /* Reset the exteral bus ( internal PCI controller is NOT resetted ) */
200 /* Not necessary and can be a bad thing if for example the bootloader
201 is displaying a splash screen or ... Just left here for
202 documentation purpose if anyone need it */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203 tmp = in_be32(&pci_regs->gscr);
Sylvain Munaut041cb622006-01-06 00:11:37 -0800204#if 0
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205 out_be32(&pci_regs->gscr, tmp | MPC52xx_PCI_GSCR_PR);
206 udelay(50);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207#endif
Sylvain Munaut041cb622006-01-06 00:11:37 -0800208 out_be32(&pci_regs->gscr, tmp & ~MPC52xx_PCI_GSCR_PR);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209}
210
Sylvain Munautdbeb1982006-01-06 00:11:35 -0800211static void
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212mpc52xx_pci_fixup_resources(struct pci_dev *dev)
213{
214 int i;
215
216 /* We don't rely on boot loader for PCI and resets all
217 devices */
218 for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
219 struct resource *res = &dev->resource[i];
220 if (res->end > res->start) { /* Only valid resources */
221 res->end -= res->start;
222 res->start = 0;
223 res->flags |= IORESOURCE_UNSET;
224 }
225 }
226
227 /* The PCI Host bridge of MPC52xx has a prefetch memory resource
228 fixed to 1Gb. Doesn't fit in the resource system so we remove it */
229 if ( (dev->vendor == PCI_VENDOR_ID_MOTOROLA) &&
230 (dev->device == PCI_DEVICE_ID_MOTOROLA_MPC5200) ) {
231 struct resource *res = &dev->resource[1];
232 res->start = res->end = res->flags = 0;
233 }
234}
235
236void __init
237mpc52xx_find_bridges(void)
238{
239 struct mpc52xx_pci __iomem *pci_regs;
240 struct pci_controller *hose;
241
Paul Mackerras399fe2b2005-10-20 20:57:05 +1000242 pci_assign_all_buses = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243
244 pci_regs = ioremap(MPC52xx_PA(MPC52xx_PCI_OFFSET), MPC52xx_PCI_SIZE);
245 if (!pci_regs)
246 return;
247
248 hose = pcibios_alloc_controller();
249 if (!hose) {
250 iounmap(pci_regs);
251 return;
252 }
253
254 ppc_md.pci_swizzle = common_swizzle;
255 ppc_md.pcibios_fixup_resources = mpc52xx_pci_fixup_resources;
256
257 hose->first_busno = 0;
258 hose->last_busno = 0xff;
259 hose->bus_offset = 0;
260 hose->ops = &mpc52xx_pci_ops;
261
262 mpc52xx_pci_setup(pci_regs);
263
264 hose->pci_mem_offset = MPC52xx_PCI_MEM_OFFSET;
265
Al Viro92a11f92005-04-25 07:55:57 -0700266 hose->io_base_virt = ioremap(MPC52xx_PCI_IO_BASE, MPC52xx_PCI_IO_SIZE);
267 isa_io_base = (unsigned long) hose->io_base_virt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268
269 hose->cfg_addr = &pci_regs->car;
Al Viro92a11f92005-04-25 07:55:57 -0700270 hose->cfg_data = hose->io_base_virt;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271
272 /* Setup resources */
273 pci_init_resource(&hose->mem_resources[0],
274 MPC52xx_PCI_MEM_START,
275 MPC52xx_PCI_MEM_STOP,
276 IORESOURCE_MEM|IORESOURCE_PREFETCH,
277 "PCI prefetchable memory");
278
279 pci_init_resource(&hose->mem_resources[1],
280 MPC52xx_PCI_MMIO_START,
281 MPC52xx_PCI_MMIO_STOP,
282 IORESOURCE_MEM,
283 "PCI memory");
284
285 pci_init_resource(&hose->io_resource,
286 MPC52xx_PCI_IO_START,
287 MPC52xx_PCI_IO_STOP,
288 IORESOURCE_IO,
289 "PCI I/O");
290
291}