Commit | Line | Data |
---|---|---|
651c74c7 SB |
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> | |
5a0e3ad6 | 13 | #include <linux/slab.h> |
651c74c7 | 14 | #include <linux/mbus.h> |
6e5c11a1 | 15 | #include <asm/irq.h> |
651c74c7 | 16 | #include <asm/mach/pci.h> |
6f088f1d | 17 | #include <plat/pcie.h> |
e8b2b7ba | 18 | #include <mach/bridge-regs.h> |
651c74c7 SB |
19 | #include "common.h" |
20 | ||
0e0cdd37 EC |
21 | void kirkwood_enable_pcie(void) |
22 | { | |
23 | u32 curr = readl(CLOCK_GATING_CTRL); | |
24 | if (!(curr & CGC_PEX0)) | |
25 | writel(curr | CGC_PEX0, CLOCK_GATING_CTRL); | |
26 | } | |
27 | ||
ffd58bd2 SB |
28 | void __init kirkwood_pcie_id(u32 *dev, u32 *rev) |
29 | { | |
0e0cdd37 | 30 | kirkwood_enable_pcie(); |
ffd58bd2 SB |
31 | *dev = orion_pcie_dev_id((void __iomem *)PCIE_VIRT_BASE); |
32 | *rev = orion_pcie_rev((void __iomem *)PCIE_VIRT_BASE); | |
33 | } | |
651c74c7 | 34 | |
ffd58bd2 SB |
35 | struct pcie_port { |
36 | u8 root_bus_nr; | |
37 | void __iomem *base; | |
38 | spinlock_t conf_lock; | |
39 | int irq; | |
40 | struct resource res[2]; | |
41 | }; | |
651c74c7 | 42 | |
ffd58bd2 SB |
43 | static int pcie_port_map[2]; |
44 | static int num_pcie_ports; | |
45 | ||
46 | static inline struct pcie_port *bus_to_port(struct pci_bus *bus) | |
b2b3dc2f | 47 | { |
ffd58bd2 SB |
48 | struct pci_sys_data *sys = bus->sysdata; |
49 | return sys->private_data; | |
b2b3dc2f RS |
50 | } |
51 | ||
ffd58bd2 | 52 | static int pcie_valid_config(struct pcie_port *pp, int bus, int dev) |
651c74c7 SB |
53 | { |
54 | /* | |
55 | * Don't go out when trying to access -- | |
56 | * 1. nonexisting device on local bus | |
57 | * 2. where there's no device connected (no link) | |
58 | */ | |
ffd58bd2 | 59 | if (bus == pp->root_bus_nr && dev == 0) |
651c74c7 SB |
60 | return 1; |
61 | ||
ffd58bd2 | 62 | if (!orion_pcie_link_up(pp->base)) |
651c74c7 SB |
63 | return 0; |
64 | ||
ffd58bd2 | 65 | if (bus == pp->root_bus_nr && dev != 1) |
651c74c7 SB |
66 | return 0; |
67 | ||
68 | return 1; | |
69 | } | |
70 | ||
71 | ||
72 | /* | |
73 | * PCIe config cycles are done by programming the PCIE_CONF_ADDR register | |
74 | * and then reading the PCIE_CONF_DATA register. Need to make sure these | |
75 | * transactions are atomic. | |
76 | */ | |
651c74c7 SB |
77 | |
78 | static int pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where, | |
79 | int size, u32 *val) | |
80 | { | |
ffd58bd2 | 81 | struct pcie_port *pp = bus_to_port(bus); |
651c74c7 SB |
82 | unsigned long flags; |
83 | int ret; | |
84 | ||
ffd58bd2 | 85 | if (pcie_valid_config(pp, bus->number, PCI_SLOT(devfn)) == 0) { |
651c74c7 SB |
86 | *val = 0xffffffff; |
87 | return PCIBIOS_DEVICE_NOT_FOUND; | |
88 | } | |
89 | ||
ffd58bd2 SB |
90 | spin_lock_irqsave(&pp->conf_lock, flags); |
91 | ret = orion_pcie_rd_conf(pp->base, bus, devfn, where, size, val); | |
92 | spin_unlock_irqrestore(&pp->conf_lock, flags); | |
651c74c7 SB |
93 | |
94 | return ret; | |
95 | } | |
96 | ||
97 | static int pcie_wr_conf(struct pci_bus *bus, u32 devfn, | |
98 | int where, int size, u32 val) | |
99 | { | |
ffd58bd2 | 100 | struct pcie_port *pp = bus_to_port(bus); |
651c74c7 SB |
101 | unsigned long flags; |
102 | int ret; | |
103 | ||
ffd58bd2 | 104 | if (pcie_valid_config(pp, bus->number, PCI_SLOT(devfn)) == 0) |
651c74c7 SB |
105 | return PCIBIOS_DEVICE_NOT_FOUND; |
106 | ||
ffd58bd2 SB |
107 | spin_lock_irqsave(&pp->conf_lock, flags); |
108 | ret = orion_pcie_wr_conf(pp->base, bus, devfn, where, size, val); | |
109 | spin_unlock_irqrestore(&pp->conf_lock, flags); | |
651c74c7 SB |
110 | |
111 | return ret; | |
112 | } | |
113 | ||
114 | static struct pci_ops pcie_ops = { | |
115 | .read = pcie_rd_conf, | |
116 | .write = pcie_wr_conf, | |
117 | }; | |
118 | ||
a87182b3 | 119 | static void __init pcie0_ioresources_init(struct pcie_port *pp) |
651c74c7 | 120 | { |
a87182b3 NP |
121 | pp->base = (void __iomem *)PCIE_VIRT_BASE; |
122 | pp->irq = IRQ_KIRKWOOD_PCIE; | |
651c74c7 SB |
123 | |
124 | /* | |
ffd58bd2 | 125 | * IORESOURCE_IO |
651c74c7 | 126 | */ |
ffd58bd2 | 127 | pp->res[0].name = "PCIe 0 I/O Space"; |
e4ff1c39 | 128 | pp->res[0].start = KIRKWOOD_PCIE_IO_BUS_BASE; |
ffd58bd2 SB |
129 | pp->res[0].end = pp->res[0].start + KIRKWOOD_PCIE_IO_SIZE - 1; |
130 | pp->res[0].flags = IORESOURCE_IO; | |
651c74c7 SB |
131 | |
132 | /* | |
ffd58bd2 | 133 | * IORESOURCE_MEM |
651c74c7 | 134 | */ |
ffd58bd2 SB |
135 | pp->res[1].name = "PCIe 0 MEM"; |
136 | pp->res[1].start = KIRKWOOD_PCIE_MEM_PHYS_BASE; | |
137 | pp->res[1].end = pp->res[1].start + KIRKWOOD_PCIE_MEM_SIZE - 1; | |
138 | pp->res[1].flags = IORESOURCE_MEM; | |
ffd58bd2 SB |
139 | } |
140 | ||
a87182b3 | 141 | static void __init pcie1_ioresources_init(struct pcie_port *pp) |
ffd58bd2 | 142 | { |
a87182b3 NP |
143 | pp->base = (void __iomem *)PCIE1_VIRT_BASE; |
144 | pp->irq = IRQ_KIRKWOOD_PCIE1; | |
651c74c7 SB |
145 | |
146 | /* | |
147 | * IORESOURCE_IO | |
148 | */ | |
ffd58bd2 | 149 | pp->res[0].name = "PCIe 1 I/O Space"; |
e4ff1c39 | 150 | pp->res[0].start = KIRKWOOD_PCIE1_IO_BUS_BASE; |
ffd58bd2 SB |
151 | pp->res[0].end = pp->res[0].start + KIRKWOOD_PCIE1_IO_SIZE - 1; |
152 | pp->res[0].flags = IORESOURCE_IO; | |
651c74c7 SB |
153 | |
154 | /* | |
155 | * IORESOURCE_MEM | |
156 | */ | |
ffd58bd2 SB |
157 | pp->res[1].name = "PCIe 1 MEM"; |
158 | pp->res[1].start = KIRKWOOD_PCIE1_MEM_PHYS_BASE; | |
159 | pp->res[1].end = pp->res[1].start + KIRKWOOD_PCIE1_MEM_SIZE - 1; | |
160 | pp->res[1].flags = IORESOURCE_MEM; | |
ffd58bd2 SB |
161 | } |
162 | ||
163 | static int __init kirkwood_pcie_setup(int nr, struct pci_sys_data *sys) | |
164 | { | |
165 | extern unsigned int kirkwood_clk_ctrl; | |
166 | struct pcie_port *pp; | |
167 | int index; | |
168 | ||
169 | if (nr >= num_pcie_ports) | |
170 | return 0; | |
171 | ||
172 | index = pcie_port_map[nr]; | |
173 | printk(KERN_INFO "PCI: bus%d uses PCIe port %d\n", sys->busnr, index); | |
174 | ||
175 | pp = kzalloc(sizeof(*pp), GFP_KERNEL); | |
176 | if (!pp) | |
177 | panic("PCIe: failed to allocate pcie_port data"); | |
178 | sys->private_data = pp; | |
179 | pp->root_bus_nr = sys->busnr; | |
180 | spin_lock_init(&pp->conf_lock); | |
181 | ||
182 | switch (index) { | |
183 | case 0: | |
ffd58bd2 | 184 | kirkwood_clk_ctrl |= CGC_PEX0; |
a87182b3 | 185 | pcie0_ioresources_init(pp); |
ffd58bd2 SB |
186 | break; |
187 | case 1: | |
ffd58bd2 | 188 | kirkwood_clk_ctrl |= CGC_PEX1; |
a87182b3 | 189 | pcie1_ioresources_init(pp); |
ffd58bd2 SB |
190 | break; |
191 | default: | |
a87182b3 | 192 | panic("PCIe setup: invalid controller %d", index); |
ffd58bd2 SB |
193 | } |
194 | ||
a87182b3 NP |
195 | if (request_resource(&ioport_resource, &pp->res[0])) |
196 | panic("Request PCIe%d IO resource failed\n", index); | |
197 | if (request_resource(&iomem_resource, &pp->res[1])) | |
198 | panic("Request PCIe%d Memory resource failed\n", index); | |
199 | ||
200 | sys->resource[0] = &pp->res[0]; | |
201 | sys->resource[1] = &pp->res[1]; | |
202 | sys->resource[2] = NULL; | |
203 | sys->io_offset = 0; | |
204 | ||
ffd58bd2 SB |
205 | /* |
206 | * Generic PCIe unit setup. | |
207 | */ | |
208 | orion_pcie_set_local_bus_nr(pp->base, sys->busnr); | |
209 | ||
210 | orion_pcie_setup(pp->base, &kirkwood_mbus_dram_info); | |
e8b2b7ba | 211 | |
651c74c7 SB |
212 | return 1; |
213 | } | |
214 | ||
215 | static void __devinit rc_pci_fixup(struct pci_dev *dev) | |
216 | { | |
217 | /* | |
218 | * Prevent enumeration of root complex. | |
219 | */ | |
220 | if (dev->bus->parent == NULL && dev->devfn == 0) { | |
221 | int i; | |
222 | ||
223 | for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { | |
224 | dev->resource[i].start = 0; | |
225 | dev->resource[i].end = 0; | |
226 | dev->resource[i].flags = 0; | |
227 | } | |
228 | } | |
229 | } | |
230 | DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_MARVELL, PCI_ANY_ID, rc_pci_fixup); | |
231 | ||
232 | static struct pci_bus __init * | |
233 | kirkwood_pcie_scan_bus(int nr, struct pci_sys_data *sys) | |
234 | { | |
235 | struct pci_bus *bus; | |
236 | ||
ffd58bd2 | 237 | if (nr < num_pcie_ports) { |
651c74c7 SB |
238 | bus = pci_scan_bus(sys->busnr, &pcie_ops, sys); |
239 | } else { | |
240 | bus = NULL; | |
241 | BUG(); | |
242 | } | |
243 | ||
244 | return bus; | |
245 | } | |
246 | ||
247 | static int __init kirkwood_pcie_map_irq(struct pci_dev *dev, u8 slot, u8 pin) | |
248 | { | |
ffd58bd2 SB |
249 | struct pcie_port *pp = bus_to_port(dev->bus); |
250 | ||
251 | return pp->irq; | |
651c74c7 SB |
252 | } |
253 | ||
254 | static struct hw_pci kirkwood_pci __initdata = { | |
651c74c7 SB |
255 | .swizzle = pci_std_swizzle, |
256 | .setup = kirkwood_pcie_setup, | |
257 | .scan = kirkwood_pcie_scan_bus, | |
258 | .map_irq = kirkwood_pcie_map_irq, | |
259 | }; | |
260 | ||
ffd58bd2 SB |
261 | static void __init add_pcie_port(int index, unsigned long base) |
262 | { | |
263 | printk(KERN_INFO "Kirkwood PCIe port %d: ", index); | |
264 | ||
265 | if (orion_pcie_link_up((void __iomem *)base)) { | |
266 | printk(KERN_INFO "link up\n"); | |
267 | pcie_port_map[num_pcie_ports++] = index; | |
268 | } else | |
269 | printk(KERN_INFO "link down, ignoring\n"); | |
270 | } | |
271 | ||
272 | void __init kirkwood_pcie_init(unsigned int portmask) | |
651c74c7 | 273 | { |
ffd58bd2 SB |
274 | if (portmask & KW_PCIE0) |
275 | add_pcie_port(0, PCIE_VIRT_BASE); | |
276 | ||
277 | if (portmask & KW_PCIE1) | |
278 | add_pcie_port(1, PCIE1_VIRT_BASE); | |
279 | ||
280 | kirkwood_pci.nr_controllers = num_pcie_ports; | |
651c74c7 SB |
281 | pci_common_init(&kirkwood_pci); |
282 | } |