aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-mv78xx0/pcie.c
diff options
context:
space:
mode:
authorStanislav Samsonov <samsonov@marvell.com>2008-06-22 16:45:10 -0400
committerLennert Buytenhek <buytenh@marvell.com>2008-06-22 16:45:10 -0400
commit794d15b25df5dda10efba600d6dd6cd74a7aa9cb (patch)
tree10797e90295895994ea3f2363e84e555e40abb97 /arch/arm/mach-mv78xx0/pcie.c
parenta9311cfed241ebcd6b5f9be5c8c6d519bf22f9e7 (diff)
[ARM] add Marvell 78xx0 ARM SoC support
The Marvell Discovery Duo (MV78xx0) is a family of ARM SoCs featuring (depending on the model) one or two Feroceon CPU cores with 512K of L2 cache and VFP coprocessors running at (depending on the model) between 800 MHz and 1.2 GHz, and features a DDR2 controller, two PCIe interfaces that can each run either in x4 or quad x1 mode, three USB 2.0 interfaces, two 3Gb/s SATA II interfaces, a SPI interface, two TWSI interfaces, a crypto accelerator, IDMA/XOR engines, a SPI interface, four UARTs, and depending on the model, two or four gigabit ethernet interfaces. This patch adds basic support for the platform, and allows booting on the MV78x00 development board, with functional UARTs, SATA, PCIe, GigE and USB ports. Signed-off-by: Stanislav Samsonov <samsonov@marvell.com> Signed-off-by: Lennert Buytenhek <buytenh@marvell.com>
Diffstat (limited to 'arch/arm/mach-mv78xx0/pcie.c')
-rw-r--r--arch/arm/mach-mv78xx0/pcie.c312
1 files changed, 312 insertions, 0 deletions
diff --git a/arch/arm/mach-mv78xx0/pcie.c b/arch/arm/mach-mv78xx0/pcie.c
new file mode 100644
index 000000000000..b78e1443159f
--- /dev/null
+++ b/arch/arm/mach-mv78xx0/pcie.c
@@ -0,0 +1,312 @@
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>
14#include <asm/mach/pci.h>
15#include <asm/plat-orion/pcie.h>
16#include "common.h"
17
18struct pcie_port {
19 u8 maj;
20 u8 min;
21 u8 root_bus_nr;
22 void __iomem *base;
23 spinlock_t conf_lock;
24 char io_space_name[16];
25 char mem_space_name[16];
26 struct resource res[2];
27};
28
29static struct pcie_port pcie_port[8];
30static int num_pcie_ports;
31static struct resource pcie_io_space;
32static struct resource pcie_mem_space;
33
34
35static void __init mv78xx0_pcie_preinit(void)
36{
37 int i;
38 u32 size_each;
39 u32 start;
40 int win;
41
42 pcie_io_space.name = "PCIe I/O Space";
43 pcie_io_space.start = MV78XX0_PCIE_IO_PHYS_BASE(0);
44 pcie_io_space.end =
45 MV78XX0_PCIE_IO_PHYS_BASE(0) + MV78XX0_PCIE_IO_SIZE * 8 - 1;
46 pcie_io_space.flags = IORESOURCE_IO;
47 if (request_resource(&iomem_resource, &pcie_io_space))
48 panic("can't allocate PCIe I/O space");
49
50 pcie_mem_space.name = "PCIe MEM Space";
51 pcie_mem_space.start = MV78XX0_PCIE_MEM_PHYS_BASE;
52 pcie_mem_space.end =
53 MV78XX0_PCIE_MEM_PHYS_BASE + MV78XX0_PCIE_MEM_SIZE - 1;
54 pcie_mem_space.flags = IORESOURCE_MEM;
55 if (request_resource(&iomem_resource, &pcie_mem_space))
56 panic("can't allocate PCIe MEM space");
57
58 for (i = 0; i < num_pcie_ports; i++) {
59 struct pcie_port *pp = pcie_port + i;
60
61 snprintf(pp->io_space_name, sizeof(pp->io_space_name),
62 "PCIe %d.%d I/O", pp->maj, pp->min);
63 pp->io_space_name[sizeof(pp->io_space_name) - 1] = 0;
64 pp->res[0].name = pp->io_space_name;
65 pp->res[0].start = MV78XX0_PCIE_IO_PHYS_BASE(i);
66 pp->res[0].end = pp->res[0].start + MV78XX0_PCIE_IO_SIZE - 1;
67 pp->res[0].flags = IORESOURCE_IO;
68
69 snprintf(pp->mem_space_name, sizeof(pp->mem_space_name),
70 "PCIe %d.%d MEM", pp->maj, pp->min);
71 pp->mem_space_name[sizeof(pp->mem_space_name) - 1] = 0;
72 pp->res[1].name = pp->mem_space_name;
73 pp->res[1].flags = IORESOURCE_MEM;
74 }
75
76 switch (num_pcie_ports) {
77 case 0:
78 size_each = 0;
79 break;
80
81 case 1:
82 size_each = 0x30000000;
83 break;
84
85 case 2 ... 3:
86 size_each = 0x10000000;
87 break;
88
89 case 4 ... 6:
90 size_each = 0x08000000;
91 break;
92
93 case 7:
94 size_each = 0x04000000;
95 break;
96
97 default:
98 panic("invalid number of PCIe ports");
99 }
100
101 start = MV78XX0_PCIE_MEM_PHYS_BASE;
102 for (i = 0; i < num_pcie_ports; i++) {
103 struct pcie_port *pp = pcie_port + i;
104
105 pp->res[1].start = start;
106 pp->res[1].end = start + size_each - 1;
107 start += size_each;
108 }
109
110 for (i = 0; i < num_pcie_ports; i++) {
111 struct pcie_port *pp = pcie_port + i;
112
113 if (request_resource(&pcie_io_space, &pp->res[0]))
114 panic("can't allocate PCIe I/O sub-space");
115
116 if (request_resource(&pcie_mem_space, &pp->res[1]))
117 panic("can't allocate PCIe MEM sub-space");
118 }
119
120 win = 0;
121 for (i = 0; i < num_pcie_ports; i++) {
122 struct pcie_port *pp = pcie_port + i;
123
124 mv78xx0_setup_pcie_io_win(win++, pp->res[0].start,
125 pp->res[0].end - pp->res[0].start + 1,
126 pp->maj, pp->min);
127
128 mv78xx0_setup_pcie_mem_win(win++, pp->res[1].start,
129 pp->res[1].end - pp->res[1].start + 1,
130 pp->maj, pp->min);
131 }
132}
133
134static int __init mv78xx0_pcie_setup(int nr, struct pci_sys_data *sys)
135{
136 struct pcie_port *pp;
137
138 if (nr >= num_pcie_ports)
139 return 0;
140
141 pp = &pcie_port[nr];
142 pp->root_bus_nr = sys->busnr;
143
144 /*
145 * Generic PCIe unit setup.
146 */
147 orion_pcie_set_local_bus_nr(pp->base, sys->busnr);
148 orion_pcie_setup(pp->base, &mv78xx0_mbus_dram_info);
149
150 sys->resource[0] = &pp->res[0];
151 sys->resource[1] = &pp->res[1];
152 sys->resource[2] = NULL;
153
154 return 1;
155}
156
157static struct pcie_port *bus_to_port(int bus)
158{
159 int i;
160
161 for (i = num_pcie_ports - 1; i >= 0; i--) {
162 int rbus = pcie_port[i].root_bus_nr;
163 if (rbus != -1 && rbus <= bus)
164 break;
165 }
166
167 return i >= 0 ? pcie_port + i : NULL;
168}
169
170static int pcie_valid_config(struct pcie_port *pp, int bus, int dev)
171{
172 /*
173 * Don't go out when trying to access nonexisting devices
174 * on the local bus.
175 */
176 if (bus == pp->root_bus_nr && dev > 1)
177 return 0;
178
179 return 1;
180}
181
182static int pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
183 int size, u32 *val)
184{
185 struct pcie_port *pp = bus_to_port(bus->number);
186 unsigned long flags;
187 int ret;
188
189 if (pcie_valid_config(pp, bus->number, PCI_SLOT(devfn)) == 0) {
190 *val = 0xffffffff;
191 return PCIBIOS_DEVICE_NOT_FOUND;
192 }
193
194 spin_lock_irqsave(&pp->conf_lock, flags);
195 ret = orion_pcie_rd_conf(pp->base, bus, devfn, where, size, val);
196 spin_unlock_irqrestore(&pp->conf_lock, flags);
197
198 return ret;
199}
200
201static int pcie_wr_conf(struct pci_bus *bus, u32 devfn,
202 int where, int size, u32 val)
203{
204 struct pcie_port *pp = bus_to_port(bus->number);
205 unsigned long flags;
206 int ret;
207
208 if (pcie_valid_config(pp, bus->number, PCI_SLOT(devfn)) == 0)
209 return PCIBIOS_DEVICE_NOT_FOUND;
210
211 spin_lock_irqsave(&pp->conf_lock, flags);
212 ret = orion_pcie_wr_conf(pp->base, bus, devfn, where, size, val);
213 spin_unlock_irqrestore(&pp->conf_lock, flags);
214
215 return ret;
216}
217
218static struct pci_ops pcie_ops = {
219 .read = pcie_rd_conf,
220 .write = pcie_wr_conf,
221};
222
223static void __devinit rc_pci_fixup(struct pci_dev *dev)
224{
225 /*
226 * Prevent enumeration of root complex.
227 */
228 if (dev->bus->parent == NULL && dev->devfn == 0) {
229 int i;
230
231 for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
232 dev->resource[i].start = 0;
233 dev->resource[i].end = 0;
234 dev->resource[i].flags = 0;
235 }
236 }
237}
238DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL, PCI_ANY_ID, rc_pci_fixup);
239
240static struct pci_bus __init *
241mv78xx0_pcie_scan_bus(int nr, struct pci_sys_data *sys)
242{
243 struct pci_bus *bus;
244
245 if (nr < num_pcie_ports) {
246 bus = pci_scan_bus(sys->busnr, &pcie_ops, sys);
247 } else {
248 bus = NULL;
249 BUG();
250 }
251
252 return bus;
253}
254
255static int __init mv78xx0_pcie_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
256{
257 struct pcie_port *pp = bus_to_port(dev->bus->number);
258
259 return IRQ_MV78XX0_PCIE_00 + (pp->maj << 2) + pp->min;
260}
261
262static struct hw_pci mv78xx0_pci __initdata = {
263 .nr_controllers = 8,
264 .preinit = mv78xx0_pcie_preinit,
265 .swizzle = pci_std_swizzle,
266 .setup = mv78xx0_pcie_setup,
267 .scan = mv78xx0_pcie_scan_bus,
268 .map_irq = mv78xx0_pcie_map_irq,
269};
270
271static void __init add_pcie_port(int maj, int min, unsigned long base)
272{
273 printk(KERN_INFO "MV78xx0 PCIe port %d.%d: ", maj, min);
274
275 if (orion_pcie_link_up((void __iomem *)base)) {
276 struct pcie_port *pp = &pcie_port[num_pcie_ports++];
277
278 printk("link up\n");
279
280 pp->maj = maj;
281 pp->min = min;
282 pp->root_bus_nr = -1;
283 pp->base = (void __iomem *)base;
284 spin_lock_init(&pp->conf_lock);
285 memset(pp->res, 0, sizeof(pp->res));
286 } else {
287 printk("link down, ignoring\n");
288 }
289}
290
291void __init mv78xx0_pcie_init(int init_port0, int init_port1)
292{
293 if (init_port0) {
294 add_pcie_port(0, 0, PCIE00_VIRT_BASE);
295 if (!orion_pcie_x4_mode((void __iomem *)PCIE00_VIRT_BASE)) {
296 add_pcie_port(0, 1, PCIE01_VIRT_BASE);
297 add_pcie_port(0, 2, PCIE02_VIRT_BASE);
298 add_pcie_port(0, 3, PCIE03_VIRT_BASE);
299 }
300 }
301
302 if (init_port1) {
303 add_pcie_port(1, 0, PCIE10_VIRT_BASE);
304 if (!orion_pcie_x4_mode((void __iomem *)PCIE10_VIRT_BASE)) {
305 add_pcie_port(1, 1, PCIE11_VIRT_BASE);
306 add_pcie_port(1, 2, PCIE12_VIRT_BASE);
307 add_pcie_port(1, 3, PCIE13_VIRT_BASE);
308 }
309 }
310
311 pci_common_init(&mv78xx0_pci);
312}