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> | |
95b80e0a | 13 | #include <linux/mbus.h> |
cc22b4c1 | 14 | #include <video/vga.h> |
ba0cda6d | 15 | #include <asm/irq.h> |
794d15b2 | 16 | #include <asm/mach/pci.h> |
6f088f1d | 17 | #include <plat/pcie.h> |
4c811b99 | 18 | #include "mv78xx0.h" |
794d15b2 SS |
19 | #include "common.h" |
20 | ||
c5d0ecc9 TP |
21 | #define MV78XX0_MBUS_PCIE_MEM_TARGET(port, lane) ((port) ? 8 : 4) |
22 | #define MV78XX0_MBUS_PCIE_MEM_ATTR(port, lane) (0xf8 & ~(0x10 << (lane))) | |
23 | #define MV78XX0_MBUS_PCIE_IO_TARGET(port, lane) ((port) ? 8 : 4) | |
24 | #define MV78XX0_MBUS_PCIE_IO_ATTR(port, lane) (0xf0 & ~(0x10 << (lane))) | |
25 | ||
794d15b2 SS |
26 | struct pcie_port { |
27 | u8 maj; | |
28 | u8 min; | |
29 | u8 root_bus_nr; | |
30 | void __iomem *base; | |
31 | spinlock_t conf_lock; | |
794d15b2 | 32 | char mem_space_name[16]; |
0b9b18e0 | 33 | struct resource res; |
794d15b2 SS |
34 | }; |
35 | ||
36 | static struct pcie_port pcie_port[8]; | |
37 | static int num_pcie_ports; | |
38 | static struct resource pcie_io_space; | |
794d15b2 | 39 | |
cfdeb637 LB |
40 | void __init mv78xx0_pcie_id(u32 *dev, u32 *rev) |
41 | { | |
383b9961 TP |
42 | *dev = orion_pcie_dev_id(PCIE00_VIRT_BASE); |
43 | *rev = orion_pcie_rev(PCIE00_VIRT_BASE); | |
cfdeb637 LB |
44 | } |
45 | ||
0b9b18e0 RH |
46 | u32 pcie_port_size[8] = { |
47 | 0, | |
48 | 0x30000000, | |
49 | 0x10000000, | |
50 | 0x10000000, | |
51 | 0x08000000, | |
52 | 0x08000000, | |
53 | 0x08000000, | |
54 | 0x04000000, | |
55 | }; | |
56 | ||
794d15b2 SS |
57 | static void __init mv78xx0_pcie_preinit(void) |
58 | { | |
59 | int i; | |
60 | u32 size_each; | |
61 | u32 start; | |
794d15b2 SS |
62 | |
63 | pcie_io_space.name = "PCIe I/O Space"; | |
64 | pcie_io_space.start = MV78XX0_PCIE_IO_PHYS_BASE(0); | |
65 | pcie_io_space.end = | |
66 | MV78XX0_PCIE_IO_PHYS_BASE(0) + MV78XX0_PCIE_IO_SIZE * 8 - 1; | |
0b9b18e0 | 67 | pcie_io_space.flags = IORESOURCE_MEM; |
794d15b2 SS |
68 | if (request_resource(&iomem_resource, &pcie_io_space)) |
69 | panic("can't allocate PCIe I/O space"); | |
70 | ||
0b9b18e0 RH |
71 | if (num_pcie_ports > 7) |
72 | panic("invalid number of PCIe ports"); | |
73 | ||
74 | size_each = pcie_port_size[num_pcie_ports]; | |
794d15b2 | 75 | |
0b9b18e0 | 76 | start = MV78XX0_PCIE_MEM_PHYS_BASE; |
794d15b2 SS |
77 | for (i = 0; i < num_pcie_ports; i++) { |
78 | struct pcie_port *pp = pcie_port + i; | |
79 | ||
794d15b2 SS |
80 | snprintf(pp->mem_space_name, sizeof(pp->mem_space_name), |
81 | "PCIe %d.%d MEM", pp->maj, pp->min); | |
82 | pp->mem_space_name[sizeof(pp->mem_space_name) - 1] = 0; | |
0b9b18e0 RH |
83 | pp->res.name = pp->mem_space_name; |
84 | pp->res.flags = IORESOURCE_MEM; | |
85 | pp->res.start = start; | |
86 | pp->res.end = start + size_each - 1; | |
794d15b2 | 87 | start += size_each; |
794d15b2 | 88 | |
0b9b18e0 | 89 | if (request_resource(&iomem_resource, &pp->res)) |
794d15b2 | 90 | panic("can't allocate PCIe MEM sub-space"); |
794d15b2 | 91 | |
c5d0ecc9 TP |
92 | mvebu_mbus_add_window_by_id(MV78XX0_MBUS_PCIE_MEM_TARGET(pp->maj, pp->min), |
93 | MV78XX0_MBUS_PCIE_MEM_ATTR(pp->maj, pp->min), | |
94 | pp->res.start, resource_size(&pp->res)); | |
95 | mvebu_mbus_add_window_remap_by_id(MV78XX0_MBUS_PCIE_IO_TARGET(pp->maj, pp->min), | |
96 | MV78XX0_MBUS_PCIE_IO_ATTR(pp->maj, pp->min), | |
97 | i * SZ_64K, SZ_64K, 0); | |
794d15b2 SS |
98 | } |
99 | } | |
100 | ||
101 | static int __init mv78xx0_pcie_setup(int nr, struct pci_sys_data *sys) | |
102 | { | |
103 | struct pcie_port *pp; | |
104 | ||
105 | if (nr >= num_pcie_ports) | |
106 | return 0; | |
107 | ||
108 | pp = &pcie_port[nr]; | |
43ba990b | 109 | sys->private_data = pp; |
794d15b2 SS |
110 | pp->root_bus_nr = sys->busnr; |
111 | ||
112 | /* | |
113 | * Generic PCIe unit setup. | |
114 | */ | |
115 | orion_pcie_set_local_bus_nr(pp->base, sys->busnr); | |
63a9332b | 116 | orion_pcie_setup(pp->base); |
794d15b2 | 117 | |
0b9b18e0 RH |
118 | pci_ioremap_io(nr * SZ_64K, MV78XX0_PCIE_IO_PHYS_BASE(nr)); |
119 | ||
120 | pci_add_resource_offset(&sys->resources, &pp->res, sys->mem_offset); | |
794d15b2 SS |
121 | |
122 | return 1; | |
123 | } | |
124 | ||
794d15b2 SS |
125 | static int pcie_valid_config(struct pcie_port *pp, int bus, int dev) |
126 | { | |
127 | /* | |
128 | * Don't go out when trying to access nonexisting devices | |
129 | * on the local bus. | |
130 | */ | |
131 | if (bus == pp->root_bus_nr && dev > 1) | |
132 | return 0; | |
133 | ||
134 | return 1; | |
135 | } | |
136 | ||
137 | static int pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, | |
138 | int size, u32 *val) | |
139 | { | |
43ba990b RK |
140 | struct pci_sys_data *sys = bus->sysdata; |
141 | struct pcie_port *pp = sys->private_data; | |
794d15b2 SS |
142 | unsigned long flags; |
143 | int ret; | |
144 | ||
145 | if (pcie_valid_config(pp, bus->number, PCI_SLOT(devfn)) == 0) { | |
146 | *val = 0xffffffff; | |
147 | return PCIBIOS_DEVICE_NOT_FOUND; | |
148 | } | |
149 | ||
150 | spin_lock_irqsave(&pp->conf_lock, flags); | |
151 | ret = orion_pcie_rd_conf(pp->base, bus, devfn, where, size, val); | |
152 | spin_unlock_irqrestore(&pp->conf_lock, flags); | |
153 | ||
154 | return ret; | |
155 | } | |
156 | ||
157 | static int pcie_wr_conf(struct pci_bus *bus, u32 devfn, | |
158 | int where, int size, u32 val) | |
159 | { | |
43ba990b RK |
160 | struct pci_sys_data *sys = bus->sysdata; |
161 | struct pcie_port *pp = sys->private_data; | |
794d15b2 SS |
162 | unsigned long flags; |
163 | int ret; | |
164 | ||
165 | if (pcie_valid_config(pp, bus->number, PCI_SLOT(devfn)) == 0) | |
166 | return PCIBIOS_DEVICE_NOT_FOUND; | |
167 | ||
168 | spin_lock_irqsave(&pp->conf_lock, flags); | |
169 | ret = orion_pcie_wr_conf(pp->base, bus, devfn, where, size, val); | |
170 | spin_unlock_irqrestore(&pp->conf_lock, flags); | |
171 | ||
172 | return ret; | |
173 | } | |
174 | ||
175 | static struct pci_ops pcie_ops = { | |
176 | .read = pcie_rd_conf, | |
177 | .write = pcie_wr_conf, | |
178 | }; | |
179 | ||
351a102d | 180 | static void rc_pci_fixup(struct pci_dev *dev) |
794d15b2 SS |
181 | { |
182 | /* | |
183 | * Prevent enumeration of root complex. | |
184 | */ | |
185 | if (dev->bus->parent == NULL && dev->devfn == 0) { | |
186 | int i; | |
187 | ||
188 | for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { | |
189 | dev->resource[i].start = 0; | |
190 | dev->resource[i].end = 0; | |
191 | dev->resource[i].flags = 0; | |
192 | } | |
193 | } | |
194 | } | |
195 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL, PCI_ANY_ID, rc_pci_fixup); | |
196 | ||
197 | static struct pci_bus __init * | |
198 | mv78xx0_pcie_scan_bus(int nr, struct pci_sys_data *sys) | |
199 | { | |
9e808eb6 | 200 | if (nr >= num_pcie_ports) { |
794d15b2 | 201 | BUG(); |
9e808eb6 | 202 | return NULL; |
794d15b2 SS |
203 | } |
204 | ||
9e808eb6 BH |
205 | return pci_scan_root_bus(NULL, sys->busnr, &pcie_ops, sys, |
206 | &sys->resources); | |
794d15b2 SS |
207 | } |
208 | ||
d5341942 RB |
209 | static int __init mv78xx0_pcie_map_irq(const struct pci_dev *dev, u8 slot, |
210 | u8 pin) | |
794d15b2 | 211 | { |
43ba990b RK |
212 | struct pci_sys_data *sys = dev->bus->sysdata; |
213 | struct pcie_port *pp = sys->private_data; | |
794d15b2 SS |
214 | |
215 | return IRQ_MV78XX0_PCIE_00 + (pp->maj << 2) + pp->min; | |
216 | } | |
217 | ||
218 | static struct hw_pci mv78xx0_pci __initdata = { | |
219 | .nr_controllers = 8, | |
220 | .preinit = mv78xx0_pcie_preinit, | |
794d15b2 SS |
221 | .setup = mv78xx0_pcie_setup, |
222 | .scan = mv78xx0_pcie_scan_bus, | |
223 | .map_irq = mv78xx0_pcie_map_irq, | |
224 | }; | |
225 | ||
383b9961 | 226 | static void __init add_pcie_port(int maj, int min, void __iomem *base) |
794d15b2 SS |
227 | { |
228 | printk(KERN_INFO "MV78xx0 PCIe port %d.%d: ", maj, min); | |
229 | ||
383b9961 | 230 | if (orion_pcie_link_up(base)) { |
794d15b2 SS |
231 | struct pcie_port *pp = &pcie_port[num_pcie_ports++]; |
232 | ||
233 | printk("link up\n"); | |
234 | ||
235 | pp->maj = maj; | |
236 | pp->min = min; | |
237 | pp->root_bus_nr = -1; | |
383b9961 | 238 | pp->base = base; |
794d15b2 | 239 | spin_lock_init(&pp->conf_lock); |
0b9b18e0 | 240 | memset(&pp->res, 0, sizeof(pp->res)); |
794d15b2 SS |
241 | } else { |
242 | printk("link down, ignoring\n"); | |
243 | } | |
244 | } | |
245 | ||
246 | void __init mv78xx0_pcie_init(int init_port0, int init_port1) | |
247 | { | |
cc22b4c1 RH |
248 | vga_base = MV78XX0_PCIE_MEM_PHYS_BASE; |
249 | ||
794d15b2 SS |
250 | if (init_port0) { |
251 | add_pcie_port(0, 0, PCIE00_VIRT_BASE); | |
383b9961 | 252 | if (!orion_pcie_x4_mode(PCIE00_VIRT_BASE)) { |
794d15b2 SS |
253 | add_pcie_port(0, 1, PCIE01_VIRT_BASE); |
254 | add_pcie_port(0, 2, PCIE02_VIRT_BASE); | |
255 | add_pcie_port(0, 3, PCIE03_VIRT_BASE); | |
256 | } | |
257 | } | |
258 | ||
259 | if (init_port1) { | |
260 | add_pcie_port(1, 0, PCIE10_VIRT_BASE); | |
261 | if (!orion_pcie_x4_mode((void __iomem *)PCIE10_VIRT_BASE)) { | |
262 | add_pcie_port(1, 1, PCIE11_VIRT_BASE); | |
263 | add_pcie_port(1, 2, PCIE12_VIRT_BASE); | |
264 | add_pcie_port(1, 3, PCIE13_VIRT_BASE); | |
265 | } | |
266 | } | |
267 | ||
268 | pci_common_init(&mv78xx0_pci); | |
269 | } |