diff options
Diffstat (limited to 'arch/arm/mach-kirkwood/pcie.c')
-rw-r--r-- | arch/arm/mach-kirkwood/pcie.c | 180 |
1 files changed, 180 insertions, 0 deletions
diff --git a/arch/arm/mach-kirkwood/pcie.c b/arch/arm/mach-kirkwood/pcie.c new file mode 100644 index 000000000000..8282d0ff84bf --- /dev/null +++ b/arch/arm/mach-kirkwood/pcie.c | |||
@@ -0,0 +1,180 @@ | |||
1 | /* | ||
2 | * arch/arm/mach-kirkwood/pcie.c | ||
3 | * | ||
4 | * PCIe functions for Marvell Kirkwood 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 | |||
18 | |||
19 | #define PCIE_BASE ((void __iomem *)PCIE_VIRT_BASE) | ||
20 | |||
21 | static int pcie_valid_config(int bus, int dev) | ||
22 | { | ||
23 | /* | ||
24 | * Don't go out when trying to access -- | ||
25 | * 1. nonexisting device on local bus | ||
26 | * 2. where there's no device connected (no link) | ||
27 | */ | ||
28 | if (bus == 0 && dev == 0) | ||
29 | return 1; | ||
30 | |||
31 | if (!orion_pcie_link_up(PCIE_BASE)) | ||
32 | return 0; | ||
33 | |||
34 | if (bus == 0 && dev != 1) | ||
35 | return 0; | ||
36 | |||
37 | return 1; | ||
38 | } | ||
39 | |||
40 | |||
41 | /* | ||
42 | * PCIe config cycles are done by programming the PCIE_CONF_ADDR register | ||
43 | * and then reading the PCIE_CONF_DATA register. Need to make sure these | ||
44 | * transactions are atomic. | ||
45 | */ | ||
46 | static DEFINE_SPINLOCK(kirkwood_pcie_lock); | ||
47 | |||
48 | static int pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, | ||
49 | int size, u32 *val) | ||
50 | { | ||
51 | unsigned long flags; | ||
52 | int ret; | ||
53 | |||
54 | if (pcie_valid_config(bus->number, PCI_SLOT(devfn)) == 0) { | ||
55 | *val = 0xffffffff; | ||
56 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
57 | } | ||
58 | |||
59 | spin_lock_irqsave(&kirkwood_pcie_lock, flags); | ||
60 | ret = orion_pcie_rd_conf(PCIE_BASE, bus, devfn, where, size, val); | ||
61 | spin_unlock_irqrestore(&kirkwood_pcie_lock, flags); | ||
62 | |||
63 | return ret; | ||
64 | } | ||
65 | |||
66 | static int pcie_wr_conf(struct pci_bus *bus, u32 devfn, | ||
67 | int where, int size, u32 val) | ||
68 | { | ||
69 | unsigned long flags; | ||
70 | int ret; | ||
71 | |||
72 | if (pcie_valid_config(bus->number, PCI_SLOT(devfn)) == 0) | ||
73 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
74 | |||
75 | spin_lock_irqsave(&kirkwood_pcie_lock, flags); | ||
76 | ret = orion_pcie_wr_conf(PCIE_BASE, bus, devfn, where, size, val); | ||
77 | spin_unlock_irqrestore(&kirkwood_pcie_lock, flags); | ||
78 | |||
79 | return ret; | ||
80 | } | ||
81 | |||
82 | static struct pci_ops pcie_ops = { | ||
83 | .read = pcie_rd_conf, | ||
84 | .write = pcie_wr_conf, | ||
85 | }; | ||
86 | |||
87 | |||
88 | static int kirkwood_pcie_setup(int nr, struct pci_sys_data *sys) | ||
89 | { | ||
90 | struct resource *res; | ||
91 | |||
92 | /* | ||
93 | * Generic PCIe unit setup. | ||
94 | */ | ||
95 | orion_pcie_setup(PCIE_BASE, &kirkwood_mbus_dram_info); | ||
96 | |||
97 | /* | ||
98 | * Request resources. | ||
99 | */ | ||
100 | res = kzalloc(sizeof(struct resource) * 2, GFP_KERNEL); | ||
101 | if (!res) | ||
102 | panic("pcie_setup unable to alloc resources"); | ||
103 | |||
104 | /* | ||
105 | * IORESOURCE_IO | ||
106 | */ | ||
107 | res[0].name = "PCIe I/O Space"; | ||
108 | res[0].flags = IORESOURCE_IO; | ||
109 | res[0].start = KIRKWOOD_PCIE_IO_PHYS_BASE; | ||
110 | res[0].end = res[0].start + KIRKWOOD_PCIE_IO_SIZE - 1; | ||
111 | if (request_resource(&ioport_resource, &res[0])) | ||
112 | panic("Request PCIe IO resource failed\n"); | ||
113 | sys->resource[0] = &res[0]; | ||
114 | |||
115 | /* | ||
116 | * IORESOURCE_MEM | ||
117 | */ | ||
118 | res[1].name = "PCIe Memory Space"; | ||
119 | res[1].flags = IORESOURCE_MEM; | ||
120 | res[1].start = KIRKWOOD_PCIE_MEM_PHYS_BASE; | ||
121 | res[1].end = res[1].start + KIRKWOOD_PCIE_MEM_SIZE - 1; | ||
122 | if (request_resource(&iomem_resource, &res[1])) | ||
123 | panic("Request PCIe Memory resource failed\n"); | ||
124 | sys->resource[1] = &res[1]; | ||
125 | |||
126 | sys->resource[2] = NULL; | ||
127 | sys->io_offset = 0; | ||
128 | |||
129 | return 1; | ||
130 | } | ||
131 | |||
132 | static void __devinit rc_pci_fixup(struct pci_dev *dev) | ||
133 | { | ||
134 | /* | ||
135 | * Prevent enumeration of root complex. | ||
136 | */ | ||
137 | if (dev->bus->parent == NULL && dev->devfn == 0) { | ||
138 | int i; | ||
139 | |||
140 | for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { | ||
141 | dev->resource[i].start = 0; | ||
142 | dev->resource[i].end = 0; | ||
143 | dev->resource[i].flags = 0; | ||
144 | } | ||
145 | } | ||
146 | } | ||
147 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL, PCI_ANY_ID, rc_pci_fixup); | ||
148 | |||
149 | static struct pci_bus __init * | ||
150 | kirkwood_pcie_scan_bus(int nr, struct pci_sys_data *sys) | ||
151 | { | ||
152 | struct pci_bus *bus; | ||
153 | |||
154 | if (nr == 0) { | ||
155 | bus = pci_scan_bus(sys->busnr, &pcie_ops, sys); | ||
156 | } else { | ||
157 | bus = NULL; | ||
158 | BUG(); | ||
159 | } | ||
160 | |||
161 | return bus; | ||
162 | } | ||
163 | |||
164 | static int __init kirkwood_pcie_map_irq(struct pci_dev *dev, u8 slot, u8 pin) | ||
165 | { | ||
166 | return IRQ_KIRKWOOD_PCIE; | ||
167 | } | ||
168 | |||
169 | static struct hw_pci kirkwood_pci __initdata = { | ||
170 | .nr_controllers = 1, | ||
171 | .swizzle = pci_std_swizzle, | ||
172 | .setup = kirkwood_pcie_setup, | ||
173 | .scan = kirkwood_pcie_scan_bus, | ||
174 | .map_irq = kirkwood_pcie_map_irq, | ||
175 | }; | ||
176 | |||
177 | void __init kirkwood_pcie_init(void) | ||
178 | { | ||
179 | pci_common_init(&kirkwood_pci); | ||
180 | } | ||