Commit | Line | Data |
---|---|---|
794d15b2 SS |
1 | /* |
2 | * arch/arm/mach-mv78xx0/pcie.c | |
3 | * | |
4 | * PCIe functions for Marvell MV78xx0 SoCs | |
5 | * | |
6 | * This file is licensed under the terms of the GNU General Public | |
7 | * License version 2. This program is licensed "as is" without any | |
8 | * warranty of any kind, whether express or implied. | |
9 | */ | |
10 | ||
11 | #include <linux/kernel.h> | |
12 | #include <linux/pci.h> | |
13 | #include <linux/mbus.h> | |
ba0cda6d | 14 | #include <asm/irq.h> |
794d15b2 | 15 | #include <asm/mach/pci.h> |
6f088f1d | 16 | #include <plat/pcie.h> |
794d15b2 SS |
17 | #include "common.h" |
18 | ||
19 | struct pcie_port { | |
20 | u8 maj; | |
21 | u8 min; | |
22 | u8 root_bus_nr; | |
23 | void __iomem *base; | |
24 | spinlock_t conf_lock; | |
25 | char io_space_name[16]; | |
26 | char mem_space_name[16]; | |
27 | struct resource res[2]; | |
28 | }; | |
29 | ||
30 | static struct pcie_port pcie_port[8]; | |
31 | static int num_pcie_ports; | |
32 | static struct resource pcie_io_space; | |
33 | static struct resource pcie_mem_space; | |
34 | ||
35 | ||
cfdeb637 LB |
36 | void __init mv78xx0_pcie_id(u32 *dev, u32 *rev) |
37 | { | |
38 | *dev = orion_pcie_dev_id((void __iomem *)PCIE00_VIRT_BASE); | |
39 | *rev = orion_pcie_rev((void __iomem *)PCIE00_VIRT_BASE); | |
40 | } | |
41 | ||
794d15b2 SS |
42 | static void __init mv78xx0_pcie_preinit(void) |
43 | { | |
44 | int i; | |
45 | u32 size_each; | |
46 | u32 start; | |
47 | int win; | |
48 | ||
49 | pcie_io_space.name = "PCIe I/O Space"; | |
50 | pcie_io_space.start = MV78XX0_PCIE_IO_PHYS_BASE(0); | |
51 | pcie_io_space.end = | |
52 | MV78XX0_PCIE_IO_PHYS_BASE(0) + MV78XX0_PCIE_IO_SIZE * 8 - 1; | |
53 | pcie_io_space.flags = IORESOURCE_IO; | |
54 | if (request_resource(&iomem_resource, &pcie_io_space)) | |
55 | panic("can't allocate PCIe I/O space"); | |
56 | ||
57 | pcie_mem_space.name = "PCIe MEM Space"; | |
58 | pcie_mem_space.start = MV78XX0_PCIE_MEM_PHYS_BASE; | |
59 | pcie_mem_space.end = | |
60 | MV78XX0_PCIE_MEM_PHYS_BASE + MV78XX0_PCIE_MEM_SIZE - 1; | |
61 | pcie_mem_space.flags = IORESOURCE_MEM; | |
62 | if (request_resource(&iomem_resource, &pcie_mem_space)) | |
63 | panic("can't allocate PCIe MEM space"); | |
64 | ||
65 | for (i = 0; i < num_pcie_ports; i++) { | |
66 | struct pcie_port *pp = pcie_port + i; | |
67 | ||
68 | snprintf(pp->io_space_name, sizeof(pp->io_space_name), | |
69 | "PCIe %d.%d I/O", pp->maj, pp->min); | |
70 | pp->io_space_name[sizeof(pp->io_space_name) - 1] = 0; | |
71 | pp->res[0].name = pp->io_space_name; | |
72 | pp->res[0].start = MV78XX0_PCIE_IO_PHYS_BASE(i); | |
73 | pp->res[0].end = pp->res[0].start + MV78XX0_PCIE_IO_SIZE - 1; | |
74 | pp->res[0].flags = IORESOURCE_IO; | |
75 | ||
76 | snprintf(pp->mem_space_name, sizeof(pp->mem_space_name), | |
77 | "PCIe %d.%d MEM", pp->maj, pp->min); | |
78 | pp->mem_space_name[sizeof(pp->mem_space_name) - 1] = 0; | |
79 | pp->res[1].name = pp->mem_space_name; | |
80 | pp->res[1].flags = IORESOURCE_MEM; | |
81 | } | |
82 | ||
83 | switch (num_pcie_ports) { | |
84 | case 0: | |
85 | size_each = 0; | |
86 | break; | |
87 | ||
88 | case 1: | |
89 | size_each = 0x30000000; | |
90 | break; | |
91 | ||
92 | case 2 ... 3: | |
93 | size_each = 0x10000000; | |
94 | break; | |
95 | ||
96 | case 4 ... 6: | |
97 | size_each = 0x08000000; | |
98 | break; | |
99 | ||
100 | case 7: | |
101 | size_each = 0x04000000; | |
102 | break; | |
103 | ||
104 | default: | |
105 | panic("invalid number of PCIe ports"); | |
106 | } | |
107 | ||
108 | start = MV78XX0_PCIE_MEM_PHYS_BASE; | |
109 | for (i = 0; i < num_pcie_ports; i++) { | |
110 | struct pcie_port *pp = pcie_port + i; | |
111 | ||
112 | pp->res[1].start = start; | |
113 | pp->res[1].end = start + size_each - 1; | |
114 | start += size_each; | |
115 | } | |
116 | ||
117 | for (i = 0; i < num_pcie_ports; i++) { | |
118 | struct pcie_port *pp = pcie_port + i; | |
119 | ||
120 | if (request_resource(&pcie_io_space, &pp->res[0])) | |
121 | panic("can't allocate PCIe I/O sub-space"); | |
122 | ||
123 | if (request_resource(&pcie_mem_space, &pp->res[1])) | |
124 | panic("can't allocate PCIe MEM sub-space"); | |
125 | } | |
126 | ||
127 | win = 0; | |
128 | for (i = 0; i < num_pcie_ports; i++) { | |
129 | struct pcie_port *pp = pcie_port + i; | |
130 | ||
131 | mv78xx0_setup_pcie_io_win(win++, pp->res[0].start, | |
132 | pp->res[0].end - pp->res[0].start + 1, | |
133 | pp->maj, pp->min); | |
134 | ||
135 | mv78xx0_setup_pcie_mem_win(win++, pp->res[1].start, | |
136 | pp->res[1].end - pp->res[1].start + 1, | |
137 | pp->maj, pp->min); | |
138 | } | |
139 | } | |
140 | ||
141 | static int __init mv78xx0_pcie_setup(int nr, struct pci_sys_data *sys) | |
142 | { | |
143 | struct pcie_port *pp; | |
144 | ||
145 | if (nr >= num_pcie_ports) | |
146 | return 0; | |
147 | ||
148 | pp = &pcie_port[nr]; | |
149 | pp->root_bus_nr = sys->busnr; | |
150 | ||
151 | /* | |
152 | * Generic PCIe unit setup. | |
153 | */ | |
154 | orion_pcie_set_local_bus_nr(pp->base, sys->busnr); | |
155 | orion_pcie_setup(pp->base, &mv78xx0_mbus_dram_info); | |
156 | ||
157 | sys->resource[0] = &pp->res[0]; | |
158 | sys->resource[1] = &pp->res[1]; | |
159 | sys->resource[2] = NULL; | |
160 | ||
161 | return 1; | |
162 | } | |
163 | ||
164 | static struct pcie_port *bus_to_port(int bus) | |
165 | { | |
166 | int i; | |
167 | ||
168 | for (i = num_pcie_ports - 1; i >= 0; i--) { | |
169 | int rbus = pcie_port[i].root_bus_nr; | |
170 | if (rbus != -1 && rbus <= bus) | |
171 | break; | |
172 | } | |
173 | ||
174 | return i >= 0 ? pcie_port + i : NULL; | |
175 | } | |
176 | ||
177 | static int pcie_valid_config(struct pcie_port *pp, int bus, int dev) | |
178 | { | |
179 | /* | |
180 | * Don't go out when trying to access nonexisting devices | |
181 | * on the local bus. | |
182 | */ | |
183 | if (bus == pp->root_bus_nr && dev > 1) | |
184 | return 0; | |
185 | ||
186 | return 1; | |
187 | } | |
188 | ||
189 | static int pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, | |
190 | int size, u32 *val) | |
191 | { | |
192 | struct pcie_port *pp = bus_to_port(bus->number); | |
193 | unsigned long flags; | |
194 | int ret; | |
195 | ||
196 | if (pcie_valid_config(pp, bus->number, PCI_SLOT(devfn)) == 0) { | |
197 | *val = 0xffffffff; | |
198 | return PCIBIOS_DEVICE_NOT_FOUND; | |
199 | } | |
200 | ||
201 | spin_lock_irqsave(&pp->conf_lock, flags); | |
202 | ret = orion_pcie_rd_conf(pp->base, bus, devfn, where, size, val); | |
203 | spin_unlock_irqrestore(&pp->conf_lock, flags); | |
204 | ||
205 | return ret; | |
206 | } | |
207 | ||
208 | static int pcie_wr_conf(struct pci_bus *bus, u32 devfn, | |
209 | int where, int size, u32 val) | |
210 | { | |
211 | struct pcie_port *pp = bus_to_port(bus->number); | |
212 | unsigned long flags; | |
213 | int ret; | |
214 | ||
215 | if (pcie_valid_config(pp, bus->number, PCI_SLOT(devfn)) == 0) | |
216 | return PCIBIOS_DEVICE_NOT_FOUND; | |
217 | ||
218 | spin_lock_irqsave(&pp->conf_lock, flags); | |
219 | ret = orion_pcie_wr_conf(pp->base, bus, devfn, where, size, val); | |
220 | spin_unlock_irqrestore(&pp->conf_lock, flags); | |
221 | ||
222 | return ret; | |
223 | } | |
224 | ||
225 | static struct pci_ops pcie_ops = { | |
226 | .read = pcie_rd_conf, | |
227 | .write = pcie_wr_conf, | |
228 | }; | |
229 | ||
230 | static void __devinit rc_pci_fixup(struct pci_dev *dev) | |
231 | { | |
232 | /* | |
233 | * Prevent enumeration of root complex. | |
234 | */ | |
235 | if (dev->bus->parent == NULL && dev->devfn == 0) { | |
236 | int i; | |
237 | ||
238 | for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { | |
239 | dev->resource[i].start = 0; | |
240 | dev->resource[i].end = 0; | |
241 | dev->resource[i].flags = 0; | |
242 | } | |
243 | } | |
244 | } | |
245 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL, PCI_ANY_ID, rc_pci_fixup); | |
246 | ||
247 | static struct pci_bus __init * | |
248 | mv78xx0_pcie_scan_bus(int nr, struct pci_sys_data *sys) | |
249 | { | |
250 | struct pci_bus *bus; | |
251 | ||
252 | if (nr < num_pcie_ports) { | |
253 | bus = pci_scan_bus(sys->busnr, &pcie_ops, sys); | |
254 | } else { | |
255 | bus = NULL; | |
256 | BUG(); | |
257 | } | |
258 | ||
259 | return bus; | |
260 | } | |
261 | ||
262 | static int __init mv78xx0_pcie_map_irq(struct pci_dev *dev, u8 slot, u8 pin) | |
263 | { | |
264 | struct pcie_port *pp = bus_to_port(dev->bus->number); | |
265 | ||
266 | return IRQ_MV78XX0_PCIE_00 + (pp->maj << 2) + pp->min; | |
267 | } | |
268 | ||
269 | static struct hw_pci mv78xx0_pci __initdata = { | |
270 | .nr_controllers = 8, | |
271 | .preinit = mv78xx0_pcie_preinit, | |
272 | .swizzle = pci_std_swizzle, | |
273 | .setup = mv78xx0_pcie_setup, | |
274 | .scan = mv78xx0_pcie_scan_bus, | |
275 | .map_irq = mv78xx0_pcie_map_irq, | |
276 | }; | |
277 | ||
278 | static void __init add_pcie_port(int maj, int min, unsigned long base) | |
279 | { | |
280 | printk(KERN_INFO "MV78xx0 PCIe port %d.%d: ", maj, min); | |
281 | ||
282 | if (orion_pcie_link_up((void __iomem *)base)) { | |
283 | struct pcie_port *pp = &pcie_port[num_pcie_ports++]; | |
284 | ||
285 | printk("link up\n"); | |
286 | ||
287 | pp->maj = maj; | |
288 | pp->min = min; | |
289 | pp->root_bus_nr = -1; | |
290 | pp->base = (void __iomem *)base; | |
291 | spin_lock_init(&pp->conf_lock); | |
292 | memset(pp->res, 0, sizeof(pp->res)); | |
293 | } else { | |
294 | printk("link down, ignoring\n"); | |
295 | } | |
296 | } | |
297 | ||
298 | void __init mv78xx0_pcie_init(int init_port0, int init_port1) | |
299 | { | |
300 | if (init_port0) { | |
301 | add_pcie_port(0, 0, PCIE00_VIRT_BASE); | |
302 | if (!orion_pcie_x4_mode((void __iomem *)PCIE00_VIRT_BASE)) { | |
303 | add_pcie_port(0, 1, PCIE01_VIRT_BASE); | |
304 | add_pcie_port(0, 2, PCIE02_VIRT_BASE); | |
305 | add_pcie_port(0, 3, PCIE03_VIRT_BASE); | |
306 | } | |
307 | } | |
308 | ||
309 | if (init_port1) { | |
310 | add_pcie_port(1, 0, PCIE10_VIRT_BASE); | |
311 | if (!orion_pcie_x4_mode((void __iomem *)PCIE10_VIRT_BASE)) { | |
312 | add_pcie_port(1, 1, PCIE11_VIRT_BASE); | |
313 | add_pcie_port(1, 2, PCIE12_VIRT_BASE); | |
314 | add_pcie_port(1, 3, PCIE13_VIRT_BASE); | |
315 | } | |
316 | } | |
317 | ||
318 | pci_common_init(&mv78xx0_pci); | |
319 | } |