summaryrefslogtreecommitdiffstats
path: root/arch/powerpc/kernel
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@g5.osdl.org>2006-07-03 15:28:34 -0700
committerLinus Torvalds <torvalds@g5.osdl.org>2006-07-03 15:28:34 -0700
commit912b2539e1e062cec73e2e61448e507f7719bd08 (patch)
tree233807569ee5e0ab3118dd54c0ae9164fec8343e /arch/powerpc/kernel
parent70b97a7f0b19cf1f2619deb5cc41e8b78c591aa7 (diff)
parent39ab9c212aac48f2744f2fd7722fa639ec048eb7 (diff)
Merge git://git.kernel.org/pub/scm/linux/kernel/git/paulus/powerpc
* git://git.kernel.org/pub/scm/linux/kernel/git/paulus/powerpc: powerpc: add defconfig for Freescale MPC8349E-mITX board powerpc: Add base support for the Freescale MPC8349E-mITX eval board Documentation: correct values in MPC8548E SEC example node [POWERPC] Actually copy over i8259.c to arch/ppc/syslib this time [POWERPC] Add new interrupt mapping core and change platforms to use it [POWERPC] Copy i8259 code back to arch/ppc [POWERPC] New device-tree interrupt parsing code [POWERPC] Use the genirq framework [PATCH] genirq: Allow fasteoi handler to retrigger disabled interrupts [POWERPC] Update the SWIM3 (powermac) floppy driver [POWERPC] Fix error handling in detecting legacy serial ports [POWERPC] Fix booting on Momentum "Apache" board (a Maple derivative) [POWERPC] Fix various offb and BootX-related issues [POWERPC] Add a default config for 32-bit CHRP machines [POWERPC] fix implicit declaration on cell. [POWERPC] change get_property to return void *
Diffstat (limited to 'arch/powerpc/kernel')
-rw-r--r--arch/powerpc/kernel/btext.c20
-rw-r--r--arch/powerpc/kernel/ibmebus.c9
-rw-r--r--arch/powerpc/kernel/irq.c653
-rw-r--r--arch/powerpc/kernel/legacy_serial.c57
-rw-r--r--arch/powerpc/kernel/misc_64.S10
-rw-r--r--arch/powerpc/kernel/pci_32.c37
-rw-r--r--arch/powerpc/kernel/pci_64.c33
-rw-r--r--arch/powerpc/kernel/prom.c454
-rw-r--r--arch/powerpc/kernel/prom_init.c20
-rw-r--r--arch/powerpc/kernel/prom_parse.c443
-rw-r--r--arch/powerpc/kernel/rtas_pci.c17
-rw-r--r--arch/powerpc/kernel/setup_32.c2
-rw-r--r--arch/powerpc/kernel/setup_64.c17
-rw-r--r--arch/powerpc/kernel/vio.c12
14 files changed, 1068 insertions, 716 deletions
diff --git a/arch/powerpc/kernel/btext.c b/arch/powerpc/kernel/btext.c
index a6920919d68..f4e5e14ee2b 100644
--- a/arch/powerpc/kernel/btext.c
+++ b/arch/powerpc/kernel/btext.c
@@ -111,7 +111,7 @@ void __init btext_setup_display(int width, int height, int depth, int pitch,
logicalDisplayBase = (unsigned char *)address;
dispDeviceBase = (unsigned char *)address;
dispDeviceRowBytes = pitch;
- dispDeviceDepth = depth;
+ dispDeviceDepth = depth == 15 ? 16 : depth;
dispDeviceRect[0] = dispDeviceRect[1] = 0;
dispDeviceRect[2] = width;
dispDeviceRect[3] = height;
@@ -160,20 +160,28 @@ int btext_initialize(struct device_node *np)
unsigned long address = 0;
u32 *prop;
- prop = (u32 *)get_property(np, "width", NULL);
+ prop = (u32 *)get_property(np, "linux,bootx-width", NULL);
+ if (prop == NULL)
+ prop = (u32 *)get_property(np, "width", NULL);
if (prop == NULL)
return -EINVAL;
width = *prop;
- prop = (u32 *)get_property(np, "height", NULL);
+ prop = (u32 *)get_property(np, "linux,bootx-height", NULL);
+ if (prop == NULL)
+ prop = (u32 *)get_property(np, "height", NULL);
if (prop == NULL)
return -EINVAL;
height = *prop;
- prop = (u32 *)get_property(np, "depth", NULL);
+ prop = (u32 *)get_property(np, "linux,bootx-depth", NULL);
+ if (prop == NULL)
+ prop = (u32 *)get_property(np, "depth", NULL);
if (prop == NULL)
return -EINVAL;
depth = *prop;
pitch = width * ((depth + 7) / 8);
- prop = (u32 *)get_property(np, "linebytes", NULL);
+ prop = (u32 *)get_property(np, "linux,bootx-linebytes", NULL);
+ if (prop == NULL)
+ prop = (u32 *)get_property(np, "linebytes", NULL);
if (prop)
pitch = *prop;
if (pitch == 1)
@@ -194,7 +202,7 @@ int btext_initialize(struct device_node *np)
g_max_loc_Y = height / 16;
dispDeviceBase = (unsigned char *)address;
dispDeviceRowBytes = pitch;
- dispDeviceDepth = depth;
+ dispDeviceDepth = depth == 15 ? 16 : depth;
dispDeviceRect[0] = dispDeviceRect[1] = 0;
dispDeviceRect[2] = width;
dispDeviceRect[3] = height;
diff --git a/arch/powerpc/kernel/ibmebus.c b/arch/powerpc/kernel/ibmebus.c
index e47d40ac6f3..97ddc02a3d4 100644
--- a/arch/powerpc/kernel/ibmebus.c
+++ b/arch/powerpc/kernel/ibmebus.c
@@ -323,13 +323,11 @@ int ibmebus_request_irq(struct ibmebus_dev *dev,
unsigned long irq_flags, const char * devname,
void *dev_id)
{
- unsigned int irq = virt_irq_create_mapping(ist);
+ unsigned int irq = irq_create_mapping(NULL, ist, 0);
if (irq == NO_IRQ)
return -EINVAL;
- irq = irq_offset_up(irq);
-
return request_irq(irq, handler,
irq_flags, devname, dev_id);
}
@@ -337,12 +335,9 @@ EXPORT_SYMBOL(ibmebus_request_irq);
void ibmebus_free_irq(struct ibmebus_dev *dev, u32 ist, void *dev_id)
{
- unsigned int irq = virt_irq_create_mapping(ist);
+ unsigned int irq = irq_find_mapping(NULL, ist);
- irq = irq_offset_up(irq);
free_irq(irq, dev_id);
-
- return;
}
EXPORT_SYMBOL(ibmebus_free_irq);
diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c
index e3774f6b57c..8cf987809c6 100644
--- a/arch/powerpc/kernel/irq.c
+++ b/arch/powerpc/kernel/irq.c
@@ -29,6 +29,8 @@
* to reduce code space and undefined function references.
*/
+#undef DEBUG
+
#include <linux/module.h>
#include <linux/threads.h>
#include <linux/kernel_stat.h>
@@ -46,7 +48,10 @@
#include <linux/cpumask.h>
#include <linux/profile.h>
#include <linux/bitops.h>
-#include <linux/pci.h>
+#include <linux/list.h>
+#include <linux/radix-tree.h>
+#include <linux/mutex.h>
+#include <linux/bootmem.h>
#include <asm/uaccess.h>
#include <asm/system.h>
@@ -57,39 +62,38 @@
#include <asm/prom.h>
#include <asm/ptrace.h>
#include <asm/machdep.h>
+#include <asm/udbg.h>
#ifdef CONFIG_PPC_ISERIES
#include <asm/paca.h>
#endif
int __irq_offset_value;
-#ifdef CONFIG_PPC32
-EXPORT_SYMBOL(__irq_offset_value);
-#endif
-
static int ppc_spurious_interrupts;
#ifdef CONFIG_PPC32
-#define NR_MASK_WORDS ((NR_IRQS + 31) / 32)
+EXPORT_SYMBOL(__irq_offset_value);
+atomic_t ppc_n_lost_interrupts;
+#ifndef CONFIG_PPC_MERGE
+#define NR_MASK_WORDS ((NR_IRQS + 31) / 32)
unsigned long ppc_cached_irq_mask[NR_MASK_WORDS];
-atomic_t ppc_n_lost_interrupts;
+#endif
#ifdef CONFIG_TAU_INT
extern int tau_initialized;
extern int tau_interrupts(int);
#endif
+#endif /* CONFIG_PPC32 */
#if defined(CONFIG_SMP) && !defined(CONFIG_PPC_MERGE)
extern atomic_t ipi_recv;
extern atomic_t ipi_sent;
#endif
-#endif /* CONFIG_PPC32 */
#ifdef CONFIG_PPC64
EXPORT_SYMBOL(irq_desc);
int distribute_irqs = 1;
-u64 ppc64_interrupt_controller;
#endif /* CONFIG_PPC64 */
int show_interrupts(struct seq_file *p, void *v)
@@ -182,7 +186,7 @@ void fixup_irqs(cpumask_t map)
void do_IRQ(struct pt_regs *regs)
{
- int irq;
+ unsigned int irq;
#ifdef CONFIG_IRQSTACKS
struct thread_info *curtp, *irqtp;
#endif
@@ -213,22 +217,26 @@ void do_IRQ(struct pt_regs *regs)
*/
irq = ppc_md.get_irq(regs);
- if (irq >= 0) {
+ if (irq != NO_IRQ && irq != NO_IRQ_IGNORE) {
#ifdef CONFIG_IRQSTACKS
/* Switch to the irq stack to handle this */
curtp = current_thread_info();
irqtp = hardirq_ctx[smp_processor_id()];
if (curtp != irqtp) {
+ struct irq_desc *desc = irq_desc + irq;
+ void *handler = desc->handle_irq;
+ if (handler == NULL)
+ handler = &__do_IRQ;
irqtp->task = curtp->task;
irqtp->flags = 0;
- call___do_IRQ(irq, regs, irqtp);
+ call_handle_irq(irq, desc, regs, irqtp, handler);
irqtp->task = NULL;
if (irqtp->flags)
set_bits(irqtp->flags, &curtp->flags);
} else
#endif
- __do_IRQ(irq, regs);
- } else if (irq != -2)
+ generic_handle_irq(irq, regs);
+ } else if (irq != NO_IRQ_IGNORE)
/* That's not SMP safe ... but who cares ? */
ppc_spurious_interrupts++;
@@ -245,191 +253,562 @@ void do_IRQ(struct pt_regs *regs)
void __init init_IRQ(void)
{
+ ppc_md.init_IRQ();
#ifdef CONFIG_PPC64
- static int once = 0;
+ irq_ctx_init();
+#endif
+}
+
+
+#ifdef CONFIG_IRQSTACKS
+struct thread_info *softirq_ctx[NR_CPUS] __read_mostly;
+struct thread_info *hardirq_ctx[NR_CPUS] __read_mostly;
+
+void irq_ctx_init(void)
+{
+ struct thread_info *tp;
+ int i;
+
+ for_each_possible_cpu(i) {
+ memset((void *)softirq_ctx[i], 0, THREAD_SIZE);
+ tp = softirq_ctx[i];
+ tp->cpu = i;
+ tp->preempt_count = SOFTIRQ_OFFSET;
+
+ memset((void *)hardirq_ctx[i], 0, THREAD_SIZE);
+ tp = hardirq_ctx[i];
+ tp->cpu = i;
+ tp->preempt_count = HARDIRQ_OFFSET;
+ }
+}
+
+static inline void do_softirq_onstack(void)
+{
+ struct thread_info *curtp, *irqtp;
+
+ curtp = current_thread_info();
+ irqtp = softirq_ctx[smp_processor_id()];
+ irqtp->task = curtp->task;
+ call_do_softirq(irqtp);
+ irqtp->task = NULL;
+}
- if (once)
+#else
+#define do_softirq_onstack() __do_softirq()
+#endif /* CONFIG_IRQSTACKS */
+
+void do_softirq(void)
+{
+ unsigned long flags;
+
+ if (in_interrupt())
return;
- once++;
+ local_irq_save(flags);
-#endif
- ppc_md.init_IRQ();
-#ifdef CONFIG_PPC64
- irq_ctx_init();
-#endif
+ if (local_softirq_pending())
+ do_softirq_onstack();
+
+ local_irq_restore(flags);
}
+EXPORT_SYMBOL(do_softirq);
+
-#ifdef CONFIG_PPC64
/*
- * Virtual IRQ mapping code, used on systems with XICS interrupt controllers.
+ * IRQ controller and virtual interrupts
*/
-#define UNDEFINED_IRQ 0xffffffff
-unsigned int virt_irq_to_real_map[NR_IRQS];
+#ifdef CONFIG_PPC_MERGE
-/*
- * Don't use virtual irqs 0, 1, 2 for devices.
- * The pcnet32 driver considers interrupt numbers < 2 to be invalid,
- * and 2 is the XICS IPI interrupt.
- * We limit virtual irqs to __irq_offet_value less than virt_irq_max so
- * that when we offset them we don't end up with an interrupt
- * number >= virt_irq_max.
- */
-#define MIN_VIRT_IRQ 3
+static LIST_HEAD(irq_hosts);
+static spinlock_t irq_big_lock = SPIN_LOCK_UNLOCKED;
-unsigned int virt_irq_max;
-static unsigned int max_virt_irq;
-static unsigned int nr_virt_irqs;
+struct irq_map_entry irq_map[NR_IRQS];
+static unsigned int irq_virq_count = NR_IRQS;
+static struct irq_host *irq_default_host;
-void
-virt_irq_init(void)
+struct irq_host *irq_alloc_host(unsigned int revmap_type,
+ unsigned int revmap_arg,
+ struct irq_host_ops *ops,
+ irq_hw_number_t inval_irq)
{
- int i;
+ struct irq_host *host;
+ unsigned int size = sizeof(struct irq_host);
+ unsigned int i;
+ unsigned int *rmap;
+ unsigned long flags;
+
+ /* Allocate structure and revmap table if using linear mapping */
+ if (revmap_type == IRQ_HOST_MAP_LINEAR)
+ size += revmap_arg * sizeof(unsigned int);
+ if (mem_init_done)
+ host = kzalloc(size, GFP_KERNEL);
+ else {
+ host = alloc_bootmem(size);
+ if (host)
+ memset(host, 0, size);
+ }
+ if (host == NULL)
+ return NULL;
- if ((virt_irq_max == 0) || (virt_irq_max > (NR_IRQS - 1)))
- virt_irq_max = NR_IRQS - 1;
- max_virt_irq = virt_irq_max - __irq_offset_value;
- nr_virt_irqs = max_virt_irq - MIN_VIRT_IRQ + 1;
+ /* Fill structure */
+ host->revmap_type = revmap_type;
+ host->inval_irq = inval_irq;
+ host->ops = ops;
- for (i = 0; i < NR_IRQS; i++)
- virt_irq_to_real_map[i] = UNDEFINED_IRQ;
+ spin_lock_irqsave(&irq_big_lock, flags);
+
+ /* If it's a legacy controller, check for duplicates and
+ * mark it as allocated (we use irq 0 host pointer for that
+ */
+ if (revmap_type == IRQ_HOST_MAP_LEGACY) {
+ if (irq_map[0].host != NULL) {
+ spin_unlock_irqrestore(&irq_big_lock, flags);
+ /* If we are early boot, we can't free the structure,
+ * too bad...
+ * this will be fixed once slab is made available early
+ * instead of the current cruft
+ */
+ if (mem_init_done)
+ kfree(host);
+ return NULL;
+ }
+ irq_map[0].host = host;
+ }
+
+ list_add(&host->link, &irq_hosts);
+ spin_unlock_irqrestore(&irq_big_lock, flags);
+
+ /* Additional setups per revmap type */
+ switch(revmap_type) {
+ case IRQ_HOST_MAP_LEGACY:
+ /* 0 is always the invalid number for legacy */
+ host->inval_irq = 0;
+ /* setup us as the host for all legacy interrupts */
+ for (i = 1; i < NUM_ISA_INTERRUPTS; i++) {
+ irq_map[i].hwirq = 0;
+ smp_wmb();
+ irq_map[i].host = host;
+ smp_wmb();
+
+ /* Clear some flags */
+ get_irq_desc(i)->status
+ &= ~(IRQ_NOREQUEST | IRQ_LEVEL);
+
+ /* Legacy flags are left to default at this point,
+ * one can then use irq_create_mapping() to
+ * explicitely change them
+ */
+ ops->map(host, i, i, 0);
+ }
+ break;
+ case IRQ_HOST_MAP_LINEAR:
+ rmap = (unsigned int *)(host + 1);
+ for (i = 0; i < revmap_arg; i++)
+ rmap[i] = IRQ_NONE;
+ host->revmap_data.linear.size = revmap_arg;
+ smp_wmb();
+ host->revmap_data.linear.revmap = rmap;
+ break;
+ default:
+ break;
+ }
+
+ pr_debug("irq: Allocated host of type %d @0x%p\n", revmap_type, host);
+
+ return host;
}
-/* Create a mapping for a real_irq if it doesn't already exist.
- * Return the virtual irq as a convenience.
- */
-int virt_irq_create_mapping(unsigned int real_irq)
+struct irq_host *irq_find_host(struct device_node *node)
{
- unsigned int virq, first_virq;
- static int warned;
+ struct irq_host *h, *found = NULL;
+ unsigned long flags;
+
+ /* We might want to match the legacy controller last since
+ * it might potentially be set to match all interrupts in
+ * the absence of a device node. This isn't a problem so far
+ * yet though...
+ */
+ spin_lock_irqsave(&irq_big_lock, flags);
+ list_for_each_entry(h, &irq_hosts, link)
+ if (h->ops->match == NULL || h->ops->match(h, node)) {
+ found = h;
+ break;
+ }
+ spin_unlock_irqrestore(&irq_big_lock, flags);
+ return found;
+}
+EXPORT_SYMBOL_GPL(irq_find_host);
+
+void irq_set_default_host(struct irq_host *host)
+{
+ pr_debug("irq: Default host set to @0x%p\n", host);
- if (ppc64_interrupt_controller == IC_OPEN_PIC)
- return real_irq; /* no mapping for openpic (for now) */
+ irq_default_host = host;
+}
- if (ppc64_interrupt_controller == IC_CELL_PIC)
- return real_irq; /* no mapping for iic either */
+void irq_set_virq_count(unsigned int count)
+{
+ pr_debug("irq: Trying to set virq count to %d\n", count);
- /* don't map interrupts < MIN_VIRT_IRQ */
- if (real_irq < MIN_VIRT_IRQ) {
- virt_irq_to_real_map[real_irq] = real_irq;
- return real_irq;
+ BUG_ON(count < NUM_ISA_INTERRUPTS);
+ if (count < NR_IRQS)
+ irq_virq_count = count;
+}
+
+unsigned int irq_create_mapping(struct irq_host *host,
+ irq_hw_number_t hwirq,
+ unsigned int flags)
+{
+ unsigned int virq, hint;
+
+ pr_debug("irq: irq_create_mapping(0x%p, 0x%lx, 0x%x)\n",
+ host, hwirq, flags);
+
+ /* Look for default host if nececssary */
+ if (host == NULL)
+ host = irq_default_host;
+ if (host == NULL) {
+ printk(KERN_WARNING "irq_create_mapping called for"
+ " NULL host, hwirq=%lx\n", hwirq);
+ WARN_ON(1);
+ return NO_IRQ;
}
+ pr_debug("irq: -> using host @%p\n", host);
- /* map to a number between MIN_VIRT_IRQ and max_virt_irq */
- virq = real_irq;
- if (virq > max_virt_irq)
- virq = (virq % nr_virt_irqs) + MIN_VIRT_IRQ;
-
- /* search for this number or a free slot */
- first_virq = virq;
- while (virt_irq_to_real_map[virq] != UNDEFINED_IRQ) {
- if (virt_irq_to_real_map[virq] == real_irq)
- return virq;
- if (++virq > max_virt_irq)
- virq = MIN_VIRT_IRQ;
- if (virq == first_virq)
- goto nospace; /* oops, no free slots */
+ /* Check if mapping already exist, if it does, call
+ * host->ops->map() to update the flags
+ */
+ virq = irq_find_mapping(host, hwirq);
+ if (virq != IRQ_NONE) {
+ pr_debug("irq: -> existing mapping on virq %d\n", virq);
+ host->ops->map(host, virq, hwirq, flags);
+ return virq;
}
- virt_irq_to_real_map[virq] = real_irq;
+ /* Get a virtual interrupt number */
+ if (host->revmap_type == IRQ_HOST_MAP_LEGACY) {
+ /* Handle legacy */
+ virq = (unsigned int)hwirq;
+ if (virq == 0 || virq >= NUM_ISA_INTERRUPTS)
+ return NO_IRQ;
+ return virq;
+ } else {
+ /* Allocate a virtual interrupt number */
+ hint = hwirq % irq_virq_count;
+ virq = irq_alloc_virt(host, 1, hint);
+ if (virq == NO_IRQ) {
+ pr_debug("irq: -> virq allocation failed\n");
+ return NO_IRQ;
+ }
+ }
+ pr_debug("irq: -> obtained virq %d\n", virq);
+
+ /* Clear some flags */
+ get_irq_desc(virq)->status &= ~(IRQ_NOREQUEST | IRQ_LEVEL);
+
+ /* map it */
+ if (host->ops->map(host, virq, hwirq, flags)) {
+ pr_debug("irq: -> mapping failed, freeing\n");
+ irq_free_virt(virq, 1);
+ return NO_IRQ;
+ }
+ smp_wmb();
+ irq_map[virq].hwirq = hwirq;
+ smp_mb();
return virq;
+}
+EXPORT_SYMBOL_GPL(irq_create_mapping);
+
+extern unsigned int irq_create_of_mapping(struct device_node *controller,
+ u32 *intspec, unsigned int intsize)
+{
+ struct irq_host *host;
+ irq_hw_number_t hwirq;
+ unsigned int flags = IRQ_TYPE_NONE;
- nospace:
- if (!warned) {
- printk(KERN_CRIT "Interrupt table is full\n");
- printk(KERN_CRIT "Increase virt_irq_max (currently %d) "
- "in your kernel sources and rebuild.\n", virt_irq_max);
- warned = 1;
+ if (controller == NULL)
+ host = irq_default_host;
+ else
+ host = irq_find_host(controller);
+ if (host == NULL)
+ return NO_IRQ;
+
+ /* If host has no translation, then we assume interrupt line */
+ if (host->ops->xlate == NULL)
+ hwirq = intspec[0];
+ else {
+ if (host->ops->xlate(host, controller, intspec, intsize,
+ &hwirq, &flags))
+ return NO_IRQ;
}
- return NO_IRQ;
+
+ return irq_create_mapping(host, hwirq, flags);
}
+EXPORT_SYMBOL_GPL(irq_create_of_mapping);
-/*
- * In most cases will get a hit on the very first slot checked in the
- * virt_irq_to_real_map. Only when there are a large number of
- * IRQs will this be expensive.
- */
-unsigned int real_irq_to_virt_slowpath(unsigned int real_irq)
+unsigned int irq_of_parse_and_map(struct device_node *dev, int index)
{
- unsigned int virq;
- unsigned int first_virq;
+ struct of_irq oirq;
- virq = real_irq;
+ if (of_irq_map_one(dev, index, &oirq))
+ return NO_IRQ;
- if (virq > max_virt_irq)
- virq = (virq % nr_virt_irqs) + MIN_VIRT_IRQ;
+ return irq_create_of_mapping(oirq.controller, oirq.specifier,
+ oirq.size);
+}
+EXPORT_SYMBOL_GPL(irq_of_parse_and_map);
- first_virq = virq;
+void irq_dispose_mapping(unsigned int virq)
+{
+ struct irq_host *host = irq_map[virq].host;
+ irq_hw_number_t hwirq;
+ unsigned long flags;
- do {
- if (virt_irq_to_real_map[virq] == real_irq)
- return virq;
+ WARN_ON (host == NULL);
+ if (host == NULL)
+ return;
- virq++;
+ /* Never unmap legacy interrupts */
+ if (host->revmap_type == IRQ_HOST_MAP_LEGACY)
+ return;
- if (virq >= max_virt_irq)
- virq = 0;
+ /* remove chip and handler */
+ set_irq_chip_and_handler(virq, NULL, NULL);
+
+ /* Make sure it's completed */
+ synchronize_irq(virq);
+
+ /* Tell the PIC about it */
+ if (host->ops->unmap)
+ host->ops->unmap(host, virq);
+ smp_mb();
+
+ /* Clear reverse map */
+ hwirq = irq_map[virq].hwirq;
+ switch(host->revmap_type) {
+ case IRQ_HOST_MAP_LINEAR:
+ if (hwirq < host->revmap_data.linear.size)
+ host->revmap_data.linear.revmap[hwirq] = IRQ_NONE;
+ break;
+ case IRQ_HOST_MAP_TREE:
+ /* Check if radix tree allocated yet */
+ if (host->revmap_data.tree.gfp_mask == 0)
+ break;
+ /* XXX radix tree not safe ! remove lock whem it becomes safe
+ * and use some RCU sync to make sure everything is ok before we
+ * can re-use that map entry
+ */
+ spin_lock_irqsave(&irq_big_lock, flags);
+ radix_tree_delete(&host->revmap_data.tree, hwirq);
+ spin_unlock_irqrestore(&irq_big_lock, flags);
+ break;
+ }
- } while (first_virq != virq);
+ /* Destroy map */
+ smp_mb();
+ irq_map[virq].hwirq = host->inval_irq;
- return NO_IRQ;
+ /* Set some flags */
+ get_irq_desc(virq)->status |= IRQ_NOREQUEST;
+ /* Free it */
+ irq_free_virt(virq, 1);
}
-#endif /* CONFIG_PPC64 */
+EXPORT_SYMBOL_GPL(irq_dispose_mapping);
-#ifdef CONFIG_IRQSTACKS
-struct thread_info *softirq_ctx[NR_CPUS] __read_mostly;
-struct thread_info *hardirq_ctx[NR_CPUS] __read_mostly;
+unsigned int irq_find_mapping(struct irq_host *host,
+ irq_hw_number_t hwirq)
+{
+ unsigned int i;
+ unsigned int hint = hwirq % irq_virq_count;
+
+ /* Look for default host if nececssary */
+ if (host == NULL)
+ host = irq_default_host;
+ if (host == NULL)
+ return NO_IRQ;
+
+ /* legacy -> bail early */
+ if (host->revmap_type == IRQ_HOST_MAP_LEGACY)
+ return hwirq;
+
+ /* Slow path does a linear search of the map */
+ if (hint < NUM_ISA_INTERRUPTS)
+ hint = NUM_ISA_INTERRUPTS;
+ i = hint;
+ do {
+ if (irq_map[i].host == host &&
+ irq_map[i].hwirq == hwirq)
+ return i;
+ i++;
+ if (i >= irq_virq_count)
+ i = NUM_ISA_INTERRUPTS;
+ } while(i != hint);
+ return NO_IRQ;
+}
+EXPORT_SYMBOL_GPL(irq_find_mapping);
-void irq_ctx_init(void)
+
+unsigned int irq_radix_revmap(struct irq_host *host,
+ irq_hw_number_t hwirq)
{
- struct thread_info *tp;
- int i;
+ struct radix_tree_root *tree;
+ struct irq_map_entry *ptr;
+ unsigned int virq;
+ unsigned long flags;
- for_each_possible_cpu(i) {
- memset((void *)softirq_ctx[i], 0, THREAD_SIZE);
- tp = softirq_ctx[i];
- tp->cpu = i;
- tp->preempt_count = SOFTIRQ_OFFSET;
+ WARN_ON(host->revmap_type != IRQ_HOST_MAP_TREE);
- memset((void *)hardirq_ctx[i], 0, THREAD_SIZE);
- tp = hardirq_ctx[i];
- tp->cpu = i;
- tp->preempt_count = HARDIRQ_OFFSET;
+ /* Check if the radix tree exist yet. We test the value of
+ * the gfp_mask for that. Sneaky but saves another int in the
+ * structure. If not, we fallback to slow mode
+ */
+ tree = &host->revmap_data.tree;
+ if (tree->gfp_mask == 0)
+ return irq_find_mapping(host, hwirq);
+
+ /* XXX Current radix trees are NOT SMP safe !!! Remove that lock
+ * when that is fixed (when Nick's patch gets in
+ */
+ spin_lock_irqsave(&irq_big_lock, flags);
+
+ /* Now try to resolve */
+ ptr = radix_tree_lookup(tree, hwirq);
+ /* Found it, return */
+ if (ptr) {
+ virq = ptr - irq_map;
+ goto bail;
}
+
+ /* If not there, try to insert it */
+ virq = irq_find_mapping(host, hwirq);
+ if (virq != NO_IRQ)
+ radix_tree_insert(tree, virq, &irq_map[virq]);
+ bail:
+ spin_unlock_irqrestore(&irq_big_lock, flags);
+ return virq;
}
-static inline void do_softirq_onstack(void)
+unsigned int irq_linear_revmap(struct irq_host *host,
+ irq_hw_number_t hwirq)
{
- struct thread_info *curtp, *irqtp;
+ unsigned int *revmap;
- curtp = current_thread_info();
- irqtp = softirq_ctx[smp_processor_id()];
- irqtp->task = curtp->task;
- call_do_softirq(irqtp);
- irqtp->task = NULL;
+ WARN_ON(host->revmap_type != IRQ_HOST_MAP_LINEAR);
+
+ /* Check revmap bounds */
+ if (unlikely(hwirq >= host->revmap_data.linear.size))
+ return irq_find_mapping(host, hwirq);
+
+ /* Check if revmap was allocated */
+ revmap = host->revmap_data.linear.revmap;
+ if (unlikely(revmap == NULL))
+ return irq_find_mapping(host, hwirq);
+
+ /* Fill up revmap with slow path if no mapping found */
+ if (unlikely(revmap[hwirq] == NO_IRQ))
+ revmap[hwirq] = irq_find_mapping(host, hwirq);
+
+ return revmap[hwirq];
}
-#else
-#define do_softirq_onstack() __do_softirq()
-#endif /* CONFIG_IRQSTACKS */
+unsigned int irq_alloc_virt(struct irq_host *host,
+ unsigned int count,
+ unsigned int hint)
+{
+ unsigned long flags;
+ unsigned int i, j, found = NO_IRQ;
+ unsigned int limit = irq_virq_count - count;
-void do_softirq(void)
+ if (count == 0 || count > (irq_virq_count - NUM_ISA_INTERRUPTS))
+ return NO_IRQ;
+
+ spin_lock_irqsave(&irq_big_lock, flags);
+
+ /* Use hint for 1 interrupt if any */
+ if (count == 1 && hint >= NUM_ISA_INTERRUPTS &&
+ hint < irq_virq_count && irq_map[hint].host == NULL) {
+ found = hint;
+ goto hint_found;
+ }
+
+ /* Look for count consecutive numbers in the allocatable
+ * (non-legacy) space
+ */
+ for (i = NUM_ISA_INTERRUPTS; i <= limit; ) {
+ for (j = i; j < (i + count); j++)
+ if (irq_map[j].host != NULL) {
+ i = j + 1;
+ continue;
+ }
+ found = i;
+ break;
+ }
+ if (found == NO_IRQ) {
+ spin_unlock_irqrestore(&irq_big_lock, flags);
+ return NO_IRQ;
+ }
+ hint_found:
+ for (i = found; i < (found + count); i++) {
+ irq_map[i].hwirq = host->inval_irq;
+ smp_wmb();
+ irq_map[i].host = host;
+ }
+ spin_unlock_irqrestore(&irq_big_lock, flags);
+ return found;
+}
+
+void irq_free_virt(unsigned int virq, unsigned int count)
{
unsigned long flags;
+ unsigned int i;
- if (in_interrupt())
- return;
+ WARN_ON (virq < NUM_ISA_INTERRUPTS);
+ WARN_ON (count == 0 || (virq + count) > irq_virq_count);
- local_irq_save(flags);
+ spin_lock_irqsave(&irq_big_lock, flags);
+ for (i = virq; i < (virq + count); i++) {
+ struct irq_host *host;
- if (local_softirq_pending())
- do_softirq_onstack();
+ if (i < NUM_ISA_INTERRUPTS ||
+ (virq + count) > irq_virq_count)
+ continue;
- local_irq_restore(flags);
+ host = irq_map[i].host;
+ irq_map[i].hwirq = host->inval_irq;
+ smp_wmb();
+ irq_map[i].host = NULL;
+ }
+ spin_unlock_irqrestore(&irq_big_lock, flags);
}
-EXPORT_SYMBOL(do_softirq);
+
+void irq_early_init(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < NR_IRQS; i++)
+ get_irq_desc(i)->status |= IRQ_NOREQUEST;
+}
+
+/* We need to create the radix trees late */
+static int irq_late_init(void)
+{
+ struct irq_host *h;
+ unsigned long flags;
+
+ spin_lock_irqsave(&irq_big_lock, flags);
+ list_for_each_entry(h, &irq_hosts, link) {
+ if (h->revmap_type == IRQ_HOST_MAP_TREE)
+ INIT_RADIX_TREE(&h->revmap_data.tree, GFP_ATOMIC);
+ }
+ spin_unlock_irqrestore(&irq_big_lock, flags);
+
+ return 0;
+}
+arch_initcall(irq_late_init);
+
+#endif /* CONFIG_PPC_MERGE */
#ifdef CONFIG_PCI_MSI
int pci_enable_msi(struct pci_dev * pdev)
diff --git a/arch/powerpc/kernel/legacy_serial.c b/arch/powerpc/kernel/legacy_serial.c
index 4cf0b971976..7e98e778b52 100644
--- a/arch/powerpc/kernel/legacy_serial.c
+++ b/arch/powerpc/kernel/legacy_serial.c
@@ -28,6 +28,7 @@ static struct legacy_serial_info {
struct device_node *np;
unsigned int speed;
unsigned int clock;
+ int irq_check_parent;
phys_addr_t taddr;
} legacy_serial_infos[MAX_LEGACY_SERIAL_PORTS];
static unsigned int legacy_serial_count;
@@ -36,7 +37,7 @@ static int legacy_serial_console = -1;
static int __init add_legacy_port(struct device_node *np, int want_index,
int iotype, phys_addr_t base,
phys_addr_t taddr, unsigned long irq,
- upf_t flags)
+ upf_t flags, int irq_check_parent)
{
u32 *clk, *spd, clock = BASE_BAUD * 16;
int index;
@@ -68,7 +69,7 @@ static int __init add_legacy_port(struct device_node *np, int want_index,
if (legacy_serial_infos[index].np != 0) {
/* if we still have some room, move it, else override */
if (legacy_serial_count < MAX_LEGACY_SERIAL_PORTS) {
- printk(KERN_INFO "Moved legacy port %d -> %d\n",
+ printk(KERN_DEBUG "Moved legacy port %d -> %d\n",
index, legacy_serial_count);
legacy_serial_ports[legacy_serial_count] =
legacy_serial_ports[index];
@@ -76,7 +77,7 @@ static int __init add_legacy_port(struct device_node *np, int want_index,
legacy_serial_infos[index];
legacy_serial_count++;
} else {
- printk(KERN_INFO "Replacing legacy port %d\n", index);
+ printk(KERN_DEBUG "Replacing legacy port %d\n", index);
}
}
@@ -95,10 +96,11 @@ static int __init add_legacy_port(struct device_node *np, int want_index,
legacy_serial_infos[index].np = of_node_get(np);
legacy_serial_infos[index].clock = clock;
legacy_serial_infos[index].speed = spd ? *spd : 0;
+ legacy_serial_infos[index].irq_check_parent = irq_check_parent;
- printk(KERN_INFO "Found legacy serial port %d for %s\n",
+ printk(KERN_DEBUG "Found legacy serial port %d for %s\n",
index, np->full_name);
- printk(KERN_INFO " %s=%llx, taddr=%llx, irq=%lx, clk=%d, speed=%d\n",
+ printk(KERN_DEBUG " %s=%llx, taddr=%llx, irq=%lx, clk=%d, speed=%d\n",
(iotype == UPIO_PORT) ? "port" : "mem",
(unsigned long long)base, (unsigned long long)taddr, irq,
legacy_serial_ports[index].uartclk,
@@ -126,11 +128,13 @@ static int __init add_legacy_soc_port(struct device_node *np,
return -1;
addr = of_translate_address(soc_dev, addrp);
+ if (addr == OF_BAD_ADDR)
+ return -1;
/* Add port, irq will be dealt with later. We passed a translated
* IO port value. It will be fixed up later along with the irq
*/
- return add_legacy_port(np, -1, UPIO_MEM, addr, addr, NO_IRQ, flags);
+ return add_legacy_port(np, -1, UPIO_MEM, addr, addr, NO_IRQ, flags, 0);
}
static int __init add_legacy_isa_port(struct device_node *np,
@@ -141,6 +145,8 @@ static int __init add_legacy_isa_port(struct device_node *np,
int index = -1;
phys_addr_t taddr;
+ DBG(" -> add_legacy_isa_port(%s)\n", np->full_name);
+
/* Get the ISA port number */
reg = (u32 *)get_property(np, "reg", NULL);
if (reg == NULL)
@@ -161,9 +167,12 @@ static int __init add_legacy_isa_port(struct device_node *np,
/* Translate ISA address */
taddr = of_translate_address(np, reg);
+ if (taddr == OF_BAD_ADDR)
+ return -1;
/* Add port, irq will be dealt with later */
- return add_legacy_port(np, index, UPIO_PORT, reg[1], taddr, NO_IRQ, UPF_BOOT_AUTOCONF);
+ return add_legacy_port(np, index, UPIO_PORT, reg[1], taddr,
+ NO_IRQ, UPF_BOOT_AUTOCONF, 0);
}
@@ -176,6 +185,8 @@ static int __init add_legacy_pci_port(struct device_node *np,
unsigned int flags;
int iotype, index = -1, lindex = 0;
+ DBG(" -> add_legacy_pci_port(%s)\n", np->full_name);
+
/* We only support ports that have a clock frequency properly
* encoded in the device-tree (that is have an fcode). Anything
* else can't be used that early and will be normally probed by
@@ -194,6 +205,8 @@ static int __init add_legacy_pci_port(struct device_node *np,
/* We only support BAR 0 for now */
iotype = (flags & IORESOURCE_MEM) ? UPIO_MEM : UPIO_PORT;
addr = of_translate_address(pci_dev, addrp);
+ if (addr == OF_BAD_ADDR)
+ return -1;
/* Set the IO base to the same as the translated address for MMIO,
* or to the domain local IO base for PIO (it will be fixed up later)
@@ -231,7 +244,8 @@ static int __init add_legacy_pci_port(struct device_node *np,
/* Add port, irq will be dealt with later. We passed a translated
* IO port value. It will be fixed up later along with the irq
*/
- return add_legacy_port(np, index, iotype, base, addr, NO_IRQ, UPF_BOOT_AUTOCONF);
+ return add_legacy_port(np, index, iotype, base, addr, NO_IRQ,
+ UPF_BOOT_AUTOCONF, np != pci_dev);
}
#endif
@@ -362,27 +376,22 @@ static void __init fixup_port_irq(int index,
struct device_node *np,
struct plat_serial8250_port *port)
{
+ unsigned int virq;
+
DBG("fixup_port_irq(%d)\n", index);
- /* Check for interrupts in that node */
- if (np->n_intrs > 0) {
- port->irq = np->intrs[0].line;
- DBG(" port %d (%s), irq=%d\n",
- index, np->full_name, port->irq);
- return;
+ virq = irq_of_parse_and_map(np, 0);
+ if (virq == NO_IRQ && legacy_serial_infos[index].irq_check_parent) {
+ np = of_get_parent(np);
+ if (np == NULL)
+ return;
+ virq = irq_of_parse_and_map(np, 0);
+ of_node_put(np);
}
-
- /* Check for interrupts in the parent */
- np = of_get_parent(np);
- if (np == NULL)
+ if (virq == NO_IRQ)
return;
- if (np->n_intrs > 0) {
- port->irq = np->intrs[0].line;
- DBG(" port %d (%s), irq=%d\n",
- index, np->full_name, port->irq);
- }
- of_node_put(np);
+ port->irq = virq;
}
static void __init fixup_port_pio(int index,
diff --git a/arch/powerpc/kernel/misc_64.S b/arch/powerpc/kernel/misc_64.S
index 0c3c70d115c..bfb407fc1aa 100644
--- a/arch/powerpc/kernel/misc_64.S
+++ b/arch/powerpc/kernel/misc_64.S
@@ -51,12 +51,14 @@ _GLOBAL(call_do_softirq)
mtlr r0
blr
-_GLOBAL(call___do_IRQ)
+_GLOBAL(call_handle_irq)
+ ld r8,0(r7)
mflr r0
std r0,16(r1)
- stdu r1,THREAD_SIZE-112(r5)
- mr r1,r5
- bl .__do_IRQ
+ mtctr r8
+ stdu r1,THREAD_SIZE-112(r6)
+ mr r1,r6
+ bctrl
ld r1,0(r1)
ld r0,16(r1)
mtlr r0
diff --git a/arch/powerpc/kernel/pci_32.c b/arch/powerpc/kernel/pci_32.c
index 1333335c474..898dae8ab6d 100644
--- a/arch/powerpc/kernel/pci_32.c
+++ b/arch/powerpc/kernel/pci_32.c
@@ -1404,6 +1404,43 @@ pcibios_update_irq(struct pci_dev *dev, int irq)
/* XXX FIXME - update OF device tree node interrupt property */
}
+#ifdef CONFIG_PPC_MERGE
+/* XXX This is a copy of the ppc64 version. This is temporary until we start
+ * merging the 2 PCI layers
+ */
+/*
+ * Reads the interrupt pin to determine if interrupt is use by card.
+ * If the interrupt is used, then gets the interrupt line from the
+ * openfirmware and sets it in the pci_dev and pci_config line.
+ */
+int pci_read_irq_line(struct pci_dev *pci_dev)
+{
+ struct of_irq oirq;
+ unsigned int virq;
+
+ DBG("Try to map irq for %s...\n", pci_name(pci_dev));
+
+ if (of_irq_map_pci(pci_dev, &oirq)) {
+ DBG(" -> failed !\n");
+ return -1;
+ }
+
+ DBG(" -> got one, spec %d cells (0x%08x...) on %s\n",
+ oirq.size, oirq.specifier[0], oirq.controller->full_name);
+
+ virq = irq_create_of_mapping(oirq.controller, oirq.specifier, oirq.size);
+ if(virq == NO_IRQ) {
+ DBG(" -> failed to map !\n");
+ return -1;
+ }
+ pci_dev->irq = virq;
+ pci_write_config_byte(pci_dev, PCI_INTERRUPT_LINE, virq);
+
+ return 0;
+}
+EXPORT_SYMBOL(pci_read_irq_line);
+#endif /* CONFIG_PPC_MERGE */
+
int pcibios_enable_device(struct pci_dev *dev, int mask)
{
u16 cmd, old_cmd;
diff --git a/arch/powerpc/kernel/pci_64.c b/arch/powerpc/kernel/pci_64.c
index bea8451fb57..efc0b5559ee 100644
--- a/arch/powerpc/kernel/pci_64.c
+++ b/arch/powerpc/kernel/pci_64.c
@@ -398,12 +398,8 @@ struct pci_dev *of_create_pci_dev(struct device_node *node,
} else {
dev->hdr_type = PCI_HEADER_TYPE_NORMAL;
dev->rom_base_reg = PCI_ROM_ADDRESS;
+ /* Maybe do a default OF mapping here */
dev->irq = NO_IRQ;
- if (node->n_intrs > 0) {
- dev->irq = node->intrs[0].line;
- pci_write_config_byte(dev, PCI_INTERRUPT_LINE,
- dev->irq);
- }
}
pci_parse_of_addrs(node, dev);
@@ -1288,23 +1284,26 @@ EXPORT_SYMBOL(pcibios_fixup_bus);
*/
int pci_read_irq_line(struct pci_dev *pci_dev)
{
- u8 intpin;
- struct device_node *node;
-
- pci_read_config_byte(pci_dev, PCI_INTERRUPT_PIN, &intpin);
- if (intpin == 0)
- return 0;
+ struct of_irq oirq;
+ unsigned int virq;
- node = pci_device_to_OF_node(pci_dev);
- if (node == NULL)
- return -1;
+ DBG("Try to map irq for %s...\n", pci_name(pci_dev));
- if (node->n_intrs == 0)
+ if (of_irq_map_pci(pci_dev, &oirq)) {
+ DBG(" -> failed !\n");
return -1;
+ }
- pci_dev->irq = node->intrs[0].line;
+ DBG(" -> got one, spec %d cells (0x%08x...) on %s\n",
+ oirq.size, oirq.specifier[0], oirq.controller->full_name);
- pci_write_config_byte(pci_dev, PCI_INTERRUPT_LINE, pci_dev->irq);
+ virq = irq_create_of_mapping(oirq.controller, oirq.specifier, oirq.size);
+ if(virq == NO_IRQ) {
+ DBG(" -> failed to map !\n");
+ return -1;
+ }
+ pci_dev->irq = virq;
+ pci_write_config_byte(pci_dev, PCI_INTERRUPT_LINE, virq);
return 0;
}
diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c
index 4c524cb5218..a1787ffb631 100644
--- a/arch/powerpc/kernel/prom.c
+++ b/arch/powerpc/kernel/prom.c
@@ -30,6 +30,7 @@
#include <linux/module.h>
#include <linux/kexec.h>
#include <linux/debugfs.h>
+#include <linux/irq.h>
#include <asm/prom.h>
#include <asm/rtas.h>
@@ -86,424 +87,6 @@ static DEFINE_RWLOCK(devtree_lock);
/* export that to outside world */
struct device_node *of_chosen;
-struct device_node *dflt_interrupt_controller;
-int num_interrupt_controllers;
-
-/*
- * Wrapper for allocating memory for various data that needs to be
- * attached to device nodes as they are processed at boot or when
- * added to the device tree later (e.g. DLPAR). At boot there is
- * already a region reserved so we just increment *mem_start by size;
- * otherwise we call kmalloc.
- */
-static void * prom_alloc(unsigned long size, unsigned long *mem_start)
-{
- unsigned long tmp;
-
- if (!mem_start)
- return kmalloc(size, GFP_KERNEL);
-
- tmp = *mem_start;
- *mem_start += size;
- return (void *)tmp;
-}
-
-/*
- * Find the device_node with a given phandle.
- */
-static struct device_node * find_phandle(phandle ph)
-{
- struct device_node *np;
-
- for (np = allnodes; np != 0; np = np->allnext)
- if (np->linux_phandle == ph)
- return np;
- return NULL;
-}
-
-/*
- * Find the interrupt parent of a node.
- */
-static struct device_node * __devinit intr_parent(struct device_node *p)
-{
- phandle *parp;
-
- parp = (phandle *) get_property(p, "interrupt-parent", NULL);
- if (parp == NULL)
- return p->parent;
- p = find_phandle(*parp);
- if (p != NULL)
- return p;
- /*
- * On a powermac booted with BootX, we don't get to know the
- * phandles for any nodes, so find_phandle will return NULL.
- * Fortunately these machines only have one interrupt controller
- * so there isn't in fact any ambiguity. -- paulus
- */
- if (num_interrupt_controllers == 1)
- p = dflt_interrupt_controller;
- return p;
-}
-
-/*
- * Find out the size of each entry of the interrupts property
- * for a node.
- */
-int __devinit prom_n_intr_cells(struct device_node *np)
-{
- struct device_node *p;
- unsigned int *icp;
-
- for (p = np; (p = intr_parent(p)) != NULL; ) {
- icp = (unsigned int *)
- get_property(p, "#interrupt-cells", NULL);
- if (icp != NULL)
- return *icp;
- if (get_property(p, "interrupt-controller", NULL) != NULL
- || get_property(p, "interrupt-map", NULL) != NULL) {
- printk("oops, node %s doesn't have #interrupt-cells\n",
- p->full_name);
- return 1;
- }
- }
-#ifdef DEBUG_IRQ
- printk("prom_n_intr_cells failed for %s\n", np->full_name);
-#endif
- return 1;
-}
-
-/*
- * Map an interrupt from a device up to the platform interrupt
- * descriptor.
- */
-static int __devinit map_interrupt(unsigned int **irq, struct device_node **ictrler,
- struct device_node *np, unsigned int *ints,
- int nintrc)
-{
- struct device_node *p, *ipar;
- unsigned int *imap, *imask, *ip;
- int i, imaplen, match;
- int newintrc = 0, newaddrc = 0;
- unsigned int *reg;
- int naddrc;
-
- reg = (unsigned int *) get_property(np, "reg", NULL);
- naddrc = prom_n_addr_cells(np);
- p = intr_parent(np);
- while (p != NULL) {
- if (get_property(p, "interrupt-controller", NULL) != NULL)
- /* this node is an interrupt controller, stop here */
- break;
- imap = (unsigned int *)
- get_property(p, "interrupt-map", &imaplen);
- if (imap == NULL) {
- p = intr_parent(p);
- continue;
- }
- imask = (unsigned int *)
- get_property(p, "interrupt-map-mask", NULL);
- if (imask == NULL) {
- printk("oops, %s has interrupt-map but no mask\n",
- p->full_name);
- return 0;
- }
- imaplen /= sizeof(unsigned int);
- match = 0;
- ipar = NULL;
- while (imaplen > 0 && !match) {
- /* check the child-interrupt field */
- match = 1;
- for (i = 0; i < naddrc && match; ++i)
- match = ((reg[i] ^ imap[i]) & imask[i]) == 0;
- for (; i < naddrc + nintrc && match; ++i)
- match = ((ints[i-naddrc] ^ imap[i]) & imask[i]) == 0;
- imap += naddrc + nintrc;
- imaplen -= naddrc + nintrc;
- /* grab the interrupt parent */
- ipar = find_phandle((phandle) *imap++);
- --imaplen;
- if (ipar == NULL && num_interrupt_controllers == 1)
- /* cope with BootX not giving us phandles */
- ipar = dflt_interrupt_controller;
- if (ipar == NULL) {
- printk("oops, no int parent %x in map of %s\n",
- imap[-1], p->full_name);
- return 0;
- }
- /* find the parent's # addr and intr cells */
- ip = (unsigned int *)
- get_property(ipar, "#interrupt-cells", NULL);
- if (ip == NULL) {
- printk("oops, no #interrupt-cells on %s\n",
- ipar->full_name);
- return 0;
- }
- newintrc = *ip;
- ip = (unsigned int *)
- get_property(ipar, "#address-cells", NULL);
- newaddrc = (ip == NULL)? 0: *ip;
- imap += newaddrc + newintrc;
- imaplen -= newaddrc + newintrc;
- }
- if (imaplen < 0) {
- printk("oops, error decoding int-map on %s, len=%d\n",
- p->full_name, imaplen);
- return 0;
- }
- if (!match) {
-#ifdef DEBUG_IRQ
- printk("oops, no match in %s int-map for %s\n",
- p->full_name, np->full_name);
-#endif
- return 0;
- }
- p = ipar;
- naddrc = newaddrc;
- nintrc = newintrc;
- ints = imap - nintrc;
- reg = ints - naddrc;
- }
- if (p == NULL) {
-#ifdef DEBUG_IRQ
- printk("hmmm, int tree for %s doesn't have ctrler\n",
- np->full_name);
-#endif
- return 0;
- }
- *irq = ints;
- *ictrler = p;
- return nintrc;
-}
-
-static unsigned char map_isa_senses[4] = {
- IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE,
- IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE,
- IRQ_SENSE_EDGE | IRQ_POLARITY_NEGATIVE,
- IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE
-};
-
-static unsigned char map_mpic_senses[4] = {
- IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE,
- IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE,
- /* 2 seems to be used for the 8259 cascade... */
- IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE,
- IRQ_SENSE_EDGE | IRQ_POLARITY_NEGATIVE,
-};
-
-static int __devinit finish_node_interrupts(struct device_node *np,
- unsigned long *mem_start,
- int measure_only)
-{
- unsigned int *ints;
- int intlen, intrcells, intrcount;
- int i, j, n, sense;
- unsigned int *irq, virq;
- struct device_node *ic;
- int trace = 0;
-
- //#define TRACE(fmt...) do { if (trace) { printk(fmt); mdelay(1000); } } while(0)
-#define TRACE(fmt...)
-
- if (!strcmp(np->name, "smu-doorbell"))
- trace = 1;
-
- TRACE("Finishing SMU doorbell ! num_interrupt_controllers = %d\n",
- num_interrupt_controllers);
-
- if (num_interrupt_controllers == 0) {
- /*
- * Old machines just have a list of interrupt numbers
- * and no interrupt-controller nodes.
- */
- ints = (unsigned int *) get_property(np, "AAPL,interrupts",
- &intlen);
- /* XXX old interpret_pci_props looked in parent too */
- /* XXX old interpret_macio_props looked for interrupts
- before AAPL,interrupts */
- if (ints == NULL)
- ints = (unsigned int *) get_property(np, "interrupts",
- &intlen);
- if (ints == NULL)
- return 0;
-
- np->n_intrs = intlen / sizeof(unsigned int);
- np->intrs = prom_alloc(np->n_intrs * sizeof(np->intrs[0]),
- mem_start);
- if (!np->intrs)
- return -ENOMEM;
- if (measure_only)
- return 0;
-
- for (i = 0; i < np->n_intrs; ++i) {
- np->intrs[i].line = *ints++;
- np->intrs[i].sense = IRQ_SENSE_LEVEL
- | IRQ_POLARITY_NEGATIVE;
- }
- return 0;
- }
-
- ints = (unsigned int *) get_property(np, "interrupts", &intlen);
- TRACE("ints=%p, intlen=%d\n", ints, intlen);
- if (ints == NULL)
- return 0;
- intrcells = prom_n_intr_cells(np);
- intlen /= intrcells * sizeof(unsigned int);
- TRACE("intrcells=%d, new intlen=%d\n", intrcells, intlen);
- np->intrs = prom_alloc(intlen * sizeof(*(np->intrs)), mem_start);
- if (!np->intrs)
- return -ENOMEM;
-
- if (measure_only)
- return 0;
-
- intrcount = 0;
- for (i = 0; i < intlen; ++i, ints += intrcells) {
- n = map_interrupt(&irq, &ic, np, ints, intrcells);
- TRACE("map, irq=%d, ic=%p, n=%d\n", irq, ic, n);
- if (n <= 0)
- continue;
-
- /* don't map IRQ numbers under a cascaded 8259 controller */
- if (ic && device_is_compatible(ic, "chrp,iic")) {
- np->intrs[intrcount].line = irq[0];
- sense = (n > 1)? (irq[1] & 3): 3;
- np->intrs[intrcount].sense = map_isa_senses[sense];
- } else {
- virq = virt_irq_create_mapping(irq[0]);
- TRACE("virq=%d\n", virq);
-#ifdef CONFIG_PPC64
- if (virq == NO_IRQ) {
- printk(KERN_CRIT "Could not allocate interrupt"
- " number for %s\n", np->full_name);
- continue;
- }
-#endif
- np->intrs[intrcount].line = irq_offset_up(virq);
- sense = (n > 1)? (irq[1] & 3): 1;
-
- /* Apple uses bits in there in a different way, let's
- * only keep the real sense bit on macs
- */
- if (machine_is(powermac))
- sense &= 0x1;
- np->intrs[intrcount].sense = map_mpic_senses[sense];
- }
-
-#ifdef CONFIG_PPC64
- /* We offset irq numbers for the u3 MPIC by 128 in PowerMac */
- if (machine_is(powermac) && ic && ic->parent) {
- char *name = get_property(ic->parent, "name", NULL);
- if (name && !strcmp(name, "u3"))
- np->intrs[intrcount].line += 128;
- else if (!(name && (!strcmp(name, "mac-io") ||
- !strcmp(name, "u4"))))
- /* ignore other cascaded controllers, such as
- the k2-sata-root */
- break;
- }
-#endif /* CONFIG_PPC64 */
- if (n > 2) {
- printk("hmmm, got %d intr cells for %s:", n,
- np->full_name);
- for (j = 0; j < n; ++j)
- printk(" %d", irq[j]);
- printk("\n");
- }
- ++intrcount;
- }
- np->n_intrs = intrcount;
-
- return 0;
-}
-
-static int __devinit finish_node(struct device_node *np,
- unsigned long *mem_start,
- int measure_only)
-{
- struct device_node *child;
- int rc = 0;
-
- rc = finish_node_interrupts(np, mem_start, measure_only);
- if (rc)
- goto out;
-
- for (child = np->child; child != NULL; child = child->sibling) {
- rc = finish_node(child, mem_start, measure_only);
- if (rc)
- goto out;
- }
-out:
- return rc;
-}
-
-static void __init scan_interrupt_controllers(void)
-{
- struct device_node *np;
- int n = 0;
- char *name, *ic;
- int iclen;
-
- for (np = allnodes; np != NULL; np = np->allnext) {
- ic = get_property(np, "interrupt-controller", &iclen);
- name = get_property(np, "name", NULL);
- /* checking iclen makes sure we don't get a false
- match on /chosen.interrupt_controller */
- if ((name != NULL
- && strcmp(name, "interrupt-controller") == 0)
- || (ic != NULL && iclen == 0
- && strcmp(name, "AppleKiwi"))) {
- if (n == 0)
- dflt_interrupt_controller = np;
- ++n;
- }
- }
- num_interrupt_controllers = n;
-}
-
-/**
- * finish_device_tree is called once things are running normally
- * (i.e. with text and data mapped to the address they were linked at).
- * It traverses the device tree and fills in some of the additional,
- * fields in each node like {n_}addrs and {n_}intrs, the virt interrupt
- * mapping is also initialized at this point.
- */
-void __init finish_device_tree(void)
-{
- unsigned long start, end, size = 0;
-
- DBG(" -> finish_device_tree\n");
-
-#ifdef CONFIG_PPC64
- /* Initialize virtual IRQ map */
- virt_irq_init();
-#endif
- scan_interrupt_controllers();
-
- /*
- * Finish device-tree (pre-parsing some properties etc...)
- * We do this in 2 passes. One with "measure_only" set, which
- * will only measure the amount of memory needed, then we can
- * allocate that memory, and call finish_node again. However,
- * we must be careful as most routines will fail nowadays when
- * prom_alloc() returns 0, so we must make sure our first pass
- * doesn't start at 0. We pre-initialize size to 16 for that
- * reason and then remove those additional 16 bytes
- */
- size = 16;
- finish_node(allnodes, &size, 1);
- size -= 16;
-
- if (0 == size)
- end = start = 0;
- else
- end = start = (unsigned long)__va(lmb_alloc(size, 128));
-
- finish_node(allnodes, &end, 0);
- BUG_ON(end != start + size);
-
- DBG(" <- finish_device_tree\n");
-}
-
static inline char *find_flat_dt_string(u32 offset)
{
return ((char *)initial_boot_params) +
@@ -1389,27 +972,6 @@ prom_n_size_cells(struct device_node* np)
EXPORT_SYMBOL(prom_n_size_cells);
/**
- * Work out the sense (active-low level / active-high edge)
- * of each interrupt from the device tree.
- */
-void __init prom_get_irq_senses(unsigned char *senses, int off, int max)
-{
- struct device_node *np;
- int i, j;
-
- /* default to level-triggered */
- memset(senses, IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE, max - off);
-
- for (np = allnodes; np != 0; np = np->allnext) {
- for (j = 0; j < np->n_intrs; j++) {
- i = np->intrs[j].line;
- if (i >= off && i < max)
- senses[i-off] = np->intrs[j].sense;
- }
- }
-}
-
-/**
* Construct and return a list of the device_nodes with a given name.
*/
struct device_node *find_devices(const char *name)
@@ -1808,7 +1370,6 @@ static void of_node_release(struct kref *kref)
node->deadprops = NULL;
}
}
- kfree(node->intrs);
kfree(node->full_name);
kfree(node->data);
kfree(node);
@@ -1881,13 +1442,7 @@ void of_detach_node(const struct device_node *np)
#ifdef CONFIG_PPC_PSERIES
/*
* Fix up the uninitialized fields in a new device node:
- * name, type, n_addrs, addrs, n_intrs, intrs, and pci-specific fields
- *
- * A lot of boot-time code is duplicated here, because functions such
- * as finish_node_interrupts, interpret_pci_props, etc. cannot use the
- * slab allocator.
- *
- * This should probably be split up into smaller chunks.
+ * name, type and pci-specific fields
*/
static int of_finish_dynamic_node(struct device_node *node)
@@ -1928,8 +1483,6 @@ static int prom_reconfig_notifier(struct notifier_block *nb,
switch (action) {
case PSERIES_RECONFIG_ADD:
err = of_finish_dynamic_node(node);
- if (!err)
- finish_node(node, NULL, 0);
if (err < 0) {
printk(KERN_ERR "finish_node returned %d\n", err);
err = NOTIFY_BAD;
@@ -1975,8 +1528,7 @@ struct property *of_find_property(struct device_node *np, const char *name,
* Find a property with a given name for a given node
* and return the value.
*/
-unsigned char *get_property(struct device_node *np, const char *name,
- int *lenp)
+void *get_property(struct device_node *np, const char *name, int *lenp)
{
struct property *pp = of_find_property(np,name,lenp);
return pp ? pp->value : NULL;
diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c
index 1e95a9f8cda..ebd501a59ab 100644
--- a/arch/powerpc/kernel/prom_init.c
+++ b/arch/powerpc/kernel/prom_init.c
@@ -1990,12 +1990,22 @@ static void __init flatten_device_tree(void)
static void __init fixup_device_tree_maple(void)
{
phandle isa;
+ u32 rloc = 0x01002000; /* IO space; PCI device = 4 */
u32 isa_ranges[6];
-
- isa = call_prom("finddevice", 1, 1, ADDR("/ht@0/isa@4"));
+ char *name;
+
+ name = "/ht@0/isa@4";
+ isa = call_prom("finddevice", 1, 1, ADDR(name));
+ if (!PHANDLE_VALID(isa)) {
+ name = "/ht@0/isa@6";
+ isa = call_prom("finddevice", 1, 1, ADDR(name));
+ rloc = 0x01003000; /* IO space; PCI device = 6 */
+ }
if (!PHANDLE_VALID(isa))
return;
+ if (prom_getproplen(isa, "ranges") != 12)
+ return;
if (prom_getprop(isa, "ranges", isa_ranges, sizeof(isa_ranges))
== PROM_ERROR)
return;
@@ -2005,15 +2015,15 @@ static void __init fixup_device_tree_maple(void)
isa_ranges[2] != 0x00010000)
return;
- prom_printf("fixing up bogus ISA range on Maple...\n");
+ prom_printf("Fixing up bogus ISA range on Maple/Apache...\n");
isa_ranges[0] = 0x1;
isa_ranges[1] = 0x0;
- isa_ranges[2] = 0x01002000; /* IO space; PCI device = 4 */
+ isa_ranges[2] = rloc;
isa_ranges[3] = 0x0;
isa_ranges[4] = 0x0;
isa_ranges[5] = 0x00010000;
- prom_setprop(isa, "/ht@0/isa@4", "ranges",
+ prom_setprop(isa, name, "ranges",
isa_ranges, sizeof(isa_ranges));
}
#else
diff --git a/arch/powerpc/kernel/prom_parse.c b/arch/powerpc/kernel/prom_parse.c
index 45df420383c..21009b1f786 100644
--- a/arch/powerpc/kernel/prom_parse.c
+++ b/arch/powerpc/kernel/prom_parse.c
@@ -38,14 +38,6 @@ static void of_dump_addr(const char *s, u32 *addr, int na)
static void of_dump_addr(const char *s, u32 *addr, int na) { }
#endif
-/* Read a big address */
-static inline u64 of_read_addr(u32 *cell, int size)
-{
- u64 r = 0;
- while (size--)
- r = (r << 32) | *(cell++);
- return r;
-}
/* Callbacks for bus specific translators */
struct of_bus {
@@ -77,9 +69,9 @@ static u64 of_bus_default_map(u32 *addr, u32 *range, int na, int ns, int pna)
{
u64 cp, s, da;
- cp = of_read_addr(range, na);
- s = of_read_addr(range + na + pna, ns);
- da = of_read_addr(addr, na);
+ cp = of_read_number(range, na);
+ s = of_read_number(range + na + pna, ns);
+ da = of_read_number(addr, na);
DBG("OF: default map, cp="PRu64", s="PRu64", da="PRu64"\n",
cp, s, da);
@@ -91,7 +83,7 @@ static u64 of_bus_default_map(u32 *addr, u32 *range, int na, int ns, int pna)
static int of_bus_default_translate(u32 *addr, u64 offset, int na)
{
- u64 a = of_read_addr(addr, na);
+ u64 a = of_read_number(addr, na);
memset(addr, 0, na * 4);
a += offset;
if (na > 1)
@@ -135,9 +127,9 @@ static u64 of_bus_pci_map(u32 *addr, u32 *range, int na, int ns, int pna)
return OF_BAD_ADDR;
/* Read address values, skipping high cell */
- cp = of_read_addr(range + 1, na - 1);
- s = of_read_addr(range + na + pna, ns);
- da = of_read_addr(addr + 1, na - 1);
+ cp = of_read_number(range + 1, na - 1);
+ s = of_read_number(range + na + pna, ns);
+ da = of_read_number(addr + 1, na - 1);
DBG("OF: PCI map, cp="PRu64", s="PRu64", da="PRu64"\n", cp, s, da);
@@ -195,9 +187,9 @@ static u64 of_bus_isa_map(u32 *addr, u32 *range, int na, int ns, int pna)
return OF_BAD_ADDR;
/* Read address values, skipping high cell */
- cp = of_read_addr(range + 1, na - 1);
- s = of_read_addr(range + na + pna, ns);
- da = of_read_addr(addr + 1, na - 1);
+ cp = of_read_number(range + 1, na - 1);
+ s = of_read_number(range + na + pna, ns);
+ da = of_read_number(addr + 1, na - 1);
DBG("OF: ISA map, cp="PRu64", s="PRu64", da="PRu64"\n", cp, s, da);
@@ -295,7 +287,7 @@ static int of_translate_one(struct device_node *parent, struct of_bus *bus,
*/
ranges = (u32 *)get_property(parent, "ranges", &rlen);
if (ranges == NULL || rlen == 0) {
- offset = of_read_addr(addr, na);
+ offset = of_read_number(addr, na);
memset(addr, 0, pna * 4);
DBG("OF: no ranges, 1:1 translation\n");
goto finish;
@@ -378,7 +370,7 @@ u64 of_translate_address(struct device_node *dev, u32 *in_addr)
/* If root, we have finished */
if (parent == NULL) {
DBG("OF: reached root node\n");
- result = of_read_addr(addr, na);
+ result = of_read_number(addr, na);
break;
}
@@ -442,7 +434,7 @@ u32 *of_get_address(struct device_node *dev, int index, u64 *size,
for (i = 0; psize >= onesize; psize -= onesize, prop += onesize, i++)
if (i == index) {
if (size)
- *size = of_read_addr(prop + na, ns);
+ *size = of_read_number(prop + na, ns);
if (flags)
*flags = bus->get_flags(prop);
return prop;
@@ -484,7 +476,7 @@ u32 *of_get_pci_address(struct device_node *dev, int bar_no, u64 *size,
for (i = 0; psize >= onesize; psize -= onesize, prop += onesize, i++)
if ((prop[0] & 0xff) == ((bar_no * 4) + PCI_BASE_ADDRESS_0)) {
if (size)
- *size = of_read_addr(prop + na, ns);
+ *size = of_read_number(prop + na, ns);
if (flags)
*flags = bus->get_flags(prop);
return prop;
@@ -565,11 +557,414 @@ void of_parse_dma_window(struct device_node *dn, unsigned char *dma_window_prop,
prop = get_property(dn, "#address-cells", NULL);
cells = prop ? *(u32 *)prop : prom_n_addr_cells(dn);
- *phys = of_read_addr(dma_window, cells);
+ *phys = of_read_number(dma_window, cells);
dma_window += cells;
prop = get_property(dn, "ibm,#dma-size-cells", NULL);
cells = prop ? *(u32 *)prop : prom_n_size_cells(dn);
- *size = of_read_addr(dma_window, cells);
+ *size = of_read_number(dma_window, cells);
+}
+
+/*
+ * Interrupt remapper
+ */
+
+static unsigned int of_irq_workarounds;
+static struct device_node *of_irq_dflt_pic;
+
+static struct device_node *of_irq_find_parent(struct device_node *child)
+{
+ struct device_node *p;
+ phandle *parp;
+
+ if (!of_node_get(child))
+ return NULL;
+
+ do {
+ parp = (phandle *)get_property(child, "interrupt-parent", NULL);
+ if (parp == NULL)
+ p = of_get_parent(child);
+ else {
+ if (of_irq_workarounds & OF_IMAP_NO_PHANDLE)
+ p = of_node_get(of_irq_dflt_pic);
+ else
+ p = of_find_node_by_phandle(*parp);
+ }
+ of_node_put(child);
+ child = p;
+ } while (p && get_property(p, "#interrupt-cells", NULL) == NULL);
+
+ return p;
+}
+
+static u8 of_irq_pci_swizzle(u8 slot, u8 pin)
+{
+ return (((pin - 1) + slot) % 4) + 1;
}
+
+/* This doesn't need to be called if you don't have any special workaround
+ * flags to pass
+ */
+void of_irq_map_init(unsigned int flags)
+{
+ of_irq_workarounds = flags;
+
+ /* OldWorld, don't bother looking at other things */
+ if (flags & OF_IMAP_OLDWORLD_MAC)
+ return;
+
+ /* If we don't have phandles, let's try to locate a default interrupt
+ * controller (happens when booting with BootX). We do a first match
+ * here, hopefully, that only ever happens on machines with one
+ * controller.
+ */
+ if (flags & OF_IMAP_NO_PHANDLE) {
+ struct device_node *np;
+
+ for(np = NULL; (np = of_find_all_nodes(np)) != NULL;) {
+ if (get_property(np, "interrupt-controller", NULL)
+ == NULL)
+ continue;
+ /* Skip /chosen/interrupt-controller */
+ if (strcmp(np->name, "chosen") == 0)
+ continue;
+ /* It seems like at least one person on this planet wants
+ * to use BootX on a machine with an AppleKiwi controller
+ * which happens to pretend to be an interrupt
+ * controller too.
+ */
+ if (strcmp(np->name, "AppleKiwi") == 0)
+ continue;
+ /* I think we found one ! */
+ of_irq_dflt_pic = np;
+ break;
+ }
+ }
+
+}
+
+int of_irq_map_raw(struct device_node *parent, u32 *intspec, u32 *addr,
+ struct of_irq *out_irq)
+{
+ struct device_node *ipar, *tnode, *old = NULL, *newpar = NULL;
+ u32 *tmp, *imap, *imask;
+ u32 intsize = 1, addrsize, newintsize = 0, newaddrsize = 0;
+ int imaplen, match, i;
+
+ ipar = of_node_get(parent);
+
+ /* First get the #interrupt-cells property of the current cursor
+ * that tells us how to interpret the passed-in intspec. If there
+ * is none, we are nice and just walk up the tree
+ */
+ do {
+ tmp = (u32 *)get_property(ipar, "#interrupt-cells", NULL);
+ if (tmp != NULL) {
+ intsize = *tmp;
+ break;
+ }
+ tnode = ipar;
+ ipar = of_irq_find_parent(ipar);
+ of_node_put(tnode);
+ } while (ipar);
+ if (ipar == NULL) {
+ DBG(" -> no parent found !\n");
+ goto fail;
+ }
+
+ DBG("of_irq_map_raw: ipar=%s, size=%d\n", ipar->full_name, intsize);
+
+ /* Look for this #address-cells. We have to implement the old linux
+ * trick of looking for the parent here as some device-trees rely on it
+ */
+ old = of_node_get(ipar);
+ do {
+ tmp = (u32 *)get_property(old, "#address-cells", NULL);
+ tnode = of_get_parent(old);
+ of_node_put(old);
+ old = tnode;
+ } while(old && tmp == NULL);
+ of_node_put(old);
+ old = NULL;
+ addrsize = (tmp == NULL) ? 2 : *tmp;
+
+ DBG(" -> addrsize=%d\n", addrsize);
+
+ /* Now start the actual "proper" walk of the interrupt tree */
+ while (ipar != NULL) {
+ /* Now check if cursor is an interrupt-controller and if it is
+ * then we are done
+ */
+ if (get_property(ipar, "interrupt-controller", NULL) != NULL) {
+ DBG(" -> got it !\n");
+ memcpy(out_irq->specifier, intspec,
+ intsize * sizeof(u32));
+ out_irq->size = intsize;
+ out_irq->controller = ipar;
+ of_node_put(old);
+ return 0;
+ }
+
+ /* Now look for an interrupt-map */
+ imap = (u32 *)get_property(ipar, "interrupt-map", &imaplen);
+ /* No interrupt map, check for an interrupt parent */
+ if (imap == NULL) {
+ DBG(" -> no map, getting parent\n");
+ newpar = of_irq_find_parent(ipar);
+ goto skiplevel;
+ }
+ imaplen /= sizeof(u32);
+
+ /* Look for a mask */
+ imask = (u32 *)get_property(ipar, "interrupt-map-mask", NULL);
+
+ /* If we were passed no "reg" property and we attempt to parse
+ * an interrupt-map, then #address-cells must be 0.
+ * Fail if it's not.
+ */
+ if (addr == NULL && addrsize != 0) {
+ DBG(" -> no reg passed in when needed !\n");
+ goto fail;
+ }
+
+ /* Parse interrupt-map */
+ match = 0;
+ while (imaplen > (addrsize + intsize + 1) && !match) {
+ /* Compare specifiers */
+ match = 1;
+ for (i = 0; i < addrsize && match; ++i) {
+ u32 mask = imask ? imask[i] : 0xffffffffu;
+ match = ((addr[i] ^ imap[i]) & mask) == 0;
+ }
+ for (; i < (addrsize + intsize) && match; ++i) {
+ u32 mask = imask ? imask[i] : 0xffffffffu;
+ match =
+ ((intspec[i-addrsize] ^ imap[i]) & mask) == 0;
+ }
+ imap += addrsize + intsize;
+ imaplen -= addrsize + intsize;
+
+ DBG(" -> match=%d (imaplen=%d)\n", match, imaplen);
+
+ /* Get the interrupt parent */
+ if (of_irq_workarounds & OF_IMAP_NO_PHANDLE)
+ newpar = of_node_get(of_irq_dflt_pic);
+ else
+ newpar = of_find_node_by_phandle((phandle)*imap);
+ imap++;
+ --imaplen;
+
+ /* Check if not found */
+ if (newpar == NULL) {
+ DBG(" -> imap parent not found !\n");
+ goto fail;
+ }
+
+ /* Get #interrupt-cells and #address-cells of new
+ * parent
+ */
+ tmp = (u32 *)get_property(newpar, "#interrupt-cells",
+ NULL);
+ if (tmp == NULL) {
+ DBG(" -> parent lacks #interrupt-cells !\n");
+ goto fail;
+ }
+ newintsize = *tmp;
+ tmp = (u32 *)get_property(newpar, "#address-cells",
+ NULL);
+ newaddrsize = (tmp == NULL) ? 0 : *tmp;
+
+ DBG(" -> newintsize=%d, newaddrsize=%d\n",
+ newintsize, newaddrsize);
+
+ /* Check for malformed properties */
+ if (imaplen < (newaddrsize + newintsize))
+ goto fail;
+
+ imap += newaddrsize + newintsize;
+ imaplen -= newaddrsize + newintsize;
+
+ DBG(" -> imaplen=%d\n", imaplen);
+ }
+ if (!match)
+ goto fail;
+
+ of_node_put(old);
+ old = of_node_get(newpar);
+ addrsize = newaddrsize;
+ intsize = newintsize;
+ intspec = imap - intsize;
+ addr = intspec - addrsize;
+
+ skiplevel:
+ /* Iterate again with new parent */
+ DBG(" -> new parent: %s\n", newpar ? newpar->full_name : "<>");
+ of_node_put(ipar);
+ ipar = newpar;
+ newpar = NULL;
+ }
+ fail:
+ of_node_put(ipar);
+ of_node_put(old);
+ of_node_put(newpar);
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(of_irq_map_raw);
+
+#if defined(CONFIG_PPC_PMAC) && defined(CONFIG_PPC32)
+static int of_irq_map_oldworld(struct device_node *device, int index,
+ struct of_irq *out_irq)
+{
+ u32 *ints;
+ int intlen;
+
+ /*
+ * Old machines just have a list of interrupt numbers
+ * and no interrupt-controller nodes.
+ */
+ ints = (u32 *) get_property(device, "AAPL,interrupts", &intlen);
+ if (ints == NULL)
+ return -EINVAL;
+ intlen /= sizeof(u32);
+
+ if (index >= intlen)
+ return -EINVAL;
+
+ out_irq->controller = NULL;
+ out_irq->specifier[0] = ints[index];
+ out_irq->size = 1;
+
+ return 0;
+}
+#else /* defined(CONFIG_PPC_PMAC) && defined(CONFIG_PPC32) */
+static int of_irq_map_oldworld(struct device_node *device, int index,
+ struct of_irq *out_irq)
+{
+ return -EINVAL;
+}
+#endif /* !(defined(CONFIG_PPC_PMAC) && defined(CONFIG_PPC32)) */
+
+int of_irq_map_one(struct device_node *device, int index, struct of_irq *out_irq)
+{
+ struct device_node *p;
+ u32 *intspec, *tmp, intsize, intlen, *addr;
+ int res;
+
+ DBG("of_irq_map_one: dev=%s, index=%d\n", device->full_name, index);
+
+ /* OldWorld mac stuff is "special", handle out of line */
+ if (of_irq_workarounds & OF_IMAP_OLDWORLD_MAC)
+ return of_irq_map_oldworld(device, index, out_irq);
+
+ /* Get the interrupts property */
+ intspec = (u32 *)get_property(device, "interrupts", &intlen);
+ if (intspec == NULL)
+ return -EINVAL;
+ intlen /= sizeof(u32);
+
+ /* Get the reg property (if any) */
+ addr = (u32 *)get_property(device, "reg", NULL);
+
+ /* Look for the interrupt parent. */
+ p = of_irq_find_parent(device);
+ if (p == NULL)
+ return -EINVAL;
+
+ /* Get size of interrupt specifier */
+ tmp = (u32 *)get_property(p, "#interrupt-cells", NULL);
+ if (tmp == NULL) {
+ of_node_put(p);
+ return -EINVAL;
+ }
+ intsize = *tmp;
+
+ /* Check index */
+ if (index * intsize >= intlen)
+ return -EINVAL;
+
+ /* Get new specifier and map it */
+ res = of_irq_map_raw(p, intspec + index * intsize, addr, out_irq);
+ of_node_put(p);
+ return res;
+}
+EXPORT_SYMBOL_GPL(of_irq_map_one);
+
+int of_irq_map_pci(struct pci_dev *pdev, struct of_irq *out_irq)
+{
+ struct device_node *dn, *ppnode;
+ struct pci_dev *ppdev;
+ u32 lspec;
+ u32 laddr[3];
+ u8 pin;
+ int rc;
+
+ /* Check if we have a device node, if yes, fallback to standard OF
+ * parsing
+ */
+ dn = pci_device_to_OF_node(pdev);
+ if (dn)
+ return of_irq_map_one(dn, 0, out_irq);
+
+ /* Ok, we don't, time to have fun. Let's start by building up an
+ * interrupt spec. we assume #interrupt-cells is 1, which is standard
+ * for PCI. If you do different, then don't use that routine.
+ */
+ rc = pci_read_config_byte(pdev, PCI_INTERRUPT_PIN, &pin);
+ if (rc != 0)
+ return rc;
+ /* No pin, exit */
+ if (pin == 0)
+ return -ENODEV;
+
+ /* Now we walk up the PCI tree */
+ lspec = pin;
+ for (;;) {
+ /* Get the pci_dev of our parent */
+ ppdev = pdev->bus->self;
+
+ /* Ouch, it's a host bridge... */
+ if (ppdev == NULL) {
+#ifdef CONFIG_PPC64
+ ppnode = pci_bus_to_OF_node(pdev->bus);
+#else
+ struct pci_controller *host;
+ host = pci_bus_to_host(pdev->bus);
+ ppnode = host ? host->arch_data : NULL;
+#endif
+ /* No node for host bridge ? give up */
+ if (ppnode == NULL)
+ return -EINVAL;
+ } else
+ /* We found a P2P bridge, check if it has a node */
+ ppnode = pci_device_to_OF_node(ppdev);
+
+ /* Ok, we have found a parent with a device-node, hand over to
+ * the OF parsing code.
+ * We build a unit address from the linux device to be used for
+ * resolution. Note that we use the linux bus number which may
+ * not match your firmware bus numbering.
+ * Fortunately, in most cases, interrupt-map-mask doesn't include
+ * the bus number as part of the matching.
+ * You should still be careful about that though if you intend
+ * to rely on this function (you ship a firmware that doesn't
+ * create device nodes for all PCI devices).
+ */
+ if (ppnode)
+ break;
+
+ /* We can only get here if we hit a P2P bridge with no node,
+ * let's do standard swizzling and try again
+ */
+ lspec = of_irq_pci_swizzle(PCI_SLOT(pdev->devfn), lspec);
+ pdev = ppdev;
+ }
+
+ laddr[0] = (pdev->bus->number << 16)
+ | (pdev->devfn << 8);
+ laddr[1] = laddr[2] = 0;
+ return of_irq_map_raw(ppnode, &lspec, laddr, out_irq);
+}
+EXPORT_SYMBOL_GPL(of_irq_map_pci);
+
diff --git a/arch/powerpc/kernel/rtas_pci.c b/arch/powerpc/kernel/rtas_pci.c
index 6eb7e49b394..cda02265732 100644
--- a/arch/powerpc/kernel/rtas_pci.c
+++ b/arch/powerpc/kernel/rtas_pci.c
@@ -297,19 +297,9 @@ unsigned long __init find_and_init_phbs(void)
struct device_node *node;
struct pci_controller *phb;
unsigned int index;
- unsigned int root_size_cells = 0;
- unsigned int *opprop = NULL;
struct device_node *root = of_find_node_by_path("/");
- if (ppc64_interrupt_controller == IC_OPEN_PIC) {
- opprop = (unsigned int *)get_property(root,
- "platform-open-pic", NULL);
- }
-
- root_size_cells = prom_n_size_cells(root);
-
index = 0;
-
for (node = of_get_next_child(root, NULL);
node != NULL;
node = of_get_next_child(root, node)) {
@@ -324,13 +314,6 @@ unsigned long __init find_and_init_phbs(void)
setup_phb(node, phb);
pci_process_bridge_OF_ranges(phb, node, 0);
pci_setup_phb_io(phb, index == 0);
-#ifdef CONFIG_PPC_PSERIES
- /* XXX This code need serious fixing ... --BenH */
- if (ppc64_interrupt_controller == IC_OPEN_PIC && pSeries_mpic) {
- int addr = root_size_cells * (index + 2) - 1;
- mpic_assign_isu(pSeries_mpic, index, opprop[addr]);
- }
-#endif
index++;
}
diff --git a/arch/powerpc/kernel/setup_32.c b/arch/powerpc/kernel/setup_32.c
index ba7cd50d820..e0df2ba1ab9 100644
--- a/arch/powerpc/kernel/setup_32.c
+++ b/arch/powerpc/kernel/setup_32.c
@@ -51,7 +51,6 @@
extern void bootx_init(unsigned long r4, unsigned long phys);
-boot_infos_t *boot_infos;
struct ide_machdep_calls ppc_ide_md;
int boot_cpuid;
@@ -240,7 +239,6 @@ void __init setup_arch(char **cmdline_p)
ppc_md.init_early();
find_legacy_serial_ports();
- finish_device_tree();
smp_setup_cpu_maps();
diff --git a/arch/powerpc/kernel/setup_64.c b/arch/powerpc/kernel/setup_64.c
index ac7276c4068..fd1785e4c9b 100644
--- a/arch/powerpc/kernel/setup_64.c
+++ b/arch/powerpc/kernel/setup_64.c
@@ -361,12 +361,15 @@ void __init setup_system(void)
/*
* Fill the ppc64_caches & systemcfg structures with informations
- * retrieved from the device-tree. Need to be called before
- * finish_device_tree() since the later requires some of the
- * informations filled up here to properly parse the interrupt tree.
+ * retrieved from the device-tree.
*/
initialize_cache_info();
+ /*
+ * Initialize irq remapping subsystem
+ */
+ irq_early_init();
+
#ifdef CONFIG_PPC_RTAS
/*
* Initialize RTAS if available
@@ -394,12 +397,6 @@ void __init setup_system(void)
find_legacy_serial_ports();
/*
- * "Finish" the device-tree, that is do the actual parsing of
- * some of the properties like the interrupt map
- */
- finish_device_tree();
-
- /*
* Initialize xmon
*/
#ifdef CONFIG_XMON_DEFAULT
@@ -427,8 +424,6 @@ void __init setup_system(void)
printk("-----------------------------------------------------\n");
printk("ppc64_pft_size = 0x%lx\n", ppc64_pft_size);
- printk("ppc64_interrupt_controller = 0x%ld\n",
- ppc64_interrupt_controller);
printk("physicalMemorySize = 0x%lx\n", lmb_phys_mem_size());
printk("ppc64_caches.dcache_line_size = 0x%x\n",
ppc64_caches.dline_size);
diff --git a/arch/powerpc/kernel/vio.c b/arch/powerpc/kernel/vio.c
index cdf5867838a..fad8580f908 100644
--- a/arch/powerpc/kernel/vio.c
+++ b/arch/powerpc/kernel/vio.c
@@ -218,7 +218,6 @@ struct vio_dev * __devinit vio_register_device_node(struct device_node *of_node)
{
struct vio_dev *viodev;
unsigned int *unit_address;
- unsigned int *irq_p;
/* we need the 'device_type' property, in order to match with drivers */
if (of_node->type == NULL) {
@@ -243,16 +242,7 @@ struct vio_dev * __devinit vio_register_device_node(struct device_node *of_node)
viodev->dev.platform_data = of_node_get(of_node);
- viodev->irq = NO_IRQ;
- irq_p = (unsigned int *)get_property(of_node, "interrupts", NULL);
- if (irq_p) {
- int virq = virt_irq_create_mapping(*irq_p);
- if (virq == NO_IRQ) {
- printk(KERN_ERR "Unable to allocate interrupt "
- "number for %s\n", of_node->full_name);
- } else
- viodev->irq = irq_offset_up(virq);
- }
+ viodev->irq = irq_of_parse_and_map(of_node, 0);
snprintf(viodev->dev.bus_id, BUS_ID_SIZE, "%x", *unit_address);
viodev->name = of_node->name;