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