Commit | Line | Data |
---|---|---|
4ff40d5a | 1 | /* |
e9b62e8e | 2 | * Atheros AR724X PCI host controller driver |
4ff40d5a RB |
3 | * |
4 | * Copyright (C) 2011 René Bolldorf <xsecute@googlemail.com> | |
e9b62e8e | 5 | * Copyright (C) 2009-2011 Gabor Juhos <juhosg@openwrt.org> |
4ff40d5a RB |
6 | * |
7 | * This program is free software; you can redistribute it and/or modify it | |
8 | * under the terms of the GNU General Public License version 2 as published | |
9 | * by the Free Software Foundation. | |
10 | */ | |
11 | ||
4c07c7df | 12 | #include <linux/irq.h> |
4ff40d5a | 13 | #include <linux/pci.h> |
58d2e9bc GJ |
14 | #include <linux/module.h> |
15 | #include <linux/platform_device.h> | |
6015a856 | 16 | #include <asm/mach-ath79/ath79.h> |
4c07c7df | 17 | #include <asm/mach-ath79/ar71xx_regs.h> |
4ff40d5a | 18 | |
a1dca315 | 19 | #define AR724X_PCI_REG_RESET 0x18 |
4c07c7df GJ |
20 | #define AR724X_PCI_REG_INT_STATUS 0x4c |
21 | #define AR724X_PCI_REG_INT_MASK 0x50 | |
22 | ||
a1dca315 GJ |
23 | #define AR724X_PCI_RESET_LINK_UP BIT(0) |
24 | ||
4c07c7df GJ |
25 | #define AR724X_PCI_INT_DEV0 BIT(14) |
26 | ||
27 | #define AR724X_PCI_IRQ_COUNT 1 | |
28 | ||
6015a856 GJ |
29 | #define AR7240_BAR0_WAR_VALUE 0xffff |
30 | ||
12401fc2 GJ |
31 | #define AR724X_PCI_CMD_INIT (PCI_COMMAND_MEMORY | \ |
32 | PCI_COMMAND_MASTER | \ | |
33 | PCI_COMMAND_INVALIDATE | \ | |
34 | PCI_COMMAND_PARITY | \ | |
35 | PCI_COMMAND_SERR | \ | |
36 | PCI_COMMAND_FAST_BACK) | |
37 | ||
908339ef GJ |
38 | struct ar724x_pci_controller { |
39 | void __iomem *devcfg_base; | |
40 | void __iomem *ctrl_base; | |
12401fc2 | 41 | void __iomem *crp_base; |
4ff40d5a | 42 | |
908339ef | 43 | int irq; |
8b66d461 | 44 | int irq_base; |
908339ef GJ |
45 | |
46 | bool link_up; | |
47 | bool bar0_is_cached; | |
48 | u32 bar0_value; | |
49 | ||
908339ef | 50 | struct pci_controller pci_controller; |
34b134ae GJ |
51 | struct resource io_res; |
52 | struct resource mem_res; | |
908339ef | 53 | }; |
a1dca315 | 54 | |
908339ef | 55 | static inline bool ar724x_pci_check_link(struct ar724x_pci_controller *apc) |
a1dca315 GJ |
56 | { |
57 | u32 reset; | |
58 | ||
908339ef | 59 | reset = __raw_readl(apc->ctrl_base + AR724X_PCI_REG_RESET); |
a1dca315 GJ |
60 | return reset & AR724X_PCI_RESET_LINK_UP; |
61 | } | |
6015a856 | 62 | |
908339ef GJ |
63 | static inline struct ar724x_pci_controller * |
64 | pci_bus_to_ar724x_controller(struct pci_bus *bus) | |
65 | { | |
66 | struct pci_controller *hose; | |
67 | ||
68 | hose = (struct pci_controller *) bus->sysdata; | |
69 | return container_of(hose, struct ar724x_pci_controller, pci_controller); | |
70 | } | |
71 | ||
12401fc2 GJ |
72 | static int ar724x_pci_local_write(struct ar724x_pci_controller *apc, |
73 | int where, int size, u32 value) | |
74 | { | |
12401fc2 GJ |
75 | void __iomem *base; |
76 | u32 data; | |
77 | int s; | |
78 | ||
79 | WARN_ON(where & (size - 1)); | |
80 | ||
81 | if (!apc->link_up) | |
82 | return PCIBIOS_DEVICE_NOT_FOUND; | |
83 | ||
84 | base = apc->crp_base; | |
12401fc2 GJ |
85 | data = __raw_readl(base + (where & ~3)); |
86 | ||
87 | switch (size) { | |
88 | case 1: | |
89 | s = ((where & 3) * 8); | |
90 | data &= ~(0xff << s); | |
91 | data |= ((value & 0xff) << s); | |
92 | break; | |
93 | case 2: | |
94 | s = ((where & 2) * 8); | |
95 | data &= ~(0xffff << s); | |
96 | data |= ((value & 0xffff) << s); | |
97 | break; | |
98 | case 4: | |
99 | data = value; | |
100 | break; | |
101 | default: | |
12401fc2 GJ |
102 | return PCIBIOS_BAD_REGISTER_NUMBER; |
103 | } | |
104 | ||
105 | __raw_writel(data, base + (where & ~3)); | |
106 | /* flush write */ | |
107 | __raw_readl(base + (where & ~3)); | |
12401fc2 GJ |
108 | |
109 | return PCIBIOS_SUCCESSFUL; | |
110 | } | |
111 | ||
d624bd3c | 112 | static int ar724x_pci_read(struct pci_bus *bus, unsigned int devfn, int where, |
4ff40d5a RB |
113 | int size, uint32_t *value) |
114 | { | |
908339ef | 115 | struct ar724x_pci_controller *apc; |
c198441a | 116 | void __iomem *base; |
64adb6bb | 117 | u32 data; |
4ff40d5a | 118 | |
908339ef GJ |
119 | apc = pci_bus_to_ar724x_controller(bus); |
120 | if (!apc->link_up) | |
a1dca315 GJ |
121 | return PCIBIOS_DEVICE_NOT_FOUND; |
122 | ||
4ff40d5a RB |
123 | if (devfn) |
124 | return PCIBIOS_DEVICE_NOT_FOUND; | |
125 | ||
908339ef | 126 | base = apc->devcfg_base; |
64adb6bb | 127 | data = __raw_readl(base + (where & ~3)); |
4ff40d5a RB |
128 | |
129 | switch (size) { | |
130 | case 1: | |
64adb6bb GJ |
131 | if (where & 1) |
132 | data >>= 8; | |
133 | if (where & 2) | |
134 | data >>= 16; | |
135 | data &= 0xff; | |
4ff40d5a RB |
136 | break; |
137 | case 2: | |
64adb6bb GJ |
138 | if (where & 2) |
139 | data >>= 16; | |
140 | data &= 0xffff; | |
4ff40d5a RB |
141 | break; |
142 | case 4: | |
4ff40d5a RB |
143 | break; |
144 | default: | |
4ff40d5a RB |
145 | return PCIBIOS_BAD_REGISTER_NUMBER; |
146 | } | |
147 | ||
6015a856 | 148 | if (where == PCI_BASE_ADDRESS_0 && size == 4 && |
908339ef | 149 | apc->bar0_is_cached) { |
6015a856 | 150 | /* use the cached value */ |
908339ef | 151 | *value = apc->bar0_value; |
6015a856 GJ |
152 | } else { |
153 | *value = data; | |
154 | } | |
4ff40d5a RB |
155 | |
156 | return PCIBIOS_SUCCESSFUL; | |
157 | } | |
158 | ||
d624bd3c | 159 | static int ar724x_pci_write(struct pci_bus *bus, unsigned int devfn, int where, |
4ff40d5a RB |
160 | int size, uint32_t value) |
161 | { | |
908339ef | 162 | struct ar724x_pci_controller *apc; |
c198441a | 163 | void __iomem *base; |
64adb6bb GJ |
164 | u32 data; |
165 | int s; | |
4ff40d5a | 166 | |
908339ef GJ |
167 | apc = pci_bus_to_ar724x_controller(bus); |
168 | if (!apc->link_up) | |
a1dca315 GJ |
169 | return PCIBIOS_DEVICE_NOT_FOUND; |
170 | ||
4ff40d5a RB |
171 | if (devfn) |
172 | return PCIBIOS_DEVICE_NOT_FOUND; | |
173 | ||
6015a856 GJ |
174 | if (soc_is_ar7240() && where == PCI_BASE_ADDRESS_0 && size == 4) { |
175 | if (value != 0xffffffff) { | |
176 | /* | |
177 | * WAR for a hw issue. If the BAR0 register of the | |
178 | * device is set to the proper base address, the | |
179 | * memory space of the device is not accessible. | |
180 | * | |
181 | * Cache the intended value so it can be read back, | |
182 | * and write a SoC specific constant value to the | |
183 | * BAR0 register in order to make the device memory | |
184 | * accessible. | |
185 | */ | |
908339ef GJ |
186 | apc->bar0_is_cached = true; |
187 | apc->bar0_value = value; | |
6015a856 GJ |
188 | |
189 | value = AR7240_BAR0_WAR_VALUE; | |
190 | } else { | |
908339ef | 191 | apc->bar0_is_cached = false; |
6015a856 GJ |
192 | } |
193 | } | |
194 | ||
908339ef | 195 | base = apc->devcfg_base; |
64adb6bb | 196 | data = __raw_readl(base + (where & ~3)); |
4ff40d5a RB |
197 | |
198 | switch (size) { | |
199 | case 1: | |
64adb6bb GJ |
200 | s = ((where & 3) * 8); |
201 | data &= ~(0xff << s); | |
202 | data |= ((value & 0xff) << s); | |
4ff40d5a RB |
203 | break; |
204 | case 2: | |
64adb6bb GJ |
205 | s = ((where & 2) * 8); |
206 | data &= ~(0xffff << s); | |
207 | data |= ((value & 0xffff) << s); | |
4ff40d5a RB |
208 | break; |
209 | case 4: | |
64adb6bb | 210 | data = value; |
4ff40d5a RB |
211 | break; |
212 | default: | |
4ff40d5a RB |
213 | return PCIBIOS_BAD_REGISTER_NUMBER; |
214 | } | |
215 | ||
64adb6bb GJ |
216 | __raw_writel(data, base + (where & ~3)); |
217 | /* flush write */ | |
218 | __raw_readl(base + (where & ~3)); | |
4ff40d5a RB |
219 | |
220 | return PCIBIOS_SUCCESSFUL; | |
221 | } | |
222 | ||
d624bd3c GJ |
223 | static struct pci_ops ar724x_pci_ops = { |
224 | .read = ar724x_pci_read, | |
225 | .write = ar724x_pci_write, | |
4ff40d5a RB |
226 | }; |
227 | ||
bd0b9ac4 | 228 | static void ar724x_pci_irq_handler(struct irq_desc *desc) |
4c07c7df | 229 | { |
908339ef | 230 | struct ar724x_pci_controller *apc; |
4c07c7df GJ |
231 | void __iomem *base; |
232 | u32 pending; | |
233 | ||
25aae561 | 234 | apc = irq_desc_get_handler_data(desc); |
908339ef | 235 | base = apc->ctrl_base; |
4c07c7df GJ |
236 | |
237 | pending = __raw_readl(base + AR724X_PCI_REG_INT_STATUS) & | |
238 | __raw_readl(base + AR724X_PCI_REG_INT_MASK); | |
239 | ||
240 | if (pending & AR724X_PCI_INT_DEV0) | |
8b66d461 | 241 | generic_handle_irq(apc->irq_base + 0); |
4c07c7df GJ |
242 | |
243 | else | |
244 | spurious_interrupt(); | |
245 | } | |
246 | ||
247 | static void ar724x_pci_irq_unmask(struct irq_data *d) | |
248 | { | |
908339ef | 249 | struct ar724x_pci_controller *apc; |
4c07c7df | 250 | void __iomem *base; |
8b66d461 | 251 | int offset; |
4c07c7df GJ |
252 | u32 t; |
253 | ||
908339ef GJ |
254 | apc = irq_data_get_irq_chip_data(d); |
255 | base = apc->ctrl_base; | |
8b66d461 | 256 | offset = apc->irq_base - d->irq; |
4c07c7df | 257 | |
8b66d461 GJ |
258 | switch (offset) { |
259 | case 0: | |
4c07c7df GJ |
260 | t = __raw_readl(base + AR724X_PCI_REG_INT_MASK); |
261 | __raw_writel(t | AR724X_PCI_INT_DEV0, | |
262 | base + AR724X_PCI_REG_INT_MASK); | |
263 | /* flush write */ | |
264 | __raw_readl(base + AR724X_PCI_REG_INT_MASK); | |
265 | } | |
266 | } | |
267 | ||
268 | static void ar724x_pci_irq_mask(struct irq_data *d) | |
269 | { | |
908339ef | 270 | struct ar724x_pci_controller *apc; |
4c07c7df | 271 | void __iomem *base; |
8b66d461 | 272 | int offset; |
4c07c7df GJ |
273 | u32 t; |
274 | ||
908339ef GJ |
275 | apc = irq_data_get_irq_chip_data(d); |
276 | base = apc->ctrl_base; | |
8b66d461 | 277 | offset = apc->irq_base - d->irq; |
4c07c7df | 278 | |
8b66d461 GJ |
279 | switch (offset) { |
280 | case 0: | |
4c07c7df GJ |
281 | t = __raw_readl(base + AR724X_PCI_REG_INT_MASK); |
282 | __raw_writel(t & ~AR724X_PCI_INT_DEV0, | |
283 | base + AR724X_PCI_REG_INT_MASK); | |
284 | ||
285 | /* flush write */ | |
286 | __raw_readl(base + AR724X_PCI_REG_INT_MASK); | |
287 | ||
288 | t = __raw_readl(base + AR724X_PCI_REG_INT_STATUS); | |
289 | __raw_writel(t | AR724X_PCI_INT_DEV0, | |
290 | base + AR724X_PCI_REG_INT_STATUS); | |
291 | ||
292 | /* flush write */ | |
293 | __raw_readl(base + AR724X_PCI_REG_INT_STATUS); | |
294 | } | |
295 | } | |
296 | ||
297 | static struct irq_chip ar724x_pci_irq_chip = { | |
298 | .name = "AR724X PCI ", | |
299 | .irq_mask = ar724x_pci_irq_mask, | |
300 | .irq_unmask = ar724x_pci_irq_unmask, | |
301 | .irq_mask_ack = ar724x_pci_irq_mask, | |
302 | }; | |
303 | ||
8b66d461 GJ |
304 | static void ar724x_pci_irq_init(struct ar724x_pci_controller *apc, |
305 | int id) | |
4c07c7df GJ |
306 | { |
307 | void __iomem *base; | |
308 | int i; | |
309 | ||
908339ef | 310 | base = apc->ctrl_base; |
4c07c7df GJ |
311 | |
312 | __raw_writel(0, base + AR724X_PCI_REG_INT_MASK); | |
313 | __raw_writel(0, base + AR724X_PCI_REG_INT_STATUS); | |
314 | ||
8b66d461 | 315 | apc->irq_base = ATH79_PCI_IRQ_BASE + (id * AR724X_PCI_IRQ_COUNT); |
4c07c7df | 316 | |
8b66d461 GJ |
317 | for (i = apc->irq_base; |
318 | i < apc->irq_base + AR724X_PCI_IRQ_COUNT; i++) { | |
4c07c7df GJ |
319 | irq_set_chip_and_handler(i, &ar724x_pci_irq_chip, |
320 | handle_level_irq); | |
908339ef GJ |
321 | irq_set_chip_data(i, apc); |
322 | } | |
4c07c7df | 323 | |
4d3f77d8 TG |
324 | irq_set_chained_handler_and_data(apc->irq, ar724x_pci_irq_handler, |
325 | apc); | |
4c07c7df GJ |
326 | } |
327 | ||
58d2e9bc GJ |
328 | static int ar724x_pci_probe(struct platform_device *pdev) |
329 | { | |
908339ef | 330 | struct ar724x_pci_controller *apc; |
58d2e9bc | 331 | struct resource *res; |
8b66d461 GJ |
332 | int id; |
333 | ||
334 | id = pdev->id; | |
335 | if (id == -1) | |
336 | id = 0; | |
908339ef GJ |
337 | |
338 | apc = devm_kzalloc(&pdev->dev, sizeof(struct ar724x_pci_controller), | |
339 | GFP_KERNEL); | |
340 | if (!apc) | |
341 | return -ENOMEM; | |
58d2e9bc GJ |
342 | |
343 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctrl_base"); | |
f560fabd SMP |
344 | apc->ctrl_base = devm_ioremap_resource(&pdev->dev, res); |
345 | if (IS_ERR(apc->ctrl_base)) | |
346 | return PTR_ERR(apc->ctrl_base); | |
58d2e9bc GJ |
347 | |
348 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg_base"); | |
f560fabd SMP |
349 | apc->devcfg_base = devm_ioremap_resource(&pdev->dev, res); |
350 | if (IS_ERR(apc->devcfg_base)) | |
351 | return PTR_ERR(apc->devcfg_base); | |
58d2e9bc | 352 | |
12401fc2 | 353 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "crp_base"); |
f560fabd SMP |
354 | apc->crp_base = devm_ioremap_resource(&pdev->dev, res); |
355 | if (IS_ERR(apc->crp_base)) | |
356 | return PTR_ERR(apc->crp_base); | |
12401fc2 | 357 | |
908339ef GJ |
358 | apc->irq = platform_get_irq(pdev, 0); |
359 | if (apc->irq < 0) | |
58d2e9bc GJ |
360 | return -EINVAL; |
361 | ||
34b134ae GJ |
362 | res = platform_get_resource_byname(pdev, IORESOURCE_IO, "io_base"); |
363 | if (!res) | |
364 | return -EINVAL; | |
365 | ||
366 | apc->io_res.parent = res; | |
367 | apc->io_res.name = "PCI IO space"; | |
368 | apc->io_res.start = res->start; | |
369 | apc->io_res.end = res->end; | |
370 | apc->io_res.flags = IORESOURCE_IO; | |
371 | ||
372 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mem_base"); | |
373 | if (!res) | |
374 | return -EINVAL; | |
375 | ||
376 | apc->mem_res.parent = res; | |
377 | apc->mem_res.name = "PCI memory space"; | |
378 | apc->mem_res.start = res->start; | |
379 | apc->mem_res.end = res->end; | |
380 | apc->mem_res.flags = IORESOURCE_MEM; | |
381 | ||
908339ef | 382 | apc->pci_controller.pci_ops = &ar724x_pci_ops; |
34b134ae GJ |
383 | apc->pci_controller.io_resource = &apc->io_res; |
384 | apc->pci_controller.mem_resource = &apc->mem_res; | |
908339ef GJ |
385 | |
386 | apc->link_up = ar724x_pci_check_link(apc); | |
387 | if (!apc->link_up) | |
58d2e9bc GJ |
388 | dev_warn(&pdev->dev, "PCIe link is down\n"); |
389 | ||
8b66d461 | 390 | ar724x_pci_irq_init(apc, id); |
58d2e9bc | 391 | |
12401fc2 GJ |
392 | ar724x_pci_local_write(apc, PCI_COMMAND, 4, AR724X_PCI_CMD_INIT); |
393 | ||
908339ef | 394 | register_pci_controller(&apc->pci_controller); |
58d2e9bc GJ |
395 | |
396 | return 0; | |
397 | } | |
398 | ||
399 | static struct platform_driver ar724x_pci_driver = { | |
400 | .probe = ar724x_pci_probe, | |
401 | .driver = { | |
402 | .name = "ar724x-pci", | |
58d2e9bc GJ |
403 | }, |
404 | }; | |
405 | ||
406 | static int __init ar724x_pci_init(void) | |
407 | { | |
408 | return platform_driver_register(&ar724x_pci_driver); | |
409 | } | |
410 | ||
411 | postcore_initcall(ar724x_pci_init); |