]>
Commit | Line | Data |
---|---|---|
5283ecb5 | 1 | /* |
62c7ae87 | 2 | * Low-Level PCI Support for the SH7780 |
5283ecb5 | 3 | * |
a45635df | 4 | * Copyright (C) 2005 - 2010 Paul Mundt |
5283ecb5 | 5 | * |
62c7ae87 PM |
6 | * This file is subject to the terms and conditions of the GNU General Public |
7 | * License. See the file "COPYING" in the main directory of this archive | |
8 | * for more details. | |
5283ecb5 | 9 | */ |
5283ecb5 PM |
10 | #include <linux/types.h> |
11 | #include <linux/kernel.h> | |
12 | #include <linux/init.h> | |
13 | #include <linux/pci.h> | |
ef407bee PM |
14 | #include <linux/interrupt.h> |
15 | #include <linux/timer.h> | |
16 | #include <linux/irq.h> | |
5283ecb5 | 17 | #include <linux/errno.h> |
5283ecb5 | 18 | #include <linux/delay.h> |
aee4467b | 19 | #include <linux/log2.h> |
959f85f8 | 20 | #include "pci-sh4.h" |
a45635df PM |
21 | #include <asm/mmu.h> |
22 | #include <asm/sizes.h> | |
5283ecb5 | 23 | |
b6c58b1d PM |
24 | static struct resource sh7785_pci_resources[] = { |
25 | { | |
3b0be1a4 | 26 | .name = "PCI IO", |
b6c58b1d PM |
27 | .start = 0x1000, |
28 | .end = SZ_4M - 1, | |
29 | .flags = IORESOURCE_IO, | |
30 | }, { | |
31 | .name = "PCI MEM 0", | |
32 | .start = 0xfd000000, | |
33 | .end = 0xfd000000 + SZ_16M - 1, | |
34 | .flags = IORESOURCE_MEM, | |
35 | }, { | |
36 | .name = "PCI MEM 1", | |
37 | .start = 0x10000000, | |
38 | .end = 0x10000000 + SZ_64M - 1, | |
39 | .flags = IORESOURCE_MEM, | |
40 | }, { | |
41 | /* | |
42 | * 32-bit only resources must be last. | |
43 | */ | |
44 | .name = "PCI MEM 2", | |
45 | .start = 0xc0000000, | |
46 | .end = 0xc0000000 + SZ_512M - 1, | |
47 | .flags = IORESOURCE_MEM | IORESOURCE_MEM_32BIT, | |
48 | }, | |
e79066a6 PM |
49 | }; |
50 | ||
51 | static struct pci_channel sh7780_pci_controller = { | |
52 | .pci_ops = &sh4_pci_ops, | |
b6c58b1d PM |
53 | .resources = sh7785_pci_resources, |
54 | .nr_resources = ARRAY_SIZE(sh7785_pci_resources), | |
55 | .io_offset = 0, | |
56 | .mem_offset = 0, | |
57 | .io_map_base = 0xfe200000, | |
ef407bee PM |
58 | .serr_irq = evt2irq(0xa00), |
59 | .err_irq = evt2irq(0xaa0), | |
e79066a6 PM |
60 | }; |
61 | ||
ef407bee PM |
62 | struct pci_errors { |
63 | unsigned int mask; | |
64 | const char *str; | |
65 | } pci_arbiter_errors[] = { | |
66 | { SH4_PCIAINT_MBKN, "master broken" }, | |
67 | { SH4_PCIAINT_TBTO, "target bus time out" }, | |
68 | { SH4_PCIAINT_MBTO, "master bus time out" }, | |
69 | { SH4_PCIAINT_TABT, "target abort" }, | |
70 | { SH4_PCIAINT_MABT, "master abort" }, | |
71 | { SH4_PCIAINT_RDPE, "read data parity error" }, | |
72 | { SH4_PCIAINT_WDPE, "write data parity error" }, | |
73 | }, pci_interrupt_errors[] = { | |
74 | { SH4_PCIINT_MLCK, "master lock error" }, | |
75 | { SH4_PCIINT_TABT, "target-target abort" }, | |
76 | { SH4_PCIINT_TRET, "target retry time out" }, | |
ecfb68c6 | 77 | { SH4_PCIINT_MFDE, "master function disable error" }, |
ef407bee PM |
78 | { SH4_PCIINT_PRTY, "address parity error" }, |
79 | { SH4_PCIINT_SERR, "SERR" }, | |
80 | { SH4_PCIINT_TWDP, "data parity error for target write" }, | |
81 | { SH4_PCIINT_TRDP, "PERR detected for target read" }, | |
82 | { SH4_PCIINT_MTABT, "target abort for master" }, | |
83 | { SH4_PCIINT_MMABT, "master abort for master" }, | |
84 | { SH4_PCIINT_MWPD, "master write data parity error" }, | |
85 | { SH4_PCIINT_MRPD, "master read data parity error" }, | |
86 | }; | |
87 | ||
88 | static irqreturn_t sh7780_pci_err_irq(int irq, void *dev_id) | |
89 | { | |
90 | struct pci_channel *hose = dev_id; | |
91 | unsigned long addr; | |
92 | unsigned int status; | |
93 | unsigned int cmd; | |
94 | int i; | |
95 | ||
96 | addr = __raw_readl(hose->reg_base + SH4_PCIALR); | |
97 | ||
98 | /* | |
99 | * Handle status errors. | |
100 | */ | |
101 | status = __raw_readw(hose->reg_base + PCI_STATUS); | |
102 | if (status & (PCI_STATUS_PARITY | | |
103 | PCI_STATUS_DETECTED_PARITY | | |
104 | PCI_STATUS_SIG_TARGET_ABORT | | |
105 | PCI_STATUS_REC_TARGET_ABORT | | |
106 | PCI_STATUS_REC_MASTER_ABORT)) { | |
107 | cmd = pcibios_handle_status_errors(addr, status, hose); | |
108 | if (likely(cmd)) | |
109 | __raw_writew(cmd, hose->reg_base + PCI_STATUS); | |
110 | } | |
111 | ||
112 | /* | |
113 | * Handle arbiter errors. | |
114 | */ | |
115 | status = __raw_readl(hose->reg_base + SH4_PCIAINT); | |
116 | for (i = cmd = 0; i < ARRAY_SIZE(pci_arbiter_errors); i++) { | |
117 | if (status & pci_arbiter_errors[i].mask) { | |
118 | printk(KERN_DEBUG "PCI: %s, addr=%08lx\n", | |
119 | pci_arbiter_errors[i].str, addr); | |
120 | cmd |= pci_arbiter_errors[i].mask; | |
121 | } | |
122 | } | |
123 | __raw_writel(cmd, hose->reg_base + SH4_PCIAINT); | |
124 | ||
125 | /* | |
126 | * Handle the remaining PCI errors. | |
127 | */ | |
128 | status = __raw_readl(hose->reg_base + SH4_PCIINT); | |
129 | for (i = cmd = 0; i < ARRAY_SIZE(pci_interrupt_errors); i++) { | |
130 | if (status & pci_interrupt_errors[i].mask) { | |
131 | printk(KERN_DEBUG "PCI: %s, addr=%08lx\n", | |
132 | pci_interrupt_errors[i].str, addr); | |
133 | cmd |= pci_interrupt_errors[i].mask; | |
134 | } | |
135 | } | |
136 | __raw_writel(cmd, hose->reg_base + SH4_PCIINT); | |
137 | ||
138 | return IRQ_HANDLED; | |
139 | } | |
140 | ||
141 | static irqreturn_t sh7780_pci_serr_irq(int irq, void *dev_id) | |
142 | { | |
143 | struct pci_channel *hose = dev_id; | |
144 | ||
145 | printk(KERN_DEBUG "PCI: system error received: "); | |
146 | pcibios_report_status(PCI_STATUS_SIG_SYSTEM_ERROR, 1); | |
147 | printk("\n"); | |
148 | ||
149 | /* Deassert SERR */ | |
150 | __raw_writel(SH4_PCIINTM_SDIM, hose->reg_base + SH4_PCIINTM); | |
151 | ||
152 | /* Back off the IRQ for awhile */ | |
9ad62ec4 | 153 | disable_irq_nosync(irq); |
ef407bee PM |
154 | hose->serr_timer.expires = jiffies + HZ; |
155 | add_timer(&hose->serr_timer); | |
156 | ||
157 | return IRQ_HANDLED; | |
158 | } | |
159 | ||
160 | static int __init sh7780_pci_setup_irqs(struct pci_channel *hose) | |
161 | { | |
162 | int ret; | |
163 | ||
164 | /* Clear out PCI arbiter IRQs */ | |
165 | __raw_writel(0, hose->reg_base + SH4_PCIAINT); | |
166 | ||
167 | /* Clear all error conditions */ | |
168 | __raw_writew(PCI_STATUS_DETECTED_PARITY | \ | |
169 | PCI_STATUS_SIG_SYSTEM_ERROR | \ | |
170 | PCI_STATUS_REC_MASTER_ABORT | \ | |
171 | PCI_STATUS_REC_TARGET_ABORT | \ | |
172 | PCI_STATUS_SIG_TARGET_ABORT | \ | |
173 | PCI_STATUS_PARITY, hose->reg_base + PCI_STATUS); | |
174 | ||
d11584a0 | 175 | ret = request_irq(hose->serr_irq, sh7780_pci_serr_irq, 0, |
ef407bee PM |
176 | "PCI SERR interrupt", hose); |
177 | if (unlikely(ret)) { | |
178 | printk(KERN_ERR "PCI: Failed hooking SERR IRQ\n"); | |
179 | return ret; | |
180 | } | |
181 | ||
182 | /* | |
183 | * The PCI ERR IRQ needs to be IRQF_SHARED since all of the power | |
184 | * down IRQ vectors are routed through the ERR IRQ vector. We | |
185 | * only request_irq() once as there is only a single masking | |
186 | * source for multiple events. | |
187 | */ | |
188 | ret = request_irq(hose->err_irq, sh7780_pci_err_irq, IRQF_SHARED, | |
189 | "PCI ERR interrupt", hose); | |
190 | if (unlikely(ret)) { | |
191 | free_irq(hose->serr_irq, hose); | |
192 | return ret; | |
193 | } | |
194 | ||
195 | /* Unmask all of the arbiter IRQs. */ | |
196 | __raw_writel(SH4_PCIAINT_MBKN | SH4_PCIAINT_TBTO | SH4_PCIAINT_MBTO | \ | |
197 | SH4_PCIAINT_TABT | SH4_PCIAINT_MABT | SH4_PCIAINT_RDPE | \ | |
198 | SH4_PCIAINT_WDPE, hose->reg_base + SH4_PCIAINTM); | |
199 | ||
200 | /* Unmask all of the PCI IRQs */ | |
201 | __raw_writel(SH4_PCIINTM_TTADIM | SH4_PCIINTM_TMTOIM | \ | |
202 | SH4_PCIINTM_MDEIM | SH4_PCIINTM_APEDIM | \ | |
203 | SH4_PCIINTM_SDIM | SH4_PCIINTM_DPEITWM | \ | |
204 | SH4_PCIINTM_PEDITRM | SH4_PCIINTM_TADIMM | \ | |
205 | SH4_PCIINTM_MADIMM | SH4_PCIINTM_MWPDIM | \ | |
206 | SH4_PCIINTM_MRDPEIM, hose->reg_base + SH4_PCIINTM); | |
207 | ||
208 | return ret; | |
209 | } | |
210 | ||
211 | static inline void __init sh7780_pci_teardown_irqs(struct pci_channel *hose) | |
212 | { | |
213 | free_irq(hose->err_irq, hose); | |
214 | free_irq(hose->serr_irq, hose); | |
215 | } | |
216 | ||
85b59f5b PM |
217 | static void __init sh7780_pci66_init(struct pci_channel *hose) |
218 | { | |
219 | unsigned int tmp; | |
220 | ||
221 | if (!pci_is_66mhz_capable(hose, 0, 0)) | |
222 | return; | |
223 | ||
224 | /* Enable register access */ | |
225 | tmp = __raw_readl(hose->reg_base + SH4_PCICR); | |
226 | tmp |= SH4_PCICR_PREFIX; | |
227 | __raw_writel(tmp, hose->reg_base + SH4_PCICR); | |
228 | ||
229 | /* Enable 66MHz operation */ | |
230 | tmp = __raw_readw(hose->reg_base + PCI_STATUS); | |
231 | tmp |= PCI_STATUS_66MHZ; | |
232 | __raw_writew(tmp, hose->reg_base + PCI_STATUS); | |
233 | ||
234 | /* Done */ | |
235 | tmp = __raw_readl(hose->reg_base + SH4_PCICR); | |
236 | tmp |= SH4_PCICR_PREFIX | SH4_PCICR_CFIN; | |
237 | __raw_writel(tmp, hose->reg_base + SH4_PCICR); | |
238 | } | |
239 | ||
e79066a6 | 240 | static int __init sh7780_pci_init(void) |
5283ecb5 | 241 | { |
e79066a6 | 242 | struct pci_channel *chan = &sh7780_pci_controller; |
a45635df PM |
243 | phys_addr_t memphys; |
244 | size_t memsize; | |
959f85f8 | 245 | unsigned int id; |
a45635df | 246 | const char *type; |
b6c58b1d | 247 | int ret, i; |
5283ecb5 | 248 | |
3b554c33 | 249 | printk(KERN_NOTICE "PCI: Starting initialization.\n"); |
5283ecb5 | 250 | |
e4c6a360 MD |
251 | chan->reg_base = 0xfe040000; |
252 | ||
4e7b7fdb PM |
253 | /* Enable CPU access to the PCIC registers. */ |
254 | __raw_writel(PCIECR_ENBL, PCIECR); | |
5283ecb5 | 255 | |
a45635df PM |
256 | /* Reset */ |
257 | __raw_writel(SH4_PCICR_PREFIX | SH4_PCICR_PRST, | |
258 | chan->reg_base + SH4_PCICR); | |
259 | ||
aee4467b PM |
260 | /* |
261 | * Wait for it to come back up. The spec says to allow for up to | |
262 | * 1 second after toggling the reset pin, but in practice 100ms | |
263 | * is more than enough. | |
264 | */ | |
a45635df PM |
265 | mdelay(100); |
266 | ||
267 | id = __raw_readw(chan->reg_base + PCI_VENDOR_ID); | |
268 | if (id != PCI_VENDOR_ID_RENESAS) { | |
4e7b7fdb PM |
269 | printk(KERN_ERR "PCI: Unknown vendor ID 0x%04x.\n", id); |
270 | return -ENODEV; | |
32351a28 PM |
271 | } |
272 | ||
a45635df PM |
273 | id = __raw_readw(chan->reg_base + PCI_DEVICE_ID); |
274 | type = (id == PCI_DEVICE_ID_RENESAS_SH7763) ? "SH7763" : | |
275 | (id == PCI_DEVICE_ID_RENESAS_SH7780) ? "SH7780" : | |
276 | (id == PCI_DEVICE_ID_RENESAS_SH7781) ? "SH7781" : | |
277 | (id == PCI_DEVICE_ID_RENESAS_SH7785) ? "SH7785" : | |
4e7b7fdb PM |
278 | NULL; |
279 | if (unlikely(!type)) { | |
280 | printk(KERN_ERR "PCI: Found an unsupported Renesas host " | |
281 | "controller, device id 0x%04x.\n", id); | |
282 | return -EINVAL; | |
5283ecb5 PM |
283 | } |
284 | ||
4e7b7fdb PM |
285 | printk(KERN_NOTICE "PCI: Found a Renesas %s host " |
286 | "controller, revision %d.\n", type, | |
a45635df | 287 | __raw_readb(chan->reg_base + PCI_REVISION_ID)); |
4e7b7fdb | 288 | |
c66c1d79 | 289 | /* |
a45635df PM |
290 | * Now throw it in to register initialization mode and |
291 | * start the real work. | |
c66c1d79 | 292 | */ |
a45635df PM |
293 | __raw_writel(SH4_PCICR_PREFIX, chan->reg_base + SH4_PCICR); |
294 | ||
295 | memphys = __pa(memory_start); | |
aee4467b | 296 | memsize = roundup_pow_of_two(memory_end - memory_start); |
0bbc9bc3 | 297 | |
62c7ae87 | 298 | /* |
aee4467b PM |
299 | * If there's more than 512MB of memory, we need to roll over to |
300 | * LAR1/LSR1. | |
5283ecb5 | 301 | */ |
aee4467b PM |
302 | if (memsize > SZ_512M) { |
303 | __raw_writel(memphys + SZ_512M, chan->reg_base + SH4_PCILAR1); | |
304 | __raw_writel((((memsize - SZ_512M) - SZ_1M) & 0x1ff00000) | 1, | |
305 | chan->reg_base + SH4_PCILSR1); | |
306 | memsize = SZ_512M; | |
307 | } else { | |
308 | /* | |
309 | * Otherwise just zero it out and disable it. | |
310 | */ | |
311 | __raw_writel(0, chan->reg_base + SH4_PCILAR1); | |
312 | __raw_writel(0, chan->reg_base + SH4_PCILSR1); | |
313 | } | |
a45635df | 314 | |
aee4467b PM |
315 | /* |
316 | * LAR0/LSR0 covers up to the first 512MB, which is enough to | |
317 | * cover all of lowmem on most platforms. | |
318 | */ | |
a45635df | 319 | __raw_writel(memphys, chan->reg_base + SH4_PCILAR0); |
aee4467b | 320 | __raw_writel(((memsize - SZ_1M) & 0x1ff00000) | 1, |
a45635df PM |
321 | chan->reg_base + SH4_PCILSR0); |
322 | ||
ef407bee PM |
323 | /* |
324 | * Hook up the ERR and SERR IRQs. | |
325 | */ | |
326 | ret = sh7780_pci_setup_irqs(chan); | |
327 | if (unlikely(ret)) | |
328 | return ret; | |
a45635df PM |
329 | |
330 | /* | |
331 | * Disable the cache snoop controller for non-coherent DMA. | |
332 | */ | |
333 | __raw_writel(0, chan->reg_base + SH7780_PCICSCR0); | |
334 | __raw_writel(0, chan->reg_base + SH7780_PCICSAR0); | |
335 | __raw_writel(0, chan->reg_base + SH7780_PCICSCR1); | |
336 | __raw_writel(0, chan->reg_base + SH7780_PCICSAR1); | |
337 | ||
b6c58b1d PM |
338 | /* |
339 | * Setup the memory BARs | |
340 | */ | |
3b0be1a4 PM |
341 | for (i = 1; i < chan->nr_resources; i++) { |
342 | struct resource *res = chan->resources + i; | |
b6c58b1d PM |
343 | resource_size_t size; |
344 | ||
345 | if (unlikely(res->flags & IORESOURCE_IO)) | |
346 | continue; | |
347 | ||
348 | /* | |
349 | * Make sure we're in the right physical addressing mode | |
350 | * for dealing with the resource. | |
351 | */ | |
352 | if ((res->flags & IORESOURCE_MEM_32BIT) && __in_29bit_mode()) { | |
353 | chan->nr_resources--; | |
354 | continue; | |
355 | } | |
a45635df | 356 | |
b6c58b1d PM |
357 | size = resource_size(res); |
358 | ||
359 | /* | |
360 | * The MBMR mask is calculated in units of 256kB, which | |
361 | * keeps things pretty simple. | |
362 | */ | |
363 | __raw_writel(((roundup_pow_of_two(size) / SZ_256K) - 1) << 18, | |
3b0be1a4 PM |
364 | chan->reg_base + SH7780_PCIMBMR(i - 1)); |
365 | __raw_writel(res->start, chan->reg_base + SH7780_PCIMBR(i - 1)); | |
b6c58b1d PM |
366 | } |
367 | ||
368 | /* | |
369 | * And I/O. | |
370 | */ | |
371 | __raw_writel(0, chan->reg_base + PCI_BASE_ADDRESS_0); | |
a45635df PM |
372 | __raw_writel(0, chan->reg_base + SH7780_PCIIOBR); |
373 | __raw_writel(0, chan->reg_base + SH7780_PCIIOBMR); | |
374 | ||
ef407bee PM |
375 | __raw_writew(PCI_COMMAND_SERR | PCI_COMMAND_WAIT | \ |
376 | PCI_COMMAND_PARITY | PCI_COMMAND_MASTER | \ | |
377 | PCI_COMMAND_MEMORY, chan->reg_base + PCI_COMMAND); | |
378 | ||
a45635df PM |
379 | /* |
380 | * Initialization mode complete, release the control register and | |
381 | * enable round robin mode to stop device overruns/starvation. | |
382 | */ | |
383 | __raw_writel(SH4_PCICR_PREFIX | SH4_PCICR_CFIN | SH4_PCICR_FTO, | |
384 | chan->reg_base + SH4_PCICR); | |
5283ecb5 | 385 | |
bcf39352 PM |
386 | ret = register_pci_controller(chan); |
387 | if (unlikely(ret)) | |
ef407bee | 388 | goto err; |
e79066a6 | 389 | |
85b59f5b PM |
390 | sh7780_pci66_init(chan); |
391 | ||
392 | printk(KERN_NOTICE "PCI: Running at %dMHz.\n", | |
393 | (__raw_readw(chan->reg_base + PCI_STATUS) & PCI_STATUS_66MHZ) ? | |
394 | 66 : 33); | |
395 | ||
d0e3db40 | 396 | return 0; |
ef407bee PM |
397 | |
398 | err: | |
399 | sh7780_pci_teardown_irqs(chan); | |
400 | return ret; | |
5283ecb5 | 401 | } |
e79066a6 | 402 | arch_initcall(sh7780_pci_init); |