diff options
Diffstat (limited to 'arch/powerpc/platforms/powermac/pic.c')
| -rw-r--r-- | arch/powerpc/platforms/powermac/pic.c | 332 |
1 files changed, 147 insertions, 185 deletions
diff --git a/arch/powerpc/platforms/powermac/pic.c b/arch/powerpc/platforms/powermac/pic.c index 829b8b02527..4c24bf60d39 100644 --- a/arch/powerpc/platforms/powermac/pic.c +++ b/arch/powerpc/platforms/powermac/pic.c @@ -21,10 +21,9 @@ #include <linux/signal.h> #include <linux/pci.h> #include <linux/interrupt.h> -#include <linux/sysdev.h> +#include <linux/syscore_ops.h> #include <linux/adb.h> #include <linux/pmu.h> -#include <linux/module.h> #include <asm/sections.h> #include <asm/io.h> @@ -34,16 +33,10 @@ #include <asm/time.h> #include <asm/pmac_feature.h> #include <asm/mpic.h> +#include <asm/xmon.h> #include "pmac.h" -/* - * XXX this should be in xmon.h, but putting it there means xmon.h - * has to include <linux/interrupt.h> (to get irqreturn_t), which - * causes all sorts of problems. -- paulus - */ -extern irqreturn_t xmon_irq(int, void *); - #ifdef CONFIG_PPC32 struct pmac_irq_hw { unsigned int event; @@ -52,24 +45,23 @@ struct pmac_irq_hw { unsigned int level; }; +/* Workaround flags for 32bit powermac machines */ +unsigned int of_irq_workarounds; +struct device_node *of_irq_dflt_pic; + /* Default addresses */ static volatile struct pmac_irq_hw __iomem *pmac_irq_hw[4]; -#define GC_LEVEL_MASK 0x3ff00000 -#define OHARE_LEVEL_MASK 0x1ff00000 -#define HEATHROW_LEVEL_MASK 0x1ff00000 - static int max_irqs; static int max_real_irqs; -static u32 level_mask[4]; -static DEFINE_SPINLOCK(pmac_pic_lock); +static DEFINE_RAW_SPINLOCK(pmac_pic_lock); -#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]; +/* The max irq number this driver deals with is 128; see max_irqs */ +static DECLARE_BITMAP(ppc_lost_interrupts, 128); +static DECLARE_BITMAP(ppc_cached_irq_mask, 128); static int pmac_irq_cascade = -1; -static struct irq_host *pmac_pic_host; +static struct irq_domain *pmac_pic_host; static void __pmac_retrigger(unsigned int irq_nr) { @@ -84,14 +76,14 @@ static void __pmac_retrigger(unsigned int irq_nr) } } -static void pmac_mask_and_ack_irq(unsigned int virq) +static void pmac_mask_and_ack_irq(struct irq_data *d) { - unsigned int src = irq_map[virq].hwirq; + unsigned int src = irqd_to_hwirq(d); unsigned long bit = 1UL << (src & 0x1f); int i = src >> 5; unsigned long flags; - spin_lock_irqsave(&pmac_pic_lock, flags); + raw_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); @@ -103,22 +95,22 @@ static void pmac_mask_and_ack_irq(unsigned int virq) mb(); } while((in_le32(&pmac_irq_hw[i]->enable) & bit) != (ppc_cached_irq_mask[i] & bit)); - spin_unlock_irqrestore(&pmac_pic_lock, flags); + raw_spin_unlock_irqrestore(&pmac_pic_lock, flags); } -static void pmac_ack_irq(unsigned int virq) +static void pmac_ack_irq(struct irq_data *d) { - unsigned int src = irq_map[virq].hwirq; + unsigned int src = irqd_to_hwirq(d); unsigned long bit = 1UL << (src & 0x1f); int i = src >> 5; unsigned long flags; - spin_lock_irqsave(&pmac_pic_lock, flags); + raw_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); + raw_spin_unlock_irqrestore(&pmac_pic_lock, flags); } static void __pmac_set_irq_mask(unsigned int irq_nr, int nokicklost) @@ -151,63 +143,63 @@ static void __pmac_set_irq_mask(unsigned int irq_nr, int nokicklost) /* 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 virq) +static unsigned int pmac_startup_irq(struct irq_data *d) { unsigned long flags; - unsigned int src = irq_map[virq].hwirq; + unsigned int src = irqd_to_hwirq(d); unsigned long bit = 1UL << (src & 0x1f); int i = src >> 5; - spin_lock_irqsave(&pmac_pic_lock, flags); - if ((irq_desc[virq].status & IRQ_LEVEL) == 0) + raw_spin_lock_irqsave(&pmac_pic_lock, flags); + if (!irqd_is_level_type(d)) out_le32(&pmac_irq_hw[i]->ack, bit); __set_bit(src, ppc_cached_irq_mask); __pmac_set_irq_mask(src, 0); - spin_unlock_irqrestore(&pmac_pic_lock, flags); + raw_spin_unlock_irqrestore(&pmac_pic_lock, flags); return 0; } -static void pmac_mask_irq(unsigned int virq) +static void pmac_mask_irq(struct irq_data *d) { unsigned long flags; - unsigned int src = irq_map[virq].hwirq; + unsigned int src = irqd_to_hwirq(d); - spin_lock_irqsave(&pmac_pic_lock, flags); + raw_spin_lock_irqsave(&pmac_pic_lock, flags); __clear_bit(src, ppc_cached_irq_mask); __pmac_set_irq_mask(src, 1); - spin_unlock_irqrestore(&pmac_pic_lock, flags); + raw_spin_unlock_irqrestore(&pmac_pic_lock, flags); } -static void pmac_unmask_irq(unsigned int virq) +static void pmac_unmask_irq(struct irq_data *d) { unsigned long flags; - unsigned int src = irq_map[virq].hwirq; + unsigned int src = irqd_to_hwirq(d); - spin_lock_irqsave(&pmac_pic_lock, flags); + raw_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); + raw_spin_unlock_irqrestore(&pmac_pic_lock, flags); } -static int pmac_retrigger(unsigned int virq) +static int pmac_retrigger(struct irq_data *d) { unsigned long flags; - spin_lock_irqsave(&pmac_pic_lock, flags); - __pmac_retrigger(irq_map[virq].hwirq); - spin_unlock_irqrestore(&pmac_pic_lock, flags); + raw_spin_lock_irqsave(&pmac_pic_lock, flags); + __pmac_retrigger(irqd_to_hwirq(d)); + raw_spin_unlock_irqrestore(&pmac_pic_lock, flags); return 1; } static struct irq_chip pmac_pic = { - .typename = " PMAC-PIC ", - .startup = pmac_startup_irq, - .mask = pmac_mask_irq, - .ack = pmac_ack_irq, - .mask_ack = pmac_mask_and_ack_irq, - .unmask = pmac_unmask_irq, - .retrigger = pmac_retrigger, + .name = "PMAC-PIC", + .irq_startup = pmac_startup_irq, + .irq_mask = pmac_mask_irq, + .irq_ack = pmac_ack_irq, + .irq_mask_ack = pmac_mask_and_ack_irq, + .irq_unmask = pmac_unmask_irq, + .irq_retrigger = pmac_retrigger, }; static irqreturn_t gatwick_action(int cpl, void *dev_id) @@ -216,22 +208,21 @@ static irqreturn_t gatwick_action(int cpl, void *dev_id) int irq, bits; int rc = IRQ_NONE; - spin_lock_irqsave(&pmac_pic_lock, flags); + raw_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]; - /* We must read level interrupts from the level register */ - bits |= (in_le32(&pmac_irq_hw[i]->level) & level_mask[i]); + bits |= in_le32(&pmac_irq_hw[i]->level); bits &= ppc_cached_irq_mask[i]; if (bits == 0) continue; irq += __ilog2(bits); - spin_unlock_irqrestore(&pmac_pic_lock, flags); - __do_IRQ(irq); - spin_lock_irqsave(&pmac_pic_lock, flags); + raw_spin_unlock_irqrestore(&pmac_pic_lock, flags); + generic_handle_irq(irq); + raw_spin_lock_irqsave(&pmac_pic_lock, flags); rc = IRQ_HANDLED; } - spin_unlock_irqrestore(&pmac_pic_lock, flags); + raw_spin_unlock_irqrestore(&pmac_pic_lock, flags); return rc; } @@ -241,28 +232,24 @@ static unsigned int pmac_pic_get_irq(void) unsigned long bits = 0; unsigned long flags; -#ifdef CONFIG_SMP - void psurge_smp_message_recv(void); - - /* IPI's are a hack on the powersurge -- Cort */ - if ( smp_processor_id() != 0 ) { - psurge_smp_message_recv(); - return NO_IRQ_IGNORE; /* ignore, already handled */ +#ifdef CONFIG_PPC_PMAC32_PSURGE + /* IPI's are a hack on the powersurge -- Cort */ + if (smp_processor_id() != 0) { + return psurge_secondary_virq; } -#endif /* CONFIG_SMP */ - spin_lock_irqsave(&pmac_pic_lock, flags); +#endif /* CONFIG_PPC_PMAC32_PSURGE */ + raw_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]; - /* We must read level interrupts from the level register */ - bits |= (in_le32(&pmac_irq_hw[i]->level) & level_mask[i]); + bits |= in_le32(&pmac_irq_hw[i]->level); bits &= ppc_cached_irq_mask[i]; if (bits == 0) continue; irq += __ilog2(bits); break; } - spin_unlock_irqrestore(&pmac_pic_lock, flags); + raw_spin_unlock_irqrestore(&pmac_pic_lock, flags); if (unlikely(irq < 0)) return NO_IRQ; return irq_linear_revmap(pmac_pic_host, irq); @@ -272,59 +259,39 @@ static unsigned int pmac_pic_get_irq(void) static struct irqaction xmon_action = { .handler = xmon_irq, .flags = 0, - .mask = CPU_MASK_NONE, .name = "NMI - XMON" }; #endif static struct irqaction gatwick_cascade_action = { .handler = gatwick_action, - .flags = IRQF_DISABLED, - .mask = CPU_MASK_NONE, .name = "cascade", }; -static int pmac_pic_host_match(struct irq_host *h, struct device_node *node) +static int pmac_pic_host_match(struct irq_domain *h, struct device_node *node) { /* We match all, we don't always have a node anyway */ return 1; } -static int pmac_pic_host_map(struct irq_host *h, unsigned int virq, +static int pmac_pic_host_map(struct irq_domain *h, unsigned int virq, irq_hw_number_t hw) { - struct irq_desc *desc = get_irq_desc(virq); - int level; - if (hw >= max_irqs) return -EINVAL; /* Mark level interrupts, set delayed disable for edge ones and set * handlers */ - level = !!(level_mask[hw >> 5] & (1UL << (hw & 0x1f))); - if (level) - desc->status |= IRQ_LEVEL; - set_irq_chip_and_handler(virq, &pmac_pic, level ? - handle_level_irq : handle_edge_irq); + irq_set_status_flags(virq, IRQ_LEVEL); + irq_set_chip_and_handler(virq, &pmac_pic, handle_level_irq); return 0; } -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_flags = IRQ_TYPE_NONE; - *out_hwirq = *intspec; - return 0; -} - -static struct irq_host_ops pmac_pic_host_ops = { +static const struct irq_domain_ops pmac_pic_host_ops = { .match = pmac_pic_host_match, .map = pmac_pic_host_map, - .xlate = pmac_pic_host_xlate, + .xlate = irq_domain_xlate_onecell, }; static void __init pmac_pic_probe_oldstyle(void) @@ -344,21 +311,14 @@ static void __init pmac_pic_probe_oldstyle(void) if ((master = of_find_node_by_name(NULL, "gc")) != NULL) { max_irqs = max_real_irqs = 32; - level_mask[0] = GC_LEVEL_MASK; } 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 */ slave = of_find_node_by_name(NULL, "pci106b,7"); - if (slave) { + 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 */ slave = of_find_node_by_name(master, "mac-io"); @@ -373,20 +333,16 @@ static void __init pmac_pic_probe_oldstyle(void) } /* We found a slave */ - if (slave) { + if (slave) max_irqs = 128; - level_mask[2] = HEATHROW_LEVEL_MASK; - level_mask[3] = 0; - } } BUG_ON(master == NULL); /* * Allocate an irq host */ - pmac_pic_host = irq_alloc_host(master, IRQ_HOST_MAP_LINEAR, max_irqs, - &pmac_pic_host_ops, - max_irqs); + pmac_pic_host = irq_domain_add_linear(master, max_irqs, + &pmac_pic_host_ops, NULL); BUG_ON(pmac_pic_host == NULL); irq_set_default_host(pmac_pic_host); @@ -436,17 +392,43 @@ static void __init pmac_pic_probe_oldstyle(void) setup_irq(irq_create_mapping(NULL, 20), &xmon_action); #endif } -#endif /* CONFIG_PPC32 */ -static void pmac_u3_cascade(unsigned int irq, struct irq_desc *desc) +int of_irq_parse_oldworld(struct device_node *device, int index, + struct of_phandle_args *out_irq) { - struct mpic *mpic = desc->handler_data; + const u32 *ints = NULL; + int intlen; + + /* + * Old machines just have a list of interrupt numbers + * and no interrupt-controller nodes. We also have dodgy + * cases where the APPL,interrupts property is completely + * missing behind pci-pci bridges and we have to get it + * from the parent (the bridge itself, as apple just wired + * everything together on these) + */ + while (device) { + ints = of_get_property(device, "AAPL,interrupts", &intlen); + if (ints != NULL) + break; + device = device->parent; + if (device && strcmp(device->type, "pci") != 0) + break; + } + if (ints == NULL) + return -EINVAL; + intlen /= sizeof(u32); + + if (index >= intlen) + return -EINVAL; - unsigned int cascade_irq = mpic_get_one_irq(mpic); - if (cascade_irq != NO_IRQ) - generic_handle_irq(cascade_irq); - desc->chip->eoi(irq); + out_irq->np = NULL; + out_irq->args[0] = ints[index]; + out_irq->args_count = 1; + + return 0; } +#endif /* CONFIG_PPC32 */ static void __init pmac_pic_setup_mpic_nmi(struct mpic *mpic) { @@ -470,18 +452,11 @@ 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; - - rc = of_address_to_resource(np, 0, &r); - if (rc) - return NULL; + unsigned int flags = master ? 0 : MPIC_SECONDARY; pmac_call_feature(PMAC_FTR_ENABLE_MPIC, np, 0, 0); - flags |= MPIC_WANTS_RESET; if (of_get_property(np, "big-endian", NULL)) flags |= MPIC_BIG_ENDIAN; @@ -491,7 +466,7 @@ static struct mpic * __init pmac_setup_one_mpic(struct device_node *np, if (master && (flags & MPIC_BIG_ENDIAN)) flags |= MPIC_U3_HT_IRQS; - mpic = mpic_alloc(np, r.start, flags, 0, 0, name); + mpic = mpic_alloc(np, 0, flags, 0, 0, name); if (mpic == NULL) return NULL; @@ -504,7 +479,6 @@ 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")) @@ -540,46 +514,53 @@ static int __init pmac_pic_probe_mpic(void) 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"); + /* Set up a cascaded controller, if present */ + if (slave) { + 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. + * platform type and whether we were booted by BootX. */ #ifdef CONFIG_PPC32 if (!pmac_newworld) - flags |= OF_IMAP_OLDWORLD_MAC; + of_irq_workarounds |= OF_IMAP_OLDWORLD_MAC; if (of_get_property(of_chosen, "linux,bootx", NULL) != NULL) - flags |= OF_IMAP_NO_PHANDLE; -#endif /* CONFIG_PPC_32 */ + of_irq_workarounds |= OF_IMAP_NO_PHANDLE; - of_irq_map_init(flags); + /* If we don't have phandles on a newworld, then 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 (pmac_newworld && (of_irq_workarounds & OF_IMAP_NO_PHANDLE)) { + struct device_node *np; + + for_each_node_with_property(np, "interrupt-controller") { + /* Skip /chosen/interrupt-controller */ + if (strcmp(np->name, "chosen") == 0) + continue; + /* It seems like at least one person 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; + } + } +#endif /* CONFIG_PPC32 */ /* We first try to detect Apple's new Core99 chipset, since mac-io * is quite different on those machines and contains an IBM MPIC2. @@ -617,14 +598,14 @@ static int pmacpic_find_viaint(void) np = of_find_node_by_name(NULL, "via-pmu"); if (np == NULL) goto not_found; - viaint = irq_of_parse_and_map(np, 0);; + viaint = irq_of_parse_and_map(np, 0); not_found: #endif /* CONFIG_ADB_PMU */ return viaint; } -static int pmacpic_suspend(struct sys_device *sysdev, pm_message_t state) +static int pmacpic_suspend(void) { int viaint = pmacpic_find_viaint(); @@ -645,7 +626,7 @@ static int pmacpic_suspend(struct sys_device *sysdev, pm_message_t state) return 0; } -static int pmacpic_resume(struct sys_device *sysdev) +static void pmacpic_resume(void) { int i; @@ -655,40 +636,21 @@ static int pmacpic_resume(struct sys_device *sysdev) mb(); for (i = 0; i < max_real_irqs; ++i) if (test_bit(i, sleep_save_mask)) - pmac_unmask_irq(i); - - return 0; + pmac_unmask_irq(irq_get_irq_data(i)); } -#endif /* CONFIG_PM && CONFIG_PPC32 */ - -static struct sysdev_class pmacpic_sysclass = { - .name = "pmac_pic", -}; - -static struct sys_device device_pmacpic = { - .id = 0, - .cls = &pmacpic_sysclass, -}; - -static struct sysdev_driver driver_pmacpic = { -#if defined(CONFIG_PM) && defined(CONFIG_PPC32) - .suspend = &pmacpic_suspend, - .resume = &pmacpic_resume, -#endif /* CONFIG_PM && CONFIG_PPC32 */ +static struct syscore_ops pmacpic_syscore_ops = { + .suspend = pmacpic_suspend, + .resume = pmacpic_resume, }; -static int __init init_pmacpic_sysfs(void) +static int __init init_pmacpic_syscore(void) { -#ifdef CONFIG_PPC32 - if (max_irqs == 0) - return -ENODEV; -#endif - printk(KERN_DEBUG "Registering pmac pic with sysfs...\n"); - sysdev_class_register(&pmacpic_sysclass); - sysdev_register(&device_pmacpic); - sysdev_driver_register(&pmacpic_sysclass, &driver_pmacpic); + if (pmac_irq_hw[0]) + register_syscore_ops(&pmacpic_syscore_ops); return 0; } -machine_subsys_initcall(powermac, init_pmacpic_sysfs); +machine_subsys_initcall(powermac, init_pmacpic_syscore); + +#endif /* CONFIG_PM && CONFIG_PPC32 */ |
