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> |
6015a856 | 14 | #include <asm/mach-ath79/ath79.h> |
4c07c7df | 15 | #include <asm/mach-ath79/ar71xx_regs.h> |
659243cc | 16 | #include <asm/mach-ath79/pci.h> |
4ff40d5a | 17 | |
c198441a GJ |
18 | #define AR724X_PCI_CFG_BASE 0x14000000 |
19 | #define AR724X_PCI_CFG_SIZE 0x1000 | |
4c07c7df GJ |
20 | #define AR724X_PCI_CTRL_BASE (AR71XX_APB_BASE + 0x000f0000) |
21 | #define AR724X_PCI_CTRL_SIZE 0x100 | |
22 | ||
d624bd3c GJ |
23 | #define AR724X_PCI_MEM_BASE 0x10000000 |
24 | #define AR724X_PCI_MEM_SIZE 0x08000000 | |
4ff40d5a | 25 | |
a1dca315 | 26 | #define AR724X_PCI_REG_RESET 0x18 |
4c07c7df GJ |
27 | #define AR724X_PCI_REG_INT_STATUS 0x4c |
28 | #define AR724X_PCI_REG_INT_MASK 0x50 | |
29 | ||
a1dca315 GJ |
30 | #define AR724X_PCI_RESET_LINK_UP BIT(0) |
31 | ||
4c07c7df GJ |
32 | #define AR724X_PCI_INT_DEV0 BIT(14) |
33 | ||
34 | #define AR724X_PCI_IRQ_COUNT 1 | |
35 | ||
6015a856 GJ |
36 | #define AR7240_BAR0_WAR_VALUE 0xffff |
37 | ||
d624bd3c | 38 | static DEFINE_SPINLOCK(ar724x_pci_lock); |
c198441a | 39 | static void __iomem *ar724x_pci_devcfg_base; |
4c07c7df | 40 | static void __iomem *ar724x_pci_ctrl_base; |
4ff40d5a | 41 | |
6015a856 GJ |
42 | static u32 ar724x_pci_bar0_value; |
43 | static bool ar724x_pci_bar0_is_cached; | |
a1dca315 GJ |
44 | static bool ar724x_pci_link_up; |
45 | ||
46 | static inline bool ar724x_pci_check_link(void) | |
47 | { | |
48 | u32 reset; | |
49 | ||
50 | reset = __raw_readl(ar724x_pci_ctrl_base + AR724X_PCI_REG_RESET); | |
51 | return reset & AR724X_PCI_RESET_LINK_UP; | |
52 | } | |
6015a856 | 53 | |
d624bd3c | 54 | static int ar724x_pci_read(struct pci_bus *bus, unsigned int devfn, int where, |
4ff40d5a RB |
55 | int size, uint32_t *value) |
56 | { | |
64adb6bb | 57 | unsigned long flags; |
c198441a | 58 | void __iomem *base; |
64adb6bb | 59 | u32 data; |
4ff40d5a | 60 | |
a1dca315 GJ |
61 | if (!ar724x_pci_link_up) |
62 | return PCIBIOS_DEVICE_NOT_FOUND; | |
63 | ||
4ff40d5a RB |
64 | if (devfn) |
65 | return PCIBIOS_DEVICE_NOT_FOUND; | |
66 | ||
c198441a GJ |
67 | base = ar724x_pci_devcfg_base; |
68 | ||
d624bd3c | 69 | spin_lock_irqsave(&ar724x_pci_lock, flags); |
64adb6bb | 70 | data = __raw_readl(base + (where & ~3)); |
4ff40d5a RB |
71 | |
72 | switch (size) { | |
73 | case 1: | |
64adb6bb GJ |
74 | if (where & 1) |
75 | data >>= 8; | |
76 | if (where & 2) | |
77 | data >>= 16; | |
78 | data &= 0xff; | |
4ff40d5a RB |
79 | break; |
80 | case 2: | |
64adb6bb GJ |
81 | if (where & 2) |
82 | data >>= 16; | |
83 | data &= 0xffff; | |
4ff40d5a RB |
84 | break; |
85 | case 4: | |
4ff40d5a RB |
86 | break; |
87 | default: | |
d624bd3c | 88 | spin_unlock_irqrestore(&ar724x_pci_lock, flags); |
4ff40d5a RB |
89 | |
90 | return PCIBIOS_BAD_REGISTER_NUMBER; | |
91 | } | |
92 | ||
d624bd3c | 93 | spin_unlock_irqrestore(&ar724x_pci_lock, flags); |
6015a856 GJ |
94 | |
95 | if (where == PCI_BASE_ADDRESS_0 && size == 4 && | |
96 | ar724x_pci_bar0_is_cached) { | |
97 | /* use the cached value */ | |
98 | *value = ar724x_pci_bar0_value; | |
99 | } else { | |
100 | *value = data; | |
101 | } | |
4ff40d5a RB |
102 | |
103 | return PCIBIOS_SUCCESSFUL; | |
104 | } | |
105 | ||
d624bd3c | 106 | static int ar724x_pci_write(struct pci_bus *bus, unsigned int devfn, int where, |
4ff40d5a RB |
107 | int size, uint32_t value) |
108 | { | |
64adb6bb | 109 | unsigned long flags; |
c198441a | 110 | void __iomem *base; |
64adb6bb GJ |
111 | u32 data; |
112 | int s; | |
4ff40d5a | 113 | |
a1dca315 GJ |
114 | if (!ar724x_pci_link_up) |
115 | return PCIBIOS_DEVICE_NOT_FOUND; | |
116 | ||
4ff40d5a RB |
117 | if (devfn) |
118 | return PCIBIOS_DEVICE_NOT_FOUND; | |
119 | ||
6015a856 GJ |
120 | if (soc_is_ar7240() && where == PCI_BASE_ADDRESS_0 && size == 4) { |
121 | if (value != 0xffffffff) { | |
122 | /* | |
123 | * WAR for a hw issue. If the BAR0 register of the | |
124 | * device is set to the proper base address, the | |
125 | * memory space of the device is not accessible. | |
126 | * | |
127 | * Cache the intended value so it can be read back, | |
128 | * and write a SoC specific constant value to the | |
129 | * BAR0 register in order to make the device memory | |
130 | * accessible. | |
131 | */ | |
132 | ar724x_pci_bar0_is_cached = true; | |
133 | ar724x_pci_bar0_value = value; | |
134 | ||
135 | value = AR7240_BAR0_WAR_VALUE; | |
136 | } else { | |
137 | ar724x_pci_bar0_is_cached = false; | |
138 | } | |
139 | } | |
140 | ||
c198441a GJ |
141 | base = ar724x_pci_devcfg_base; |
142 | ||
d624bd3c | 143 | spin_lock_irqsave(&ar724x_pci_lock, flags); |
64adb6bb | 144 | data = __raw_readl(base + (where & ~3)); |
4ff40d5a RB |
145 | |
146 | switch (size) { | |
147 | case 1: | |
64adb6bb GJ |
148 | s = ((where & 3) * 8); |
149 | data &= ~(0xff << s); | |
150 | data |= ((value & 0xff) << s); | |
4ff40d5a RB |
151 | break; |
152 | case 2: | |
64adb6bb GJ |
153 | s = ((where & 2) * 8); |
154 | data &= ~(0xffff << s); | |
155 | data |= ((value & 0xffff) << s); | |
4ff40d5a RB |
156 | break; |
157 | case 4: | |
64adb6bb | 158 | data = value; |
4ff40d5a RB |
159 | break; |
160 | default: | |
d624bd3c | 161 | spin_unlock_irqrestore(&ar724x_pci_lock, flags); |
4ff40d5a RB |
162 | |
163 | return PCIBIOS_BAD_REGISTER_NUMBER; | |
164 | } | |
165 | ||
64adb6bb GJ |
166 | __raw_writel(data, base + (where & ~3)); |
167 | /* flush write */ | |
168 | __raw_readl(base + (where & ~3)); | |
d624bd3c | 169 | spin_unlock_irqrestore(&ar724x_pci_lock, flags); |
4ff40d5a RB |
170 | |
171 | return PCIBIOS_SUCCESSFUL; | |
172 | } | |
173 | ||
d624bd3c GJ |
174 | static struct pci_ops ar724x_pci_ops = { |
175 | .read = ar724x_pci_read, | |
176 | .write = ar724x_pci_write, | |
4ff40d5a RB |
177 | }; |
178 | ||
d624bd3c | 179 | static struct resource ar724x_io_resource = { |
4ff40d5a RB |
180 | .name = "PCI IO space", |
181 | .start = 0, | |
182 | .end = 0, | |
183 | .flags = IORESOURCE_IO, | |
184 | }; | |
185 | ||
d624bd3c | 186 | static struct resource ar724x_mem_resource = { |
4ff40d5a | 187 | .name = "PCI memory space", |
d624bd3c GJ |
188 | .start = AR724X_PCI_MEM_BASE, |
189 | .end = AR724X_PCI_MEM_BASE + AR724X_PCI_MEM_SIZE - 1, | |
4ff40d5a RB |
190 | .flags = IORESOURCE_MEM, |
191 | }; | |
192 | ||
d624bd3c GJ |
193 | static struct pci_controller ar724x_pci_controller = { |
194 | .pci_ops = &ar724x_pci_ops, | |
195 | .io_resource = &ar724x_io_resource, | |
196 | .mem_resource = &ar724x_mem_resource, | |
4ff40d5a RB |
197 | }; |
198 | ||
4c07c7df GJ |
199 | static void ar724x_pci_irq_handler(unsigned int irq, struct irq_desc *desc) |
200 | { | |
201 | void __iomem *base; | |
202 | u32 pending; | |
203 | ||
204 | base = ar724x_pci_ctrl_base; | |
205 | ||
206 | pending = __raw_readl(base + AR724X_PCI_REG_INT_STATUS) & | |
207 | __raw_readl(base + AR724X_PCI_REG_INT_MASK); | |
208 | ||
209 | if (pending & AR724X_PCI_INT_DEV0) | |
210 | generic_handle_irq(ATH79_PCI_IRQ(0)); | |
211 | ||
212 | else | |
213 | spurious_interrupt(); | |
214 | } | |
215 | ||
216 | static void ar724x_pci_irq_unmask(struct irq_data *d) | |
217 | { | |
218 | void __iomem *base; | |
219 | u32 t; | |
220 | ||
221 | base = ar724x_pci_ctrl_base; | |
222 | ||
223 | switch (d->irq) { | |
224 | case ATH79_PCI_IRQ(0): | |
225 | t = __raw_readl(base + AR724X_PCI_REG_INT_MASK); | |
226 | __raw_writel(t | AR724X_PCI_INT_DEV0, | |
227 | base + AR724X_PCI_REG_INT_MASK); | |
228 | /* flush write */ | |
229 | __raw_readl(base + AR724X_PCI_REG_INT_MASK); | |
230 | } | |
231 | } | |
232 | ||
233 | static void ar724x_pci_irq_mask(struct irq_data *d) | |
234 | { | |
235 | void __iomem *base; | |
236 | u32 t; | |
237 | ||
238 | base = ar724x_pci_ctrl_base; | |
239 | ||
240 | switch (d->irq) { | |
241 | case ATH79_PCI_IRQ(0): | |
242 | t = __raw_readl(base + AR724X_PCI_REG_INT_MASK); | |
243 | __raw_writel(t & ~AR724X_PCI_INT_DEV0, | |
244 | base + AR724X_PCI_REG_INT_MASK); | |
245 | ||
246 | /* flush write */ | |
247 | __raw_readl(base + AR724X_PCI_REG_INT_MASK); | |
248 | ||
249 | t = __raw_readl(base + AR724X_PCI_REG_INT_STATUS); | |
250 | __raw_writel(t | AR724X_PCI_INT_DEV0, | |
251 | base + AR724X_PCI_REG_INT_STATUS); | |
252 | ||
253 | /* flush write */ | |
254 | __raw_readl(base + AR724X_PCI_REG_INT_STATUS); | |
255 | } | |
256 | } | |
257 | ||
258 | static struct irq_chip ar724x_pci_irq_chip = { | |
259 | .name = "AR724X PCI ", | |
260 | .irq_mask = ar724x_pci_irq_mask, | |
261 | .irq_unmask = ar724x_pci_irq_unmask, | |
262 | .irq_mask_ack = ar724x_pci_irq_mask, | |
263 | }; | |
264 | ||
265 | static void __init ar724x_pci_irq_init(int irq) | |
266 | { | |
267 | void __iomem *base; | |
268 | int i; | |
269 | ||
270 | base = ar724x_pci_ctrl_base; | |
271 | ||
272 | __raw_writel(0, base + AR724X_PCI_REG_INT_MASK); | |
273 | __raw_writel(0, base + AR724X_PCI_REG_INT_STATUS); | |
274 | ||
275 | BUILD_BUG_ON(ATH79_PCI_IRQ_COUNT < AR724X_PCI_IRQ_COUNT); | |
276 | ||
277 | for (i = ATH79_PCI_IRQ_BASE; | |
278 | i < ATH79_PCI_IRQ_BASE + AR724X_PCI_IRQ_COUNT; i++) | |
279 | irq_set_chip_and_handler(i, &ar724x_pci_irq_chip, | |
280 | handle_level_irq); | |
281 | ||
282 | irq_set_chained_handler(irq, ar724x_pci_irq_handler); | |
283 | } | |
284 | ||
285 | int __init ar724x_pcibios_init(int irq) | |
4ff40d5a | 286 | { |
4c07c7df GJ |
287 | int ret; |
288 | ||
289 | ret = -ENOMEM; | |
290 | ||
c198441a GJ |
291 | ar724x_pci_devcfg_base = ioremap(AR724X_PCI_CFG_BASE, |
292 | AR724X_PCI_CFG_SIZE); | |
293 | if (ar724x_pci_devcfg_base == NULL) | |
4c07c7df | 294 | goto err; |
c198441a | 295 | |
4c07c7df GJ |
296 | ar724x_pci_ctrl_base = ioremap(AR724X_PCI_CTRL_BASE, |
297 | AR724X_PCI_CTRL_SIZE); | |
298 | if (ar724x_pci_ctrl_base == NULL) | |
299 | goto err_unmap_devcfg; | |
300 | ||
a1dca315 GJ |
301 | ar724x_pci_link_up = ar724x_pci_check_link(); |
302 | if (!ar724x_pci_link_up) | |
303 | pr_warn("ar724x: PCIe link is down\n"); | |
304 | ||
4c07c7df | 305 | ar724x_pci_irq_init(irq); |
d624bd3c | 306 | register_pci_controller(&ar724x_pci_controller); |
4ff40d5a RB |
307 | |
308 | return PCIBIOS_SUCCESSFUL; | |
4c07c7df GJ |
309 | |
310 | err_unmap_devcfg: | |
311 | iounmap(ar724x_pci_devcfg_base); | |
312 | err: | |
313 | return ret; | |
4ff40d5a | 314 | } |