Commit 3ed7a2a702dc0f5bc44d67f27a1a289356b5dc42
Committed by
Ralf Baechle
1 parent
d58eaa7ffd
Exists in
ti-lsk-linux-4.1.y
and in
10 other branches
MIPS: ath25: add AR2315 PCI host controller driver
Add PCI host controller driver and DMA address calculation hook. Signed-off-by: Sergey Ryazanov <ryazanov.s.a@gmail.com> Cc: Linux MIPS <linux-mips@linux-mips.org> Patchwork: https://patchwork.linux-mips.org/patch/8246/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Showing 5 changed files with 597 additions and 3 deletions Side-by-side Diff
arch/mips/ath25/Kconfig
arch/mips/ath25/ar2315.c
... | ... | @@ -19,6 +19,7 @@ |
19 | 19 | #include <linux/bitops.h> |
20 | 20 | #include <linux/irqdomain.h> |
21 | 21 | #include <linux/interrupt.h> |
22 | +#include <linux/platform_device.h> | |
22 | 23 | #include <linux/reboot.h> |
23 | 24 | #include <asm/bootinfo.h> |
24 | 25 | #include <asm/reboot.h> |
... | ... | @@ -133,6 +134,10 @@ |
133 | 134 | |
134 | 135 | if (pending & CAUSEF_IP3) |
135 | 136 | do_IRQ(AR2315_IRQ_WLAN0); |
137 | +#ifdef CONFIG_PCI_AR2315 | |
138 | + else if (pending & CAUSEF_IP5) | |
139 | + do_IRQ(AR2315_IRQ_LCBUS_PCI); | |
140 | +#endif | |
136 | 141 | else if (pending & CAUSEF_IP2) |
137 | 142 | do_IRQ(AR2315_IRQ_MISC); |
138 | 143 | else if (pending & CAUSEF_IP7) |
139 | 144 | |
... | ... | @@ -296,11 +301,63 @@ |
296 | 301 | _machine_restart = ar2315_restart; |
297 | 302 | } |
298 | 303 | |
304 | +#ifdef CONFIG_PCI_AR2315 | |
305 | +static struct resource ar2315_pci_res[] = { | |
306 | + { | |
307 | + .name = "ar2315-pci-ctrl", | |
308 | + .flags = IORESOURCE_MEM, | |
309 | + .start = AR2315_PCI_BASE, | |
310 | + .end = AR2315_PCI_BASE + AR2315_PCI_SIZE - 1, | |
311 | + }, | |
312 | + { | |
313 | + .name = "ar2315-pci-ext", | |
314 | + .flags = IORESOURCE_MEM, | |
315 | + .start = AR2315_PCI_EXT_BASE, | |
316 | + .end = AR2315_PCI_EXT_BASE + AR2315_PCI_EXT_SIZE - 1, | |
317 | + }, | |
318 | + { | |
319 | + .name = "ar2315-pci", | |
320 | + .flags = IORESOURCE_IRQ, | |
321 | + .start = AR2315_IRQ_LCBUS_PCI, | |
322 | + .end = AR2315_IRQ_LCBUS_PCI, | |
323 | + }, | |
324 | +}; | |
325 | +#endif | |
326 | + | |
299 | 327 | void __init ar2315_arch_init(void) |
300 | 328 | { |
301 | 329 | unsigned irq = irq_create_mapping(ar2315_misc_irq_domain, |
302 | 330 | AR2315_MISC_IRQ_UART0); |
303 | 331 | |
304 | 332 | ath25_serial_setup(AR2315_UART0_BASE, irq, ar2315_apb_frequency()); |
333 | + | |
334 | +#ifdef CONFIG_PCI_AR2315 | |
335 | + if (ath25_soc == ATH25_SOC_AR2315) { | |
336 | + /* Reset PCI DMA logic */ | |
337 | + ar2315_rst_reg_mask(AR2315_RESET, 0, AR2315_RESET_PCIDMA); | |
338 | + msleep(20); | |
339 | + ar2315_rst_reg_mask(AR2315_RESET, AR2315_RESET_PCIDMA, 0); | |
340 | + msleep(20); | |
341 | + | |
342 | + /* Configure endians */ | |
343 | + ar2315_rst_reg_mask(AR2315_ENDIAN_CTL, 0, AR2315_CONFIG_PCIAHB | | |
344 | + AR2315_CONFIG_PCIAHB_BRIDGE); | |
345 | + | |
346 | + /* Configure as PCI host with DMA */ | |
347 | + ar2315_rst_reg_write(AR2315_PCICLK, AR2315_PCICLK_PLLC_CLKM | | |
348 | + (AR2315_PCICLK_IN_FREQ_DIV_6 << | |
349 | + AR2315_PCICLK_DIV_S)); | |
350 | + ar2315_rst_reg_mask(AR2315_AHB_ARB_CTL, 0, AR2315_ARB_PCI); | |
351 | + ar2315_rst_reg_mask(AR2315_IF_CTL, AR2315_IF_PCI_CLK_MASK | | |
352 | + AR2315_IF_MASK, AR2315_IF_PCI | | |
353 | + AR2315_IF_PCI_HOST | AR2315_IF_PCI_INTR | | |
354 | + (AR2315_IF_PCI_CLK_OUTPUT_CLK << | |
355 | + AR2315_IF_PCI_CLK_SHIFT)); | |
356 | + | |
357 | + platform_device_register_simple("ar2315-pci", -1, | |
358 | + ar2315_pci_res, | |
359 | + ARRAY_SIZE(ar2315_pci_res)); | |
360 | + } | |
361 | +#endif | |
305 | 362 | } |
arch/mips/include/asm/mach-ath25/dma-coherence.h
... | ... | @@ -12,22 +12,40 @@ |
12 | 12 | |
13 | 13 | #include <linux/device.h> |
14 | 14 | |
15 | +/* | |
16 | + * We need some arbitrary non-zero value to be programmed to the BAR1 register | |
17 | + * of PCI host controller to enable DMA. The same value should be used as the | |
18 | + * offset to calculate the physical address of DMA buffer for PCI devices. | |
19 | + */ | |
20 | +#define AR2315_PCI_HOST_SDRAM_BASEADDR 0x20000000 | |
21 | + | |
22 | +static inline dma_addr_t ath25_dev_offset(struct device *dev) | |
23 | +{ | |
24 | +#ifdef CONFIG_PCI | |
25 | + extern struct bus_type pci_bus_type; | |
26 | + | |
27 | + if (dev && dev->bus == &pci_bus_type) | |
28 | + return AR2315_PCI_HOST_SDRAM_BASEADDR; | |
29 | +#endif | |
30 | + return 0; | |
31 | +} | |
32 | + | |
15 | 33 | static inline dma_addr_t |
16 | 34 | plat_map_dma_mem(struct device *dev, void *addr, size_t size) |
17 | 35 | { |
18 | - return virt_to_phys(addr); | |
36 | + return virt_to_phys(addr) + ath25_dev_offset(dev); | |
19 | 37 | } |
20 | 38 | |
21 | 39 | static inline dma_addr_t |
22 | 40 | plat_map_dma_mem_page(struct device *dev, struct page *page) |
23 | 41 | { |
24 | - return page_to_phys(page); | |
42 | + return page_to_phys(page) + ath25_dev_offset(dev); | |
25 | 43 | } |
26 | 44 | |
27 | 45 | static inline unsigned long |
28 | 46 | plat_dma_addr_to_phys(struct device *dev, dma_addr_t dma_addr) |
29 | 47 | { |
30 | - return dma_addr; | |
48 | + return dma_addr - ath25_dev_offset(dev); | |
31 | 49 | } |
32 | 50 | |
33 | 51 | static inline void |
arch/mips/pci/Makefile
... | ... | @@ -19,6 +19,7 @@ |
19 | 19 | obj-$(CONFIG_BCM63XX) += pci-bcm63xx.o fixup-bcm63xx.o \ |
20 | 20 | ops-bcm63xx.o |
21 | 21 | obj-$(CONFIG_MIPS_ALCHEMY) += pci-alchemy.o |
22 | +obj-$(CONFIG_PCI_AR2315) += pci-ar2315.o | |
22 | 23 | obj-$(CONFIG_SOC_AR71XX) += pci-ar71xx.o |
23 | 24 | obj-$(CONFIG_PCI_AR724X) += pci-ar724x.o |
24 | 25 | obj-$(CONFIG_MIPS_PCI_VIRTIO) += pci-virtio-guest.o |
arch/mips/pci/pci-ar2315.c
1 | +/* | |
2 | + * This program is free software; you can redistribute it and/or | |
3 | + * modify it under the terms of the GNU General Public License | |
4 | + * as published by the Free Software Foundation; either version 2 | |
5 | + * of the License, or (at your option) any later version. | |
6 | + * | |
7 | + * This program is distributed in the hope that it will be useful, | |
8 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
9 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
10 | + * GNU General Public License for more details. | |
11 | + * | |
12 | + * You should have received a copy of the GNU General Public License | |
13 | + * along with this program; if not, see <http://www.gnu.org/licenses/>. | |
14 | + */ | |
15 | + | |
16 | +/** | |
17 | + * Both AR2315 and AR2316 chips have PCI interface unit, which supports DMA | |
18 | + * and interrupt. PCI interface supports MMIO access method, but does not | |
19 | + * seem to support I/O ports. | |
20 | + * | |
21 | + * Read/write operation in the region 0x80000000-0xBFFFFFFF causes | |
22 | + * a memory read/write command on the PCI bus. 30 LSBs of address on | |
23 | + * the bus are taken from memory read/write request and 2 MSBs are | |
24 | + * determined by PCI unit configuration. | |
25 | + * | |
26 | + * To work with the configuration space instead of memory is necessary set | |
27 | + * the CFG_SEL bit in the PCI_MISC_CONFIG register. | |
28 | + * | |
29 | + * Devices on the bus can perform DMA requests via chip BAR1. PCI host | |
30 | + * controller BARs are programmend as if an external device is programmed. | |
31 | + * Which means that during configuration, IDSEL pin of the chip should be | |
32 | + * asserted. | |
33 | + * | |
34 | + * We know (and support) only one board that uses the PCI interface - | |
35 | + * Fonera 2.0g (FON2202). It has a USB EHCI controller connected to the | |
36 | + * AR2315 PCI bus. IDSEL pin of USB controller is connected to AD[13] line | |
37 | + * and IDSEL pin of AR2315 is connected to AD[16] line. | |
38 | + */ | |
39 | + | |
40 | +#include <linux/types.h> | |
41 | +#include <linux/pci.h> | |
42 | +#include <linux/platform_device.h> | |
43 | +#include <linux/kernel.h> | |
44 | +#include <linux/init.h> | |
45 | +#include <linux/mm.h> | |
46 | +#include <linux/delay.h> | |
47 | +#include <linux/bitops.h> | |
48 | +#include <linux/irq.h> | |
49 | +#include <linux/irqdomain.h> | |
50 | +#include <linux/io.h> | |
51 | +#include <asm/paccess.h> | |
52 | + | |
53 | +/* | |
54 | + * PCI Bus Interface Registers | |
55 | + */ | |
56 | +#define AR2315_PCI_1MS_REG 0x0008 | |
57 | + | |
58 | +#define AR2315_PCI_1MS_MASK 0x3FFFF /* # of AHB clk cycles in 1ms */ | |
59 | + | |
60 | +#define AR2315_PCI_MISC_CONFIG 0x000c | |
61 | + | |
62 | +#define AR2315_PCIMISC_TXD_EN 0x00000001 /* Enable TXD for fragments */ | |
63 | +#define AR2315_PCIMISC_CFG_SEL 0x00000002 /* Mem or Config cycles */ | |
64 | +#define AR2315_PCIMISC_GIG_MASK 0x0000000C /* bits 31-30 for pci req */ | |
65 | +#define AR2315_PCIMISC_RST_MODE 0x00000030 | |
66 | +#define AR2315_PCIRST_INPUT 0x00000000 /* 4:5=0 rst is input */ | |
67 | +#define AR2315_PCIRST_LOW 0x00000010 /* 4:5=1 rst to GND */ | |
68 | +#define AR2315_PCIRST_HIGH 0x00000020 /* 4:5=2 rst to VDD */ | |
69 | +#define AR2315_PCIGRANT_EN 0x00000000 /* 6:7=0 early grant en */ | |
70 | +#define AR2315_PCIGRANT_FRAME 0x00000040 /* 6:7=1 grant waits 4 frame */ | |
71 | +#define AR2315_PCIGRANT_IDLE 0x00000080 /* 6:7=2 grant waits 4 idle */ | |
72 | +#define AR2315_PCIGRANT_GAP 0x00000000 /* 6:7=2 grant waits 4 idle */ | |
73 | +#define AR2315_PCICACHE_DIS 0x00001000 /* PCI external access cache | |
74 | + * disable */ | |
75 | + | |
76 | +#define AR2315_PCI_OUT_TSTAMP 0x0010 | |
77 | + | |
78 | +#define AR2315_PCI_UNCACHE_CFG 0x0014 | |
79 | + | |
80 | +#define AR2315_PCI_IN_EN 0x0100 | |
81 | + | |
82 | +#define AR2315_PCI_IN_EN0 0x01 /* Enable chain 0 */ | |
83 | +#define AR2315_PCI_IN_EN1 0x02 /* Enable chain 1 */ | |
84 | +#define AR2315_PCI_IN_EN2 0x04 /* Enable chain 2 */ | |
85 | +#define AR2315_PCI_IN_EN3 0x08 /* Enable chain 3 */ | |
86 | + | |
87 | +#define AR2315_PCI_IN_DIS 0x0104 | |
88 | + | |
89 | +#define AR2315_PCI_IN_DIS0 0x01 /* Disable chain 0 */ | |
90 | +#define AR2315_PCI_IN_DIS1 0x02 /* Disable chain 1 */ | |
91 | +#define AR2315_PCI_IN_DIS2 0x04 /* Disable chain 2 */ | |
92 | +#define AR2315_PCI_IN_DIS3 0x08 /* Disable chain 3 */ | |
93 | + | |
94 | +#define AR2315_PCI_IN_PTR 0x0200 | |
95 | + | |
96 | +#define AR2315_PCI_OUT_EN 0x0400 | |
97 | + | |
98 | +#define AR2315_PCI_OUT_EN0 0x01 /* Enable chain 0 */ | |
99 | + | |
100 | +#define AR2315_PCI_OUT_DIS 0x0404 | |
101 | + | |
102 | +#define AR2315_PCI_OUT_DIS0 0x01 /* Disable chain 0 */ | |
103 | + | |
104 | +#define AR2315_PCI_OUT_PTR 0x0408 | |
105 | + | |
106 | +/* PCI interrupt status (write one to clear) */ | |
107 | +#define AR2315_PCI_ISR 0x0500 | |
108 | + | |
109 | +#define AR2315_PCI_INT_TX 0x00000001 /* Desc In Completed */ | |
110 | +#define AR2315_PCI_INT_TXOK 0x00000002 /* Desc In OK */ | |
111 | +#define AR2315_PCI_INT_TXERR 0x00000004 /* Desc In ERR */ | |
112 | +#define AR2315_PCI_INT_TXEOL 0x00000008 /* Desc In End-of-List */ | |
113 | +#define AR2315_PCI_INT_RX 0x00000010 /* Desc Out Completed */ | |
114 | +#define AR2315_PCI_INT_RXOK 0x00000020 /* Desc Out OK */ | |
115 | +#define AR2315_PCI_INT_RXERR 0x00000040 /* Desc Out ERR */ | |
116 | +#define AR2315_PCI_INT_RXEOL 0x00000080 /* Desc Out EOL */ | |
117 | +#define AR2315_PCI_INT_TXOOD 0x00000200 /* Desc In Out-of-Desc */ | |
118 | +#define AR2315_PCI_INT_DESCMASK 0x0000FFFF /* Desc Mask */ | |
119 | +#define AR2315_PCI_INT_EXT 0x02000000 /* Extern PCI INTA */ | |
120 | +#define AR2315_PCI_INT_ABORT 0x04000000 /* PCI bus abort event */ | |
121 | + | |
122 | +/* PCI interrupt mask */ | |
123 | +#define AR2315_PCI_IMR 0x0504 | |
124 | + | |
125 | +/* Global PCI interrupt enable */ | |
126 | +#define AR2315_PCI_IER 0x0508 | |
127 | + | |
128 | +#define AR2315_PCI_IER_DISABLE 0x00 /* disable pci interrupts */ | |
129 | +#define AR2315_PCI_IER_ENABLE 0x01 /* enable pci interrupts */ | |
130 | + | |
131 | +#define AR2315_PCI_HOST_IN_EN 0x0800 | |
132 | +#define AR2315_PCI_HOST_IN_DIS 0x0804 | |
133 | +#define AR2315_PCI_HOST_IN_PTR 0x0810 | |
134 | +#define AR2315_PCI_HOST_OUT_EN 0x0900 | |
135 | +#define AR2315_PCI_HOST_OUT_DIS 0x0904 | |
136 | +#define AR2315_PCI_HOST_OUT_PTR 0x0908 | |
137 | + | |
138 | +/* | |
139 | + * PCI interrupts, which share IP5 | |
140 | + * Keep ordered according to AR2315_PCI_INT_XXX bits | |
141 | + */ | |
142 | +#define AR2315_PCI_IRQ_EXT 25 | |
143 | +#define AR2315_PCI_IRQ_ABORT 26 | |
144 | +#define AR2315_PCI_IRQ_COUNT 27 | |
145 | + | |
146 | +/* Arbitrary size of memory region to access the configuration space */ | |
147 | +#define AR2315_PCI_CFG_SIZE 0x00100000 | |
148 | + | |
149 | +#define AR2315_PCI_HOST_SLOT 3 | |
150 | +#define AR2315_PCI_HOST_DEVID ((0xff18 << 16) | PCI_VENDOR_ID_ATHEROS) | |
151 | + | |
152 | +/* ??? access BAR */ | |
153 | +#define AR2315_PCI_HOST_MBAR0 0x10000000 | |
154 | +/* RAM access BAR */ | |
155 | +#define AR2315_PCI_HOST_MBAR1 AR2315_PCI_HOST_SDRAM_BASEADDR | |
156 | +/* ??? access BAR */ | |
157 | +#define AR2315_PCI_HOST_MBAR2 0x30000000 | |
158 | + | |
159 | +struct ar2315_pci_ctrl { | |
160 | + void __iomem *cfg_mem; | |
161 | + void __iomem *mmr_mem; | |
162 | + unsigned irq; | |
163 | + unsigned irq_ext; | |
164 | + struct irq_domain *domain; | |
165 | + struct pci_controller pci_ctrl; | |
166 | + struct resource mem_res; | |
167 | + struct resource io_res; | |
168 | +}; | |
169 | + | |
170 | +static inline struct ar2315_pci_ctrl *ar2315_pci_bus_to_apc(struct pci_bus *bus) | |
171 | +{ | |
172 | + struct pci_controller *hose = bus->sysdata; | |
173 | + | |
174 | + return container_of(hose, struct ar2315_pci_ctrl, pci_ctrl); | |
175 | +} | |
176 | + | |
177 | +static inline u32 ar2315_pci_reg_read(struct ar2315_pci_ctrl *apc, u32 reg) | |
178 | +{ | |
179 | + return __raw_readl(apc->mmr_mem + reg); | |
180 | +} | |
181 | + | |
182 | +static inline void ar2315_pci_reg_write(struct ar2315_pci_ctrl *apc, u32 reg, | |
183 | + u32 val) | |
184 | +{ | |
185 | + __raw_writel(val, apc->mmr_mem + reg); | |
186 | +} | |
187 | + | |
188 | +static inline void ar2315_pci_reg_mask(struct ar2315_pci_ctrl *apc, u32 reg, | |
189 | + u32 mask, u32 val) | |
190 | +{ | |
191 | + u32 ret = ar2315_pci_reg_read(apc, reg); | |
192 | + | |
193 | + ret &= ~mask; | |
194 | + ret |= val; | |
195 | + ar2315_pci_reg_write(apc, reg, ret); | |
196 | +} | |
197 | + | |
198 | +static int ar2315_pci_cfg_access(struct ar2315_pci_ctrl *apc, unsigned devfn, | |
199 | + int where, int size, u32 *ptr, bool write) | |
200 | +{ | |
201 | + int func = PCI_FUNC(devfn); | |
202 | + int dev = PCI_SLOT(devfn); | |
203 | + u32 addr = (1 << (13 + dev)) | (func << 8) | (where & ~3); | |
204 | + u32 mask = 0xffffffff >> 8 * (4 - size); | |
205 | + u32 sh = (where & 3) * 8; | |
206 | + u32 value, isr; | |
207 | + | |
208 | + /* Prevent access past the remapped area */ | |
209 | + if (addr >= AR2315_PCI_CFG_SIZE || dev > 18) | |
210 | + return PCIBIOS_DEVICE_NOT_FOUND; | |
211 | + | |
212 | + /* Clear pending errors */ | |
213 | + ar2315_pci_reg_write(apc, AR2315_PCI_ISR, AR2315_PCI_INT_ABORT); | |
214 | + /* Select Configuration access */ | |
215 | + ar2315_pci_reg_mask(apc, AR2315_PCI_MISC_CONFIG, 0, | |
216 | + AR2315_PCIMISC_CFG_SEL); | |
217 | + | |
218 | + mb(); /* PCI must see space change before we begin */ | |
219 | + | |
220 | + value = __raw_readl(apc->cfg_mem + addr); | |
221 | + | |
222 | + isr = ar2315_pci_reg_read(apc, AR2315_PCI_ISR); | |
223 | + | |
224 | + if (isr & AR2315_PCI_INT_ABORT) | |
225 | + goto exit_err; | |
226 | + | |
227 | + if (write) { | |
228 | + value = (value & ~(mask << sh)) | *ptr << sh; | |
229 | + __raw_writel(value, apc->cfg_mem + addr); | |
230 | + isr = ar2315_pci_reg_read(apc, AR2315_PCI_ISR); | |
231 | + if (isr & AR2315_PCI_INT_ABORT) | |
232 | + goto exit_err; | |
233 | + } else { | |
234 | + *ptr = (value >> sh) & mask; | |
235 | + } | |
236 | + | |
237 | + goto exit; | |
238 | + | |
239 | +exit_err: | |
240 | + ar2315_pci_reg_write(apc, AR2315_PCI_ISR, AR2315_PCI_INT_ABORT); | |
241 | + if (!write) | |
242 | + *ptr = 0xffffffff; | |
243 | + | |
244 | +exit: | |
245 | + /* Select Memory access */ | |
246 | + ar2315_pci_reg_mask(apc, AR2315_PCI_MISC_CONFIG, AR2315_PCIMISC_CFG_SEL, | |
247 | + 0); | |
248 | + | |
249 | + return isr & AR2315_PCI_INT_ABORT ? PCIBIOS_DEVICE_NOT_FOUND : | |
250 | + PCIBIOS_SUCCESSFUL; | |
251 | +} | |
252 | + | |
253 | +static inline int ar2315_pci_local_cfg_rd(struct ar2315_pci_ctrl *apc, | |
254 | + unsigned devfn, int where, u32 *val) | |
255 | +{ | |
256 | + return ar2315_pci_cfg_access(apc, devfn, where, sizeof(u32), val, | |
257 | + false); | |
258 | +} | |
259 | + | |
260 | +static inline int ar2315_pci_local_cfg_wr(struct ar2315_pci_ctrl *apc, | |
261 | + unsigned devfn, int where, u32 val) | |
262 | +{ | |
263 | + return ar2315_pci_cfg_access(apc, devfn, where, sizeof(u32), &val, | |
264 | + true); | |
265 | +} | |
266 | + | |
267 | +static int ar2315_pci_cfg_read(struct pci_bus *bus, unsigned devfn, int where, | |
268 | + int size, u32 *value) | |
269 | +{ | |
270 | + struct ar2315_pci_ctrl *apc = ar2315_pci_bus_to_apc(bus); | |
271 | + | |
272 | + if (PCI_SLOT(devfn) == AR2315_PCI_HOST_SLOT) | |
273 | + return PCIBIOS_DEVICE_NOT_FOUND; | |
274 | + | |
275 | + return ar2315_pci_cfg_access(apc, devfn, where, size, value, false); | |
276 | +} | |
277 | + | |
278 | +static int ar2315_pci_cfg_write(struct pci_bus *bus, unsigned devfn, int where, | |
279 | + int size, u32 value) | |
280 | +{ | |
281 | + struct ar2315_pci_ctrl *apc = ar2315_pci_bus_to_apc(bus); | |
282 | + | |
283 | + if (PCI_SLOT(devfn) == AR2315_PCI_HOST_SLOT) | |
284 | + return PCIBIOS_DEVICE_NOT_FOUND; | |
285 | + | |
286 | + return ar2315_pci_cfg_access(apc, devfn, where, size, &value, true); | |
287 | +} | |
288 | + | |
289 | +static struct pci_ops ar2315_pci_ops = { | |
290 | + .read = ar2315_pci_cfg_read, | |
291 | + .write = ar2315_pci_cfg_write, | |
292 | +}; | |
293 | + | |
294 | +static int ar2315_pci_host_setup(struct ar2315_pci_ctrl *apc) | |
295 | +{ | |
296 | + unsigned devfn = PCI_DEVFN(AR2315_PCI_HOST_SLOT, 0); | |
297 | + int res; | |
298 | + u32 id; | |
299 | + | |
300 | + res = ar2315_pci_local_cfg_rd(apc, devfn, PCI_VENDOR_ID, &id); | |
301 | + if (res != PCIBIOS_SUCCESSFUL || id != AR2315_PCI_HOST_DEVID) | |
302 | + return -ENODEV; | |
303 | + | |
304 | + /* Program MBARs */ | |
305 | + ar2315_pci_local_cfg_wr(apc, devfn, PCI_BASE_ADDRESS_0, | |
306 | + AR2315_PCI_HOST_MBAR0); | |
307 | + ar2315_pci_local_cfg_wr(apc, devfn, PCI_BASE_ADDRESS_1, | |
308 | + AR2315_PCI_HOST_MBAR1); | |
309 | + ar2315_pci_local_cfg_wr(apc, devfn, PCI_BASE_ADDRESS_2, | |
310 | + AR2315_PCI_HOST_MBAR2); | |
311 | + | |
312 | + /* Run */ | |
313 | + ar2315_pci_local_cfg_wr(apc, devfn, PCI_COMMAND, PCI_COMMAND_MEMORY | | |
314 | + PCI_COMMAND_MASTER | PCI_COMMAND_SPECIAL | | |
315 | + PCI_COMMAND_INVALIDATE | PCI_COMMAND_PARITY | | |
316 | + PCI_COMMAND_SERR | PCI_COMMAND_FAST_BACK); | |
317 | + | |
318 | + return 0; | |
319 | +} | |
320 | + | |
321 | +static void ar2315_pci_irq_handler(unsigned irq, struct irq_desc *desc) | |
322 | +{ | |
323 | + struct ar2315_pci_ctrl *apc = irq_get_handler_data(irq); | |
324 | + u32 pending = ar2315_pci_reg_read(apc, AR2315_PCI_ISR) & | |
325 | + ar2315_pci_reg_read(apc, AR2315_PCI_IMR); | |
326 | + unsigned pci_irq = 0; | |
327 | + | |
328 | + if (pending) | |
329 | + pci_irq = irq_find_mapping(apc->domain, __ffs(pending)); | |
330 | + | |
331 | + if (pci_irq) | |
332 | + generic_handle_irq(pci_irq); | |
333 | + else | |
334 | + spurious_interrupt(); | |
335 | +} | |
336 | + | |
337 | +static void ar2315_pci_irq_mask(struct irq_data *d) | |
338 | +{ | |
339 | + struct ar2315_pci_ctrl *apc = irq_data_get_irq_chip_data(d); | |
340 | + | |
341 | + ar2315_pci_reg_mask(apc, AR2315_PCI_IMR, BIT(d->hwirq), 0); | |
342 | +} | |
343 | + | |
344 | +static void ar2315_pci_irq_mask_ack(struct irq_data *d) | |
345 | +{ | |
346 | + struct ar2315_pci_ctrl *apc = irq_data_get_irq_chip_data(d); | |
347 | + u32 m = BIT(d->hwirq); | |
348 | + | |
349 | + ar2315_pci_reg_mask(apc, AR2315_PCI_IMR, m, 0); | |
350 | + ar2315_pci_reg_write(apc, AR2315_PCI_ISR, m); | |
351 | +} | |
352 | + | |
353 | +static void ar2315_pci_irq_unmask(struct irq_data *d) | |
354 | +{ | |
355 | + struct ar2315_pci_ctrl *apc = irq_data_get_irq_chip_data(d); | |
356 | + | |
357 | + ar2315_pci_reg_mask(apc, AR2315_PCI_IMR, 0, BIT(d->hwirq)); | |
358 | +} | |
359 | + | |
360 | +static struct irq_chip ar2315_pci_irq_chip = { | |
361 | + .name = "AR2315-PCI", | |
362 | + .irq_mask = ar2315_pci_irq_mask, | |
363 | + .irq_mask_ack = ar2315_pci_irq_mask_ack, | |
364 | + .irq_unmask = ar2315_pci_irq_unmask, | |
365 | +}; | |
366 | + | |
367 | +static int ar2315_pci_irq_map(struct irq_domain *d, unsigned irq, | |
368 | + irq_hw_number_t hw) | |
369 | +{ | |
370 | + irq_set_chip_and_handler(irq, &ar2315_pci_irq_chip, handle_level_irq); | |
371 | + irq_set_chip_data(irq, d->host_data); | |
372 | + return 0; | |
373 | +} | |
374 | + | |
375 | +static struct irq_domain_ops ar2315_pci_irq_domain_ops = { | |
376 | + .map = ar2315_pci_irq_map, | |
377 | +}; | |
378 | + | |
379 | +static void ar2315_pci_irq_init(struct ar2315_pci_ctrl *apc) | |
380 | +{ | |
381 | + ar2315_pci_reg_mask(apc, AR2315_PCI_IER, AR2315_PCI_IER_ENABLE, 0); | |
382 | + ar2315_pci_reg_mask(apc, AR2315_PCI_IMR, (AR2315_PCI_INT_ABORT | | |
383 | + AR2315_PCI_INT_EXT), 0); | |
384 | + | |
385 | + apc->irq_ext = irq_create_mapping(apc->domain, AR2315_PCI_IRQ_EXT); | |
386 | + | |
387 | + irq_set_chained_handler(apc->irq, ar2315_pci_irq_handler); | |
388 | + irq_set_handler_data(apc->irq, apc); | |
389 | + | |
390 | + /* Clear any pending Abort or external Interrupts | |
391 | + * and enable interrupt processing */ | |
392 | + ar2315_pci_reg_write(apc, AR2315_PCI_ISR, AR2315_PCI_INT_ABORT | | |
393 | + AR2315_PCI_INT_EXT); | |
394 | + ar2315_pci_reg_mask(apc, AR2315_PCI_IER, 0, AR2315_PCI_IER_ENABLE); | |
395 | +} | |
396 | + | |
397 | +static int ar2315_pci_probe(struct platform_device *pdev) | |
398 | +{ | |
399 | + struct ar2315_pci_ctrl *apc; | |
400 | + struct device *dev = &pdev->dev; | |
401 | + struct resource *res; | |
402 | + int irq, err; | |
403 | + | |
404 | + apc = devm_kzalloc(dev, sizeof(*apc), GFP_KERNEL); | |
405 | + if (!apc) | |
406 | + return -ENOMEM; | |
407 | + | |
408 | + irq = platform_get_irq(pdev, 0); | |
409 | + if (irq < 0) | |
410 | + return -EINVAL; | |
411 | + apc->irq = irq; | |
412 | + | |
413 | + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, | |
414 | + "ar2315-pci-ctrl"); | |
415 | + apc->mmr_mem = devm_ioremap_resource(dev, res); | |
416 | + if (IS_ERR(apc->mmr_mem)) | |
417 | + return PTR_ERR(apc->mmr_mem); | |
418 | + | |
419 | + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, | |
420 | + "ar2315-pci-ext"); | |
421 | + if (!res) | |
422 | + return -EINVAL; | |
423 | + | |
424 | + apc->mem_res.name = "AR2315 PCI mem space"; | |
425 | + apc->mem_res.parent = res; | |
426 | + apc->mem_res.start = res->start; | |
427 | + apc->mem_res.end = res->end; | |
428 | + apc->mem_res.flags = IORESOURCE_MEM; | |
429 | + | |
430 | + /* Remap PCI config space */ | |
431 | + apc->cfg_mem = devm_ioremap_nocache(dev, res->start, | |
432 | + AR2315_PCI_CFG_SIZE); | |
433 | + if (!apc->cfg_mem) { | |
434 | + dev_err(dev, "failed to remap PCI config space\n"); | |
435 | + return -ENOMEM; | |
436 | + } | |
437 | + | |
438 | + /* Reset the PCI bus by setting bits 5-4 in PCI_MCFG */ | |
439 | + ar2315_pci_reg_mask(apc, AR2315_PCI_MISC_CONFIG, | |
440 | + AR2315_PCIMISC_RST_MODE, | |
441 | + AR2315_PCIRST_LOW); | |
442 | + msleep(100); | |
443 | + | |
444 | + /* Bring the PCI out of reset */ | |
445 | + ar2315_pci_reg_mask(apc, AR2315_PCI_MISC_CONFIG, | |
446 | + AR2315_PCIMISC_RST_MODE, | |
447 | + AR2315_PCIRST_HIGH | AR2315_PCICACHE_DIS | 0x8); | |
448 | + | |
449 | + ar2315_pci_reg_write(apc, AR2315_PCI_UNCACHE_CFG, | |
450 | + 0x1E | /* 1GB uncached */ | |
451 | + (1 << 5) | /* Enable uncached */ | |
452 | + (0x2 << 30) /* Base: 0x80000000 */); | |
453 | + ar2315_pci_reg_read(apc, AR2315_PCI_UNCACHE_CFG); | |
454 | + | |
455 | + msleep(500); | |
456 | + | |
457 | + err = ar2315_pci_host_setup(apc); | |
458 | + if (err) | |
459 | + return err; | |
460 | + | |
461 | + apc->domain = irq_domain_add_linear(NULL, AR2315_PCI_IRQ_COUNT, | |
462 | + &ar2315_pci_irq_domain_ops, apc); | |
463 | + if (!apc->domain) { | |
464 | + dev_err(dev, "failed to add IRQ domain\n"); | |
465 | + return -ENOMEM; | |
466 | + } | |
467 | + | |
468 | + ar2315_pci_irq_init(apc); | |
469 | + | |
470 | + /* PCI controller does not support I/O ports */ | |
471 | + apc->io_res.name = "AR2315 IO space"; | |
472 | + apc->io_res.start = 0; | |
473 | + apc->io_res.end = 0; | |
474 | + apc->io_res.flags = IORESOURCE_IO, | |
475 | + | |
476 | + apc->pci_ctrl.pci_ops = &ar2315_pci_ops; | |
477 | + apc->pci_ctrl.mem_resource = &apc->mem_res, | |
478 | + apc->pci_ctrl.io_resource = &apc->io_res, | |
479 | + | |
480 | + register_pci_controller(&apc->pci_ctrl); | |
481 | + | |
482 | + dev_info(dev, "register PCI controller\n"); | |
483 | + | |
484 | + return 0; | |
485 | +} | |
486 | + | |
487 | +static struct platform_driver ar2315_pci_driver = { | |
488 | + .probe = ar2315_pci_probe, | |
489 | + .driver = { | |
490 | + .name = "ar2315-pci", | |
491 | + .owner = THIS_MODULE, | |
492 | + }, | |
493 | +}; | |
494 | + | |
495 | +static int __init ar2315_pci_init(void) | |
496 | +{ | |
497 | + return platform_driver_register(&ar2315_pci_driver); | |
498 | +} | |
499 | +arch_initcall(ar2315_pci_init); | |
500 | + | |
501 | +int pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) | |
502 | +{ | |
503 | + struct ar2315_pci_ctrl *apc = ar2315_pci_bus_to_apc(dev->bus); | |
504 | + | |
505 | + return slot ? 0 : apc->irq_ext; | |
506 | +} | |
507 | + | |
508 | +int pcibios_plat_dev_init(struct pci_dev *dev) | |
509 | +{ | |
510 | + return 0; | |
511 | +} |