aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/common/vic.c
diff options
context:
space:
mode:
authorAlessandro Rubini <rubini@gnudd.com>2009-07-02 10:28:41 -0400
committerRussell King <rmk+kernel@arm.linux.org.uk>2009-07-02 16:20:43 -0400
commit87e8824b4588076409692b82ef4b1d98f25cd400 (patch)
tree2a825e8251776042164d65c769833a5908b2a8eb /arch/arm/common/vic.c
parent28d0325ce6e0a52f53d8af687e6427fee59004d3 (diff)
[ARM] 5582/1: VIC: support ST-modified version with a split init
The Nomadik SoC (not yet merged) has a modified PL090 VIC cell. This adds support for it by reading the PrimeCell ID at the end of the page and calling a separate init function. Signed-off-by: Alessandro Rubini <rubini@unipv.it> Acked-by: Andrea Gallo <andrea.gallo@stericsson.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm/common/vic.c')
-rw-r--r--arch/arm/common/vic.c88
1 files changed, 88 insertions, 0 deletions
diff --git a/arch/arm/common/vic.c b/arch/arm/common/vic.c
index 6ed89836e908..27714ab30f25 100644
--- a/arch/arm/common/vic.c
+++ b/arch/arm/common/vic.c
@@ -259,6 +259,15 @@ static struct irq_chip vic_chip = {
259 .set_wake = vic_set_wake, 259 .set_wake = vic_set_wake,
260}; 260};
261 261
262/* The PL190 cell from ARM has been modified by ST, so handle both here */
263static void vik_init_st(void __iomem *base, unsigned int irq_start,
264 u32 vic_sources);
265
266enum vic_vendor {
267 VENDOR_ARM = 0x41,
268 VENDOR_ST = 0x80,
269};
270
262/** 271/**
263 * vic_init - initialise a vectored interrupt controller 272 * vic_init - initialise a vectored interrupt controller
264 * @base: iomem base address 273 * @base: iomem base address
@@ -270,6 +279,28 @@ void __init vic_init(void __iomem *base, unsigned int irq_start,
270 u32 vic_sources, u32 resume_sources) 279 u32 vic_sources, u32 resume_sources)
271{ 280{
272 unsigned int i; 281 unsigned int i;
282 u32 cellid = 0;
283 enum vic_vendor vendor;
284
285 /* Identify which VIC cell this one is, by reading the ID */
286 for (i = 0; i < 4; i++) {
287 u32 addr = ((u32)base & PAGE_MASK) + 0xfe0 + (i * 4);
288 cellid |= (readl(addr) & 0xff) << (8 * i);
289 }
290 vendor = (cellid >> 12) & 0xff;
291 printk(KERN_INFO "VIC @%p: id 0x%08x, vendor 0x%02x\n",
292 base, cellid, vendor);
293
294 switch(vendor) {
295 case VENDOR_ST:
296 vik_init_st(base, irq_start, vic_sources);
297 return;
298 default:
299 printk(KERN_WARNING "VIC: unknown vendor, continuing anyways\n");
300 /* fall through */
301 case VENDOR_ARM:
302 break;
303 }
273 304
274 /* Disable all interrupts initially. */ 305 /* Disable all interrupts initially. */
275 306
@@ -306,3 +337,60 @@ void __init vic_init(void __iomem *base, unsigned int irq_start,
306 337
307 vic_pm_register(base, irq_start, resume_sources); 338 vic_pm_register(base, irq_start, resume_sources);
308} 339}
340
341/*
342 * The PL190 cell from ARM has been modified by ST to handle 64 interrupts.
343 * The original cell has 32 interrupts, while the modified one has 64,
344 * replocating two blocks 0x00..0x1f in 0x20..0x3f. In that case
345 * the probe function is called twice, with base set to offset 000
346 * and 020 within the page. We call this "second block".
347 */
348static void __init vik_init_st(void __iomem *base, unsigned int irq_start,
349 u32 vic_sources)
350{
351 unsigned int i;
352 int vic_2nd_block = ((unsigned long)base & ~PAGE_MASK) != 0;
353
354 /* Disable all interrupts initially. */
355
356 writel(0, base + VIC_INT_SELECT);
357 writel(0, base + VIC_INT_ENABLE);
358 writel(~0, base + VIC_INT_ENABLE_CLEAR);
359 writel(0, base + VIC_IRQ_STATUS);
360 writel(0, base + VIC_ITCR);
361 writel(~0, base + VIC_INT_SOFT_CLEAR);
362
363 /*
364 * Make sure we clear all existing interrupts. The vector registers
365 * in this cell are after the second block of general registers,
366 * so we can address them using standard offsets, but only from
367 * the second base address, which is 0x20 in the page
368 */
369 if (vic_2nd_block) {
370 writel(0, base + VIC_PL190_VECT_ADDR);
371 for (i = 0; i < 19; i++) {
372 unsigned int value;
373
374 value = readl(base + VIC_PL190_VECT_ADDR);
375 writel(value, base + VIC_PL190_VECT_ADDR);
376 }
377 /* ST has 16 vectors as well, but we don't enable them by now */
378 for (i = 0; i < 16; i++) {
379 void __iomem *reg = base + VIC_VECT_CNTL0 + (i * 4);
380 writel(0, reg);
381 }
382
383 writel(32, base + VIC_PL190_DEF_VECT_ADDR);
384 }
385
386 for (i = 0; i < 32; i++) {
387 if (vic_sources & (1 << i)) {
388 unsigned int irq = irq_start + i;
389
390 set_irq_chip(irq, &vic_chip);
391 set_irq_chip_data(irq, base);
392 set_irq_handler(irq, handle_level_irq);
393 set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
394 }
395 }
396}