* in a separate file
*
* Copyright (C) 1997 Paul Mackerras (paulus@samba.org)
- *
- * Maintained by Benjamin Herrenschmidt (benh@kernel.crashing.org)
+ * Copyright (C) 2005 Benjamin Herrenschmidt (benh@kernel.crashing.org)
+ * IBM, Corp.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
*
*/
-#include <linux/config.h>
#include <linux/stddef.h>
#include <linux/init.h>
#include <linux/sched.h>
};
/* Default addresses */
-static volatile struct pmac_irq_hw *pmac_irq_hw[4] = {
- (struct pmac_irq_hw *) 0xf3000020,
- (struct pmac_irq_hw *) 0xf3000010,
- (struct pmac_irq_hw *) 0xf4000020,
- (struct pmac_irq_hw *) 0xf4000010,
-};
+static volatile struct pmac_irq_hw __iomem *pmac_irq_hw[4];
#define GC_LEVEL_MASK 0x3ff00000
#define OHARE_LEVEL_MASK 0x1ff00000
static DEFINE_SPINLOCK(pmac_pic_lock);
-#define GATWICK_IRQ_POOL_SIZE 10
-static struct interrupt_info gatwick_int_pool[GATWICK_IRQ_POOL_SIZE];
-
#define NR_MASK_WORDS ((NR_IRQS + 31) / 32)
static unsigned long ppc_lost_interrupts[NR_MASK_WORDS];
+static unsigned long ppc_cached_irq_mask[NR_MASK_WORDS];
+static int pmac_irq_cascade = -1;
+static struct irq_host *pmac_pic_host;
-/*
- * Mark an irq as "lost". This is only used on the pmac
- * since it can lose interrupts (see pmac_set_irq_mask).
- * -- Cort
- */
-void
-__set_lost(unsigned long irq_nr, int nokick)
+static void __pmac_retrigger(unsigned int irq_nr)
{
- if (!test_and_set_bit(irq_nr, ppc_lost_interrupts)) {
+ if (irq_nr >= max_real_irqs && pmac_irq_cascade > 0) {
+ __set_bit(irq_nr, ppc_lost_interrupts);
+ irq_nr = pmac_irq_cascade;
+ mb();
+ }
+ if (!__test_and_set_bit(irq_nr, ppc_lost_interrupts)) {
atomic_inc(&ppc_n_lost_interrupts);
- if (!nokick)
- set_dec(1);
+ set_dec(1);
}
}
-static void
-pmac_mask_and_ack_irq(unsigned int irq_nr)
+static void pmac_mask_and_ack_irq(unsigned int virq)
{
- unsigned long bit = 1UL << (irq_nr & 0x1f);
- int i = irq_nr >> 5;
+ unsigned int src = irq_map[virq].hwirq;
+ unsigned long bit = 1UL << (virq & 0x1f);
+ int i = virq >> 5;
unsigned long flags;
- if ((unsigned)irq_nr >= max_irqs)
- return;
-
- clear_bit(irq_nr, ppc_cached_irq_mask);
- if (test_and_clear_bit(irq_nr, ppc_lost_interrupts))
- atomic_dec(&ppc_n_lost_interrupts);
spin_lock_irqsave(&pmac_pic_lock, flags);
+ __clear_bit(src, ppc_cached_irq_mask);
+ if (__test_and_clear_bit(src, ppc_lost_interrupts))
+ atomic_dec(&ppc_n_lost_interrupts);
out_le32(&pmac_irq_hw[i]->enable, ppc_cached_irq_mask[i]);
out_le32(&pmac_irq_hw[i]->ack, bit);
do {
spin_unlock_irqrestore(&pmac_pic_lock, flags);
}
-static void pmac_set_irq_mask(unsigned int irq_nr, int nokicklost)
+static void pmac_ack_irq(unsigned int virq)
+{
+ unsigned int src = irq_map[virq].hwirq;
+ unsigned long bit = 1UL << (src & 0x1f);
+ int i = src >> 5;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pmac_pic_lock, flags);
+ if (__test_and_clear_bit(src, ppc_lost_interrupts))
+ atomic_dec(&ppc_n_lost_interrupts);
+ out_le32(&pmac_irq_hw[i]->ack, bit);
+ (void)in_le32(&pmac_irq_hw[i]->ack);
+ spin_unlock_irqrestore(&pmac_pic_lock, flags);
+}
+
+static void __pmac_set_irq_mask(unsigned int irq_nr, int nokicklost)
{
unsigned long bit = 1UL << (irq_nr & 0x1f);
int i = irq_nr >> 5;
- unsigned long flags;
if ((unsigned)irq_nr >= max_irqs)
return;
- spin_lock_irqsave(&pmac_pic_lock, flags);
/* enable unmasked interrupts */
out_le32(&pmac_irq_hw[i]->enable, ppc_cached_irq_mask[i]);
* the bit in the flag register or request another interrupt.
*/
if (bit & ppc_cached_irq_mask[i] & in_le32(&pmac_irq_hw[i]->level))
- __set_lost((ulong)irq_nr, nokicklost);
- spin_unlock_irqrestore(&pmac_pic_lock, flags);
+ __pmac_retrigger(irq_nr);
}
/* When an irq gets requested for the first client, if it's an
* edge interrupt, we clear any previous one on the controller
*/
-static unsigned int pmac_startup_irq(unsigned int irq_nr)
+static unsigned int pmac_startup_irq(unsigned int virq)
{
- unsigned long bit = 1UL << (irq_nr & 0x1f);
- int i = irq_nr >> 5;
+ unsigned long flags;
+ unsigned int src = irq_map[virq].hwirq;
+ unsigned long bit = 1UL << (src & 0x1f);
+ int i = src >> 5;
- if ((irq_desc[irq_nr].status & IRQ_LEVEL) == 0)
+ spin_lock_irqsave(&pmac_pic_lock, flags);
+ if ((irq_desc[virq].status & IRQ_LEVEL) == 0)
out_le32(&pmac_irq_hw[i]->ack, bit);
- set_bit(irq_nr, ppc_cached_irq_mask);
- pmac_set_irq_mask(irq_nr, 0);
+ __set_bit(src, ppc_cached_irq_mask);
+ __pmac_set_irq_mask(src, 0);
+ spin_unlock_irqrestore(&pmac_pic_lock, flags);
return 0;
}
-static void pmac_mask_irq(unsigned int irq_nr)
+static void pmac_mask_irq(unsigned int virq)
{
- clear_bit(irq_nr, ppc_cached_irq_mask);
- pmac_set_irq_mask(irq_nr, 0);
- mb();
+ unsigned long flags;
+ unsigned int src = irq_map[virq].hwirq;
+
+ spin_lock_irqsave(&pmac_pic_lock, flags);
+ __clear_bit(src, ppc_cached_irq_mask);
+ __pmac_set_irq_mask(src, 0);
+ spin_unlock_irqrestore(&pmac_pic_lock, flags);
}
-static void pmac_unmask_irq(unsigned int irq_nr)
+static void pmac_unmask_irq(unsigned int virq)
{
- set_bit(irq_nr, ppc_cached_irq_mask);
- pmac_set_irq_mask(irq_nr, 0);
+ unsigned long flags;
+ unsigned int src = irq_map[virq].hwirq;
+
+ spin_lock_irqsave(&pmac_pic_lock, flags);
+ __set_bit(src, ppc_cached_irq_mask);
+ __pmac_set_irq_mask(src, 0);
+ spin_unlock_irqrestore(&pmac_pic_lock, flags);
}
-static void pmac_end_irq(unsigned int irq_nr)
+static int pmac_retrigger(unsigned int virq)
{
- if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS))
- && irq_desc[irq_nr].action) {
- set_bit(irq_nr, ppc_cached_irq_mask);
- pmac_set_irq_mask(irq_nr, 1);
- }
-}
+ unsigned long flags;
+ spin_lock_irqsave(&pmac_pic_lock, flags);
+ __pmac_retrigger(irq_map[virq].hwirq);
+ spin_unlock_irqrestore(&pmac_pic_lock, flags);
+ return 1;
+}
-struct hw_interrupt_type pmac_pic = {
+static struct irq_chip pmac_pic = {
.typename = " PMAC-PIC ",
.startup = pmac_startup_irq,
- .enable = pmac_unmask_irq,
- .disable = pmac_mask_irq,
- .ack = pmac_mask_and_ack_irq,
- .end = pmac_end_irq,
-};
-
-struct hw_interrupt_type gatwick_pic = {
- .typename = " GATWICK ",
- .startup = pmac_startup_irq,
- .enable = pmac_unmask_irq,
- .disable = pmac_mask_irq,
- .ack = pmac_mask_and_ack_irq,
- .end = pmac_end_irq,
+ .mask = pmac_mask_irq,
+ .ack = pmac_ack_irq,
+ .mask_ack = pmac_mask_and_ack_irq,
+ .unmask = pmac_unmask_irq,
+ .retrigger = pmac_retrigger,
};
static irqreturn_t gatwick_action(int cpl, void *dev_id, struct pt_regs *regs)
{
+ unsigned long flags;
int irq, bits;
+ int rc = IRQ_NONE;
+ spin_lock_irqsave(&pmac_pic_lock, flags);
for (irq = max_irqs; (irq -= 32) >= max_real_irqs; ) {
int i = irq >> 5;
bits = in_le32(&pmac_irq_hw[i]->event) | ppc_lost_interrupts[i];
if (bits == 0)
continue;
irq += __ilog2(bits);
+ spin_unlock_irqrestore(&pmac_pic_lock, flags);
__do_IRQ(irq, regs);
- return IRQ_HANDLED;
+ spin_lock_irqsave(&pmac_pic_lock, flags);
+ rc = IRQ_HANDLED;
}
- printk("gatwick irq not from gatwick pic\n");
- return IRQ_NONE;
+ spin_unlock_irqrestore(&pmac_pic_lock, flags);
+ return rc;
}
-int
-pmac_get_irq(struct pt_regs *regs)
+static unsigned int pmac_pic_get_irq(struct pt_regs *regs)
{
int irq;
unsigned long bits = 0;
+ unsigned long flags;
#ifdef CONFIG_SMP
void psurge_smp_message_recv(struct pt_regs *);
/* IPI's are a hack on the powersurge -- Cort */
if ( smp_processor_id() != 0 ) {
psurge_smp_message_recv(regs);
- return -2; /* ignore, already handled */
+ return NO_IRQ_IGNORE; /* ignore, already handled */
}
#endif /* CONFIG_SMP */
+ spin_lock_irqsave(&pmac_pic_lock, flags);
for (irq = max_real_irqs; (irq -= 32) >= 0; ) {
int i = irq >> 5;
bits = in_le32(&pmac_irq_hw[i]->event) | ppc_lost_interrupts[i];
irq += __ilog2(bits);
break;
}
-
- return irq;
-}
-
-/* This routine will fix some missing interrupt values in the device tree
- * on the gatwick mac-io controller used by some PowerBooks
- */
-static void __init
-pmac_fix_gatwick_interrupts(struct device_node *gw, int irq_base)
-{
- struct device_node *node;
- int count;
-
- memset(gatwick_int_pool, 0, sizeof(gatwick_int_pool));
- node = gw->child;
- count = 0;
- while(node)
- {
- /* Fix SCC */
- if (strcasecmp(node->name, "escc") == 0)
- if (node->child) {
- if (node->child->n_intrs < 3) {
- node->child->intrs = &gatwick_int_pool[count];
- count += 3;
- }
- node->child->n_intrs = 3;
- node->child->intrs[0].line = 15+irq_base;
- node->child->intrs[1].line = 4+irq_base;
- node->child->intrs[2].line = 5+irq_base;
- printk(KERN_INFO "irq: fixed SCC on second controller (%d,%d,%d)\n",
- node->child->intrs[0].line,
- node->child->intrs[1].line,
- node->child->intrs[2].line);
- }
- /* Fix media-bay & left SWIM */
- if (strcasecmp(node->name, "media-bay") == 0) {
- struct device_node* ya_node;
-
- if (node->n_intrs == 0)
- node->intrs = &gatwick_int_pool[count++];
- node->n_intrs = 1;
- node->intrs[0].line = 29+irq_base;
- printk(KERN_INFO "irq: fixed media-bay on second controller (%d)\n",
- node->intrs[0].line);
-
- ya_node = node->child;
- while(ya_node)
- {
- if (strcasecmp(ya_node->name, "floppy") == 0) {
- if (ya_node->n_intrs < 2) {
- ya_node->intrs = &gatwick_int_pool[count];
- count += 2;
- }
- ya_node->n_intrs = 2;
- ya_node->intrs[0].line = 19+irq_base;
- ya_node->intrs[1].line = 1+irq_base;
- printk(KERN_INFO "irq: fixed floppy on second controller (%d,%d)\n",
- ya_node->intrs[0].line, ya_node->intrs[1].line);
- }
- if (strcasecmp(ya_node->name, "ata4") == 0) {
- if (ya_node->n_intrs < 2) {
- ya_node->intrs = &gatwick_int_pool[count];
- count += 2;
- }
- ya_node->n_intrs = 2;
- ya_node->intrs[0].line = 14+irq_base;
- ya_node->intrs[1].line = 3+irq_base;
- printk(KERN_INFO "irq: fixed ide on second controller (%d,%d)\n",
- ya_node->intrs[0].line, ya_node->intrs[1].line);
- }
- ya_node = ya_node->sibling;
- }
- }
- node = node->sibling;
- }
- if (count > 10) {
- printk("WARNING !! Gatwick interrupt pool overflow\n");
- printk(" GATWICK_IRQ_POOL_SIZE = %d\n", GATWICK_IRQ_POOL_SIZE);
- printk(" requested = %d\n", count);
- }
-}
-
-/*
- * The PowerBook 3400/2400/3500 can have a combo ethernet/modem
- * card which includes an ohare chip that acts as a second interrupt
- * controller. If we find this second ohare, set it up and fix the
- * interrupt value in the device tree for the ethernet chip.
- */
-static int __init enable_second_ohare(void)
-{
- unsigned char bus, devfn;
- unsigned short cmd;
- unsigned long addr;
- struct device_node *irqctrler = find_devices("pci106b,7");
- struct device_node *ether;
-
- if (irqctrler == NULL || irqctrler->n_addrs <= 0)
- return -1;
- addr = (unsigned long) ioremap(irqctrler->addrs[0].address, 0x40);
- pmac_irq_hw[1] = (volatile struct pmac_irq_hw *)(addr + 0x20);
- max_irqs = 64;
- if (pci_device_from_OF_node(irqctrler, &bus, &devfn) == 0) {
- struct pci_controller* hose = pci_find_hose_for_OF_device(irqctrler);
- if (!hose)
- printk(KERN_ERR "Can't find PCI hose for OHare2 !\n");
- else {
- early_read_config_word(hose, bus, devfn, PCI_COMMAND, &cmd);
- cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
- cmd &= ~PCI_COMMAND_IO;
- early_write_config_word(hose, bus, devfn, PCI_COMMAND, cmd);
- }
- }
-
- /* Fix interrupt for the modem/ethernet combo controller. The number
- in the device tree (27) is bogus (correct for the ethernet-only
- board but not the combo ethernet/modem board).
- The real interrupt is 28 on the second controller -> 28+32 = 60.
- */
- ether = find_devices("pci1011,14");
- if (ether && ether->n_intrs > 0) {
- ether->intrs[0].line = 60;
- printk(KERN_INFO "irq: Fixed ethernet IRQ to %d\n",
- ether->intrs[0].line);
- }
-
- /* Return the interrupt number of the cascade */
- return irqctrler->intrs[0].line;
+ spin_unlock_irqrestore(&pmac_pic_lock, flags);
+ if (unlikely(irq < 0))
+ return NO_IRQ;
+ return irq_linear_revmap(pmac_pic_host, irq);
}
#ifdef CONFIG_XMON
static struct irqaction gatwick_cascade_action = {
.handler = gatwick_action,
- .flags = SA_INTERRUPT,
+ .flags = IRQF_DISABLED,
.mask = CPU_MASK_NONE,
.name = "cascade",
};
-#endif /* CONFIG_PPC32 */
-static int pmac_u3_cascade(struct pt_regs *regs, void *data)
+static int pmac_pic_host_match(struct irq_host *h, struct device_node *node)
{
- return mpic_get_one_irq((struct mpic *)data, regs);
+ /* We match all, we don't always have a node anyway */
+ return 1;
}
-void __init pmac_pic_init(void)
+static int pmac_pic_host_map(struct irq_host *h, unsigned int virq,
+ irq_hw_number_t hw, unsigned int flags)
{
- struct device_node *irqctrler = NULL;
- struct device_node *irqctrler2 = NULL;
- struct device_node *np;
-#ifdef CONFIG_PPC32
- int i;
- unsigned long addr;
- int irq_cascade = -1;
-#endif
- struct mpic *mpic1, *mpic2;
+ struct irq_desc *desc = get_irq_desc(virq);
+ int level;
- /* We first try to detect Apple's new Core99 chipset, since mac-io
- * is quite different on those machines and contains an IBM MPIC2.
+ if (hw >= max_irqs)
+ return -EINVAL;
+
+ /* Mark level interrupts, set delayed disable for edge ones and set
+ * handlers
*/
- np = find_type_devices("open-pic");
- while (np) {
- if (np->parent && !strcmp(np->parent->name, "u3"))
- irqctrler2 = np;
- else
- irqctrler = np;
- np = np->next;
- }
- if (irqctrler != NULL && irqctrler->n_addrs > 0) {
- unsigned char senses[128];
-
- printk(KERN_INFO "PowerMac using OpenPIC irq controller at 0x%08x\n",
- (unsigned int)irqctrler->addrs[0].address);
- pmac_call_feature(PMAC_FTR_ENABLE_MPIC, irqctrler, 0, 0);
-
- prom_get_irq_senses(senses, 0, 128);
- mpic1 = mpic_alloc(irqctrler->addrs[0].address,
- MPIC_PRIMARY | MPIC_WANTS_RESET,
- 0, 0, 128, 252, senses, 128, " OpenPIC ");
- BUG_ON(mpic1 == NULL);
- mpic_init(mpic1);
-
- if (irqctrler2 != NULL && irqctrler2->n_intrs > 0 &&
- irqctrler2->n_addrs > 0) {
- printk(KERN_INFO "Slave OpenPIC at 0x%08x hooked on IRQ %d\n",
- (u32)irqctrler2->addrs[0].address,
- irqctrler2->intrs[0].line);
-
- pmac_call_feature(PMAC_FTR_ENABLE_MPIC, irqctrler2, 0, 0);
- prom_get_irq_senses(senses, 128, 128 + 124);
-
- /* We don't need to set MPIC_BROKEN_U3 here since we don't have
- * hypertransport interrupts routed to it
- */
- mpic2 = mpic_alloc(irqctrler2->addrs[0].address,
- MPIC_BIG_ENDIAN | MPIC_WANTS_RESET,
- 0, 128, 124, 0, senses, 124,
- " U3-MPIC ");
- BUG_ON(mpic2 == NULL);
- mpic_init(mpic2);
- mpic_setup_cascade(irqctrler2->intrs[0].line,
- pmac_u3_cascade, mpic2);
- }
-#if defined(CONFIG_XMON) && defined(CONFIG_PPC32)
- {
- struct device_node* pswitch;
- int nmi_irq;
-
- pswitch = find_devices("programmer-switch");
- if (pswitch && pswitch->n_intrs) {
- nmi_irq = pswitch->intrs[0].line;
- mpic_irq_set_priority(nmi_irq, 9);
- setup_irq(nmi_irq, &xmon_action);
- }
- }
-#endif /* CONFIG_XMON */
- return;
- }
- irqctrler = NULL;
+ level = !!(level_mask[hw >> 5] & (1UL << (hw & 0x1f)));
+ if (level)
+ desc->status |= IRQ_LEVEL;
+ else
+ desc->status |= IRQ_DELAYED_DISABLE;
+ set_irq_chip_and_handler(virq, &pmac_pic, level ?
+ handle_level_irq : handle_edge_irq);
+ return 0;
+}
-#ifdef CONFIG_PPC32
- /* Get the level/edge settings, assume if it's not
- * a Grand Central nor an OHare, then it's an Heathrow
- * (or Paddington).
+static int pmac_pic_host_xlate(struct irq_host *h, struct device_node *ct,
+ u32 *intspec, unsigned int intsize,
+ irq_hw_number_t *out_hwirq,
+ unsigned int *out_flags)
+
+{
+ *out_hwirq = *intspec;
+ return 0;
+}
+
+static struct irq_host_ops pmac_pic_host_ops = {
+ .match = pmac_pic_host_match,
+ .map = pmac_pic_host_map,
+ .xlate = pmac_pic_host_xlate,
+};
+
+static void __init pmac_pic_probe_oldstyle(void)
+{
+ int i;
+ struct device_node *master = NULL;
+ struct device_node *slave = NULL;
+ u8 __iomem *addr;
+ struct resource r;
+
+ /* Set our get_irq function */
+ ppc_md.get_irq = pmac_pic_get_irq;
+
+ /*
+ * Find the interrupt controller type & node
*/
- ppc_md.get_irq = pmac_get_irq;
- if (find_devices("gc"))
+
+ if ((master = of_find_node_by_name(NULL, "gc")) != NULL) {
+ max_irqs = max_real_irqs = 32;
level_mask[0] = GC_LEVEL_MASK;
- else if (find_devices("ohare")) {
+ } else if ((master = of_find_node_by_name(NULL, "ohare")) != NULL) {
+ max_irqs = max_real_irqs = 32;
level_mask[0] = OHARE_LEVEL_MASK;
+
/* We might have a second cascaded ohare */
- level_mask[1] = OHARE_LEVEL_MASK;
- } else {
+ slave = of_find_node_by_name(NULL, "pci106b,7");
+ if (slave) {
+ max_irqs = 64;
+ level_mask[1] = OHARE_LEVEL_MASK;
+ }
+ } else if ((master = of_find_node_by_name(NULL, "mac-io")) != NULL) {
+ max_irqs = max_real_irqs = 64;
level_mask[0] = HEATHROW_LEVEL_MASK;
level_mask[1] = 0;
+
/* We might have a second cascaded heathrow */
- level_mask[2] = HEATHROW_LEVEL_MASK;
- level_mask[3] = 0;
+ slave = of_find_node_by_name(master, "mac-io");
+
+ /* Check ordering of master & slave */
+ if (device_is_compatible(master, "gatwick")) {
+ struct device_node *tmp;
+ BUG_ON(slave == NULL);
+ tmp = master;
+ master = slave;
+ slave = tmp;
+ }
+
+ /* We found a slave */
+ if (slave) {
+ max_irqs = 128;
+ level_mask[2] = HEATHROW_LEVEL_MASK;
+ level_mask[3] = 0;
+ }
}
+ BUG_ON(master == NULL);
/*
- * G3 powermacs and 1999 G3 PowerBooks have 64 interrupts,
- * 1998 G3 Series PowerBooks have 128,
- * other powermacs have 32.
- * The combo ethernet/modem card for the Powerstar powerbooks
- * (2400/3400/3500, ohare based) has a second ohare chip
- * effectively making a total of 64.
+ * Allocate an irq host
*/
- max_irqs = max_real_irqs = 32;
- irqctrler = find_devices("mac-io");
- if (irqctrler)
- {
- max_real_irqs = 64;
- if (irqctrler->next)
- max_irqs = 128;
- else
- max_irqs = 64;
+ pmac_pic_host = irq_alloc_host(IRQ_HOST_MAP_LINEAR, max_irqs,
+ &pmac_pic_host_ops,
+ max_irqs);
+ BUG_ON(pmac_pic_host == NULL);
+ irq_set_default_host(pmac_pic_host);
+
+ /* Get addresses of first controller if we have a node for it */
+ BUG_ON(of_address_to_resource(master, 0, &r));
+
+ /* Map interrupts of primary controller */
+ addr = (u8 __iomem *) ioremap(r.start, 0x40);
+ i = 0;
+ pmac_irq_hw[i++] = (volatile struct pmac_irq_hw __iomem *)
+ (addr + 0x20);
+ if (max_real_irqs > 32)
+ pmac_irq_hw[i++] = (volatile struct pmac_irq_hw __iomem *)
+ (addr + 0x10);
+ of_node_put(master);
+
+ printk(KERN_INFO "irq: Found primary Apple PIC %s for %d irqs\n",
+ master->full_name, max_real_irqs);
+
+ /* Map interrupts of cascaded controller */
+ if (slave && !of_address_to_resource(slave, 0, &r)) {
+ addr = (u8 __iomem *)ioremap(r.start, 0x40);
+ pmac_irq_hw[i++] = (volatile struct pmac_irq_hw __iomem *)
+ (addr + 0x20);
+ if (max_irqs > 64)
+ pmac_irq_hw[i++] =
+ (volatile struct pmac_irq_hw __iomem *)
+ (addr + 0x10);
+ pmac_irq_cascade = irq_of_parse_and_map(slave, 0);
+
+ printk(KERN_INFO "irq: Found slave Apple PIC %s for %d irqs"
+ " cascade: %d\n", slave->full_name,
+ max_irqs - max_real_irqs, pmac_irq_cascade);
}
- for ( i = 0; i < max_real_irqs ; i++ )
- irq_desc[i].handler = &pmac_pic;
-
- /* get addresses of first controller */
- if (irqctrler) {
- if (irqctrler->n_addrs > 0) {
- addr = (unsigned long)
- ioremap(irqctrler->addrs[0].address, 0x40);
- for (i = 0; i < 2; ++i)
- pmac_irq_hw[i] = (volatile struct pmac_irq_hw*)
- (addr + (2 - i) * 0x10);
- }
+ of_node_put(slave);
- /* get addresses of second controller */
- irqctrler = irqctrler->next;
- if (irqctrler && irqctrler->n_addrs > 0) {
- addr = (unsigned long)
- ioremap(irqctrler->addrs[0].address, 0x40);
- for (i = 2; i < 4; ++i)
- pmac_irq_hw[i] = (volatile struct pmac_irq_hw*)
- (addr + (4 - i) * 0x10);
- irq_cascade = irqctrler->intrs[0].line;
- if (device_is_compatible(irqctrler, "gatwick"))
- pmac_fix_gatwick_interrupts(irqctrler, max_real_irqs);
+ /* Disable all interrupts in all controllers */
+ for (i = 0; i * 32 < max_irqs; ++i)
+ out_le32(&pmac_irq_hw[i]->enable, 0);
+
+ /* Hookup cascade irq */
+ if (slave && pmac_irq_cascade != NO_IRQ)
+ setup_irq(pmac_irq_cascade, &gatwick_cascade_action);
+
+ printk(KERN_INFO "irq: System has %d possible interrupts\n", max_irqs);
+#ifdef CONFIG_XMON
+ setup_irq(irq_create_mapping(NULL, 20, 0), &xmon_action);
+#endif
+}
+#endif /* CONFIG_PPC32 */
+
+static void pmac_u3_cascade(unsigned int irq, struct irq_desc *desc,
+ struct pt_regs *regs)
+{
+ struct mpic *mpic = desc->handler_data;
+
+ unsigned int cascade_irq = mpic_get_one_irq(mpic, regs);
+ if (cascade_irq != NO_IRQ)
+ generic_handle_irq(cascade_irq, regs);
+ desc->chip->eoi(irq);
+}
+
+static void __init pmac_pic_setup_mpic_nmi(struct mpic *mpic)
+{
+#if defined(CONFIG_XMON) && defined(CONFIG_PPC32)
+ struct device_node* pswitch;
+ int nmi_irq;
+
+ pswitch = of_find_node_by_name(NULL, "programmer-switch");
+ if (pswitch) {
+ nmi_irq = irq_of_parse_and_map(pswitch, 0);
+ if (nmi_irq != NO_IRQ) {
+ mpic_irq_set_priority(nmi_irq, 9);
+ setup_irq(nmi_irq, &xmon_action);
}
- } else {
- /* older powermacs have a GC (grand central) or ohare at
- f3000000, with interrupt control registers at f3000020. */
- addr = (unsigned long) ioremap(0xf3000000, 0x40);
- pmac_irq_hw[0] = (volatile struct pmac_irq_hw *) (addr + 0x20);
+ of_node_put(pswitch);
}
+#endif /* defined(CONFIG_XMON) && defined(CONFIG_PPC32) */
+}
- /* PowerBooks 3400 and 3500 can have a second controller in a second
- ohare chip, on the combo ethernet/modem card */
- if (machine_is_compatible("AAPL,3400/2400")
- || machine_is_compatible("AAPL,3500"))
- irq_cascade = enable_second_ohare();
+static struct mpic * __init pmac_setup_one_mpic(struct device_node *np,
+ int master)
+{
+ const char *name = master ? " MPIC 1 " : " MPIC 2 ";
+ struct resource r;
+ struct mpic *mpic;
+ unsigned int flags = master ? MPIC_PRIMARY : 0;
+ int rc;
- /* disable all interrupts in all controllers */
- for (i = 0; i * 32 < max_irqs; ++i)
- out_le32(&pmac_irq_hw[i]->enable, 0);
- /* mark level interrupts */
- for (i = 0; i < max_irqs; i++)
- if (level_mask[i >> 5] & (1UL << (i & 0x1f)))
- irq_desc[i].status = IRQ_LEVEL;
-
- /* get interrupt line of secondary interrupt controller */
- if (irq_cascade >= 0) {
- printk(KERN_INFO "irq: secondary controller on irq %d\n",
- (int)irq_cascade);
- for ( i = max_real_irqs ; i < max_irqs ; i++ )
- irq_desc[i].handler = &gatwick_pic;
- setup_irq(irq_cascade, &gatwick_cascade_action);
+ rc = of_address_to_resource(np, 0, &r);
+ if (rc)
+ return NULL;
+
+ pmac_call_feature(PMAC_FTR_ENABLE_MPIC, np, 0, 0);
+
+ flags |= MPIC_WANTS_RESET;
+ if (get_property(np, "big-endian", NULL))
+ flags |= MPIC_BIG_ENDIAN;
+
+ /* Primary Big Endian means HT interrupts. This is quite dodgy
+ * but works until I find a better way
+ */
+ if (master && (flags & MPIC_BIG_ENDIAN))
+ flags |= MPIC_BROKEN_U3;
+
+ mpic = mpic_alloc(np, r.start, flags, 0, 0, name);
+ if (mpic == NULL)
+ return NULL;
+
+ mpic_init(mpic);
+
+ return mpic;
+ }
+
+static int __init pmac_pic_probe_mpic(void)
+{
+ struct mpic *mpic1, *mpic2;
+ struct device_node *np, *master = NULL, *slave = NULL;
+ unsigned int cascade;
+
+ /* We can have up to 2 MPICs cascaded */
+ for (np = NULL; (np = of_find_node_by_type(np, "open-pic"))
+ != NULL;) {
+ if (master == NULL &&
+ get_property(np, "interrupts", NULL) == NULL)
+ master = of_node_get(np);
+ else if (slave == NULL)
+ slave = of_node_get(np);
+ if (master && slave)
+ break;
}
- printk("System has %d possible interrupts\n", max_irqs);
- if (max_irqs != max_real_irqs)
- printk(KERN_DEBUG "%d interrupts on main controller\n",
- max_real_irqs);
-#ifdef CONFIG_XMON
- setup_irq(20, &xmon_action);
-#endif /* CONFIG_XMON */
-#endif /* CONFIG_PPC32 */
+ /* Check for bogus setups */
+ if (master == NULL && slave != NULL) {
+ master = slave;
+ slave = NULL;
+ }
+
+ /* Not found, default to good old pmac pic */
+ if (master == NULL)
+ return -ENODEV;
+
+ /* Set master handler */
+ ppc_md.get_irq = mpic_get_irq;
+
+ /* Setup master */
+ mpic1 = pmac_setup_one_mpic(master, 1);
+ BUG_ON(mpic1 == NULL);
+
+ /* Install NMI if any */
+ pmac_pic_setup_mpic_nmi(mpic1);
+
+ of_node_put(master);
+
+ /* No slave, let's go out */
+ if (slave == NULL)
+ return 0;
+
+ /* Get/Map slave interrupt */
+ cascade = irq_of_parse_and_map(slave, 0);
+ if (cascade == NO_IRQ) {
+ printk(KERN_ERR "Failed to map cascade IRQ\n");
+ return 0;
+ }
+
+ mpic2 = pmac_setup_one_mpic(slave, 0);
+ if (mpic2 == NULL) {
+ printk(KERN_ERR "Failed to setup slave MPIC\n");
+ of_node_put(slave);
+ return 0;
+ }
+ set_irq_data(cascade, mpic2);
+ set_irq_chained_handler(cascade, pmac_u3_cascade);
+
+ of_node_put(slave);
+ return 0;
+}
+
+
+void __init pmac_pic_init(void)
+{
+ unsigned int flags = 0;
+
+ /* We configure the OF parsing based on our oldworld vs. newworld
+ * platform type and wether we were booted by BootX.
+ */
+#ifdef CONFIG_PPC32
+ if (!pmac_newworld)
+ flags |= OF_IMAP_OLDWORLD_MAC;
+ if (get_property(of_chosen, "linux,bootx", NULL) != NULL)
+ flags |= OF_IMAP_NO_PHANDLE;
+ of_irq_map_init(flags);
+#endif /* CONFIG_PPC_32 */
+
+ /* We first try to detect Apple's new Core99 chipset, since mac-io
+ * is quite different on those machines and contains an IBM MPIC2.
+ */
+ if (pmac_pic_probe_mpic() == 0)
+ return;
+
+#ifdef CONFIG_PPC32
+ pmac_pic_probe_oldstyle();
+#endif
}
#if defined(CONFIG_PM) && defined(CONFIG_PPC32)
/* This used to be passed by the PMU driver but that link got
* broken with the new driver model. We use this tweak for now...
+ * We really want to do things differently though...
*/
static int pmacpic_find_viaint(void)
{
np = of_find_node_by_name(NULL, "via-pmu");
if (np == NULL)
goto not_found;
- viaint = np->intrs[0].line;
+ viaint = irq_of_parse_and_map(np, 0);;
#endif /* CONFIG_ADB_PMU */
not_found: