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