162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Support for the interrupt controllers found on Power Macintosh, 462306a36Sopenharmony_ci * currently Apple's "Grand Central" interrupt controller in all 562306a36Sopenharmony_ci * it's incarnations. OpenPIC support used on newer machines is 662306a36Sopenharmony_ci * in a separate file 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Copyright (C) 1997 Paul Mackerras (paulus@samba.org) 962306a36Sopenharmony_ci * Copyright (C) 2005 Benjamin Herrenschmidt (benh@kernel.crashing.org) 1062306a36Sopenharmony_ci * IBM, Corp. 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/stddef.h> 1462306a36Sopenharmony_ci#include <linux/init.h> 1562306a36Sopenharmony_ci#include <linux/sched.h> 1662306a36Sopenharmony_ci#include <linux/signal.h> 1762306a36Sopenharmony_ci#include <linux/pci.h> 1862306a36Sopenharmony_ci#include <linux/interrupt.h> 1962306a36Sopenharmony_ci#include <linux/syscore_ops.h> 2062306a36Sopenharmony_ci#include <linux/adb.h> 2162306a36Sopenharmony_ci#include <linux/minmax.h> 2262306a36Sopenharmony_ci#include <linux/pmu.h> 2362306a36Sopenharmony_ci#include <linux/irqdomain.h> 2462306a36Sopenharmony_ci#include <linux/of_address.h> 2562306a36Sopenharmony_ci#include <linux/of_irq.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include <asm/sections.h> 2862306a36Sopenharmony_ci#include <asm/io.h> 2962306a36Sopenharmony_ci#include <asm/smp.h> 3062306a36Sopenharmony_ci#include <asm/pci-bridge.h> 3162306a36Sopenharmony_ci#include <asm/time.h> 3262306a36Sopenharmony_ci#include <asm/pmac_feature.h> 3362306a36Sopenharmony_ci#include <asm/mpic.h> 3462306a36Sopenharmony_ci#include <asm/xmon.h> 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#include "pmac.h" 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#ifdef CONFIG_PPC32 3962306a36Sopenharmony_cistruct pmac_irq_hw { 4062306a36Sopenharmony_ci unsigned int event; 4162306a36Sopenharmony_ci unsigned int enable; 4262306a36Sopenharmony_ci unsigned int ack; 4362306a36Sopenharmony_ci unsigned int level; 4462306a36Sopenharmony_ci}; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/* Workaround flags for 32bit powermac machines */ 4762306a36Sopenharmony_ciunsigned int of_irq_workarounds; 4862306a36Sopenharmony_cistruct device_node *of_irq_dflt_pic; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/* Default addresses */ 5162306a36Sopenharmony_cistatic volatile struct pmac_irq_hw __iomem *pmac_irq_hw[4]; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic int max_irqs; 5462306a36Sopenharmony_cistatic int max_real_irqs; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic DEFINE_RAW_SPINLOCK(pmac_pic_lock); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* The max irq number this driver deals with is 128; see max_irqs */ 5962306a36Sopenharmony_cistatic DECLARE_BITMAP(ppc_lost_interrupts, 128); 6062306a36Sopenharmony_cistatic DECLARE_BITMAP(ppc_cached_irq_mask, 128); 6162306a36Sopenharmony_cistatic int pmac_irq_cascade = -1; 6262306a36Sopenharmony_cistatic struct irq_domain *pmac_pic_host; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic void __pmac_retrigger(unsigned int irq_nr) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci if (irq_nr >= max_real_irqs && pmac_irq_cascade > 0) { 6762306a36Sopenharmony_ci __set_bit(irq_nr, ppc_lost_interrupts); 6862306a36Sopenharmony_ci irq_nr = pmac_irq_cascade; 6962306a36Sopenharmony_ci mb(); 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci if (!__test_and_set_bit(irq_nr, ppc_lost_interrupts)) { 7262306a36Sopenharmony_ci atomic_inc(&ppc_n_lost_interrupts); 7362306a36Sopenharmony_ci set_dec(1); 7462306a36Sopenharmony_ci } 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic void pmac_mask_and_ack_irq(struct irq_data *d) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci unsigned int src = irqd_to_hwirq(d); 8062306a36Sopenharmony_ci unsigned long bit = 1UL << (src & 0x1f); 8162306a36Sopenharmony_ci int i = src >> 5; 8262306a36Sopenharmony_ci unsigned long flags; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci raw_spin_lock_irqsave(&pmac_pic_lock, flags); 8562306a36Sopenharmony_ci __clear_bit(src, ppc_cached_irq_mask); 8662306a36Sopenharmony_ci if (__test_and_clear_bit(src, ppc_lost_interrupts)) 8762306a36Sopenharmony_ci atomic_dec(&ppc_n_lost_interrupts); 8862306a36Sopenharmony_ci out_le32(&pmac_irq_hw[i]->enable, ppc_cached_irq_mask[i]); 8962306a36Sopenharmony_ci out_le32(&pmac_irq_hw[i]->ack, bit); 9062306a36Sopenharmony_ci do { 9162306a36Sopenharmony_ci /* make sure ack gets to controller before we enable 9262306a36Sopenharmony_ci interrupts */ 9362306a36Sopenharmony_ci mb(); 9462306a36Sopenharmony_ci } while((in_le32(&pmac_irq_hw[i]->enable) & bit) 9562306a36Sopenharmony_ci != (ppc_cached_irq_mask[i] & bit)); 9662306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&pmac_pic_lock, flags); 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic void pmac_ack_irq(struct irq_data *d) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci unsigned int src = irqd_to_hwirq(d); 10262306a36Sopenharmony_ci unsigned long bit = 1UL << (src & 0x1f); 10362306a36Sopenharmony_ci int i = src >> 5; 10462306a36Sopenharmony_ci unsigned long flags; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci raw_spin_lock_irqsave(&pmac_pic_lock, flags); 10762306a36Sopenharmony_ci if (__test_and_clear_bit(src, ppc_lost_interrupts)) 10862306a36Sopenharmony_ci atomic_dec(&ppc_n_lost_interrupts); 10962306a36Sopenharmony_ci out_le32(&pmac_irq_hw[i]->ack, bit); 11062306a36Sopenharmony_ci (void)in_le32(&pmac_irq_hw[i]->ack); 11162306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&pmac_pic_lock, flags); 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic void __pmac_set_irq_mask(unsigned int irq_nr, int nokicklost) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci unsigned long bit = 1UL << (irq_nr & 0x1f); 11762306a36Sopenharmony_ci int i = irq_nr >> 5; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci if ((unsigned)irq_nr >= max_irqs) 12062306a36Sopenharmony_ci return; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci /* enable unmasked interrupts */ 12362306a36Sopenharmony_ci out_le32(&pmac_irq_hw[i]->enable, ppc_cached_irq_mask[i]); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci do { 12662306a36Sopenharmony_ci /* make sure mask gets to controller before we 12762306a36Sopenharmony_ci return to user */ 12862306a36Sopenharmony_ci mb(); 12962306a36Sopenharmony_ci } while((in_le32(&pmac_irq_hw[i]->enable) & bit) 13062306a36Sopenharmony_ci != (ppc_cached_irq_mask[i] & bit)); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci /* 13362306a36Sopenharmony_ci * Unfortunately, setting the bit in the enable register 13462306a36Sopenharmony_ci * when the device interrupt is already on *doesn't* set 13562306a36Sopenharmony_ci * the bit in the flag register or request another interrupt. 13662306a36Sopenharmony_ci */ 13762306a36Sopenharmony_ci if (bit & ppc_cached_irq_mask[i] & in_le32(&pmac_irq_hw[i]->level)) 13862306a36Sopenharmony_ci __pmac_retrigger(irq_nr); 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci/* When an irq gets requested for the first client, if it's an 14262306a36Sopenharmony_ci * edge interrupt, we clear any previous one on the controller 14362306a36Sopenharmony_ci */ 14462306a36Sopenharmony_cistatic unsigned int pmac_startup_irq(struct irq_data *d) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci unsigned long flags; 14762306a36Sopenharmony_ci unsigned int src = irqd_to_hwirq(d); 14862306a36Sopenharmony_ci unsigned long bit = 1UL << (src & 0x1f); 14962306a36Sopenharmony_ci int i = src >> 5; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci raw_spin_lock_irqsave(&pmac_pic_lock, flags); 15262306a36Sopenharmony_ci if (!irqd_is_level_type(d)) 15362306a36Sopenharmony_ci out_le32(&pmac_irq_hw[i]->ack, bit); 15462306a36Sopenharmony_ci __set_bit(src, ppc_cached_irq_mask); 15562306a36Sopenharmony_ci __pmac_set_irq_mask(src, 0); 15662306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&pmac_pic_lock, flags); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci return 0; 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic void pmac_mask_irq(struct irq_data *d) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci unsigned long flags; 16462306a36Sopenharmony_ci unsigned int src = irqd_to_hwirq(d); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci raw_spin_lock_irqsave(&pmac_pic_lock, flags); 16762306a36Sopenharmony_ci __clear_bit(src, ppc_cached_irq_mask); 16862306a36Sopenharmony_ci __pmac_set_irq_mask(src, 1); 16962306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&pmac_pic_lock, flags); 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic void pmac_unmask_irq(struct irq_data *d) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci unsigned long flags; 17562306a36Sopenharmony_ci unsigned int src = irqd_to_hwirq(d); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci raw_spin_lock_irqsave(&pmac_pic_lock, flags); 17862306a36Sopenharmony_ci __set_bit(src, ppc_cached_irq_mask); 17962306a36Sopenharmony_ci __pmac_set_irq_mask(src, 0); 18062306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&pmac_pic_lock, flags); 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic int pmac_retrigger(struct irq_data *d) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci unsigned long flags; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci raw_spin_lock_irqsave(&pmac_pic_lock, flags); 18862306a36Sopenharmony_ci __pmac_retrigger(irqd_to_hwirq(d)); 18962306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&pmac_pic_lock, flags); 19062306a36Sopenharmony_ci return 1; 19162306a36Sopenharmony_ci} 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_cistatic struct irq_chip pmac_pic = { 19462306a36Sopenharmony_ci .name = "PMAC-PIC", 19562306a36Sopenharmony_ci .irq_startup = pmac_startup_irq, 19662306a36Sopenharmony_ci .irq_mask = pmac_mask_irq, 19762306a36Sopenharmony_ci .irq_ack = pmac_ack_irq, 19862306a36Sopenharmony_ci .irq_mask_ack = pmac_mask_and_ack_irq, 19962306a36Sopenharmony_ci .irq_unmask = pmac_unmask_irq, 20062306a36Sopenharmony_ci .irq_retrigger = pmac_retrigger, 20162306a36Sopenharmony_ci}; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic irqreturn_t gatwick_action(int cpl, void *dev_id) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci unsigned long flags; 20662306a36Sopenharmony_ci int irq, bits; 20762306a36Sopenharmony_ci int rc = IRQ_NONE; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci raw_spin_lock_irqsave(&pmac_pic_lock, flags); 21062306a36Sopenharmony_ci for (irq = max_irqs; (irq -= 32) >= max_real_irqs; ) { 21162306a36Sopenharmony_ci int i = irq >> 5; 21262306a36Sopenharmony_ci bits = in_le32(&pmac_irq_hw[i]->event) | ppc_lost_interrupts[i]; 21362306a36Sopenharmony_ci bits |= in_le32(&pmac_irq_hw[i]->level); 21462306a36Sopenharmony_ci bits &= ppc_cached_irq_mask[i]; 21562306a36Sopenharmony_ci if (bits == 0) 21662306a36Sopenharmony_ci continue; 21762306a36Sopenharmony_ci irq += __ilog2(bits); 21862306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&pmac_pic_lock, flags); 21962306a36Sopenharmony_ci generic_handle_irq(irq); 22062306a36Sopenharmony_ci raw_spin_lock_irqsave(&pmac_pic_lock, flags); 22162306a36Sopenharmony_ci rc = IRQ_HANDLED; 22262306a36Sopenharmony_ci } 22362306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&pmac_pic_lock, flags); 22462306a36Sopenharmony_ci return rc; 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic unsigned int pmac_pic_get_irq(void) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci int irq; 23062306a36Sopenharmony_ci unsigned long bits = 0; 23162306a36Sopenharmony_ci unsigned long flags; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci#ifdef CONFIG_PPC_PMAC32_PSURGE 23462306a36Sopenharmony_ci /* IPI's are a hack on the powersurge -- Cort */ 23562306a36Sopenharmony_ci if (smp_processor_id() != 0) { 23662306a36Sopenharmony_ci return psurge_secondary_virq; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci#endif /* CONFIG_PPC_PMAC32_PSURGE */ 23962306a36Sopenharmony_ci raw_spin_lock_irqsave(&pmac_pic_lock, flags); 24062306a36Sopenharmony_ci for (irq = max_real_irqs; (irq -= 32) >= 0; ) { 24162306a36Sopenharmony_ci int i = irq >> 5; 24262306a36Sopenharmony_ci bits = in_le32(&pmac_irq_hw[i]->event) | ppc_lost_interrupts[i]; 24362306a36Sopenharmony_ci bits |= in_le32(&pmac_irq_hw[i]->level); 24462306a36Sopenharmony_ci bits &= ppc_cached_irq_mask[i]; 24562306a36Sopenharmony_ci if (bits == 0) 24662306a36Sopenharmony_ci continue; 24762306a36Sopenharmony_ci irq += __ilog2(bits); 24862306a36Sopenharmony_ci break; 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&pmac_pic_lock, flags); 25162306a36Sopenharmony_ci if (unlikely(irq < 0)) 25262306a36Sopenharmony_ci return 0; 25362306a36Sopenharmony_ci return irq_linear_revmap(pmac_pic_host, irq); 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic int pmac_pic_host_match(struct irq_domain *h, struct device_node *node, 25762306a36Sopenharmony_ci enum irq_domain_bus_token bus_token) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci /* We match all, we don't always have a node anyway */ 26062306a36Sopenharmony_ci return 1; 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cistatic int pmac_pic_host_map(struct irq_domain *h, unsigned int virq, 26462306a36Sopenharmony_ci irq_hw_number_t hw) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci if (hw >= max_irqs) 26762306a36Sopenharmony_ci return -EINVAL; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci /* Mark level interrupts, set delayed disable for edge ones and set 27062306a36Sopenharmony_ci * handlers 27162306a36Sopenharmony_ci */ 27262306a36Sopenharmony_ci irq_set_status_flags(virq, IRQ_LEVEL); 27362306a36Sopenharmony_ci irq_set_chip_and_handler(virq, &pmac_pic, handle_level_irq); 27462306a36Sopenharmony_ci return 0; 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic const struct irq_domain_ops pmac_pic_host_ops = { 27862306a36Sopenharmony_ci .match = pmac_pic_host_match, 27962306a36Sopenharmony_ci .map = pmac_pic_host_map, 28062306a36Sopenharmony_ci .xlate = irq_domain_xlate_onecell, 28162306a36Sopenharmony_ci}; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_cistatic void __init pmac_pic_probe_oldstyle(void) 28462306a36Sopenharmony_ci{ 28562306a36Sopenharmony_ci int i; 28662306a36Sopenharmony_ci struct device_node *master = NULL; 28762306a36Sopenharmony_ci struct device_node *slave = NULL; 28862306a36Sopenharmony_ci u8 __iomem *addr; 28962306a36Sopenharmony_ci struct resource r; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci /* Set our get_irq function */ 29262306a36Sopenharmony_ci ppc_md.get_irq = pmac_pic_get_irq; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci /* 29562306a36Sopenharmony_ci * Find the interrupt controller type & node 29662306a36Sopenharmony_ci */ 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if ((master = of_find_node_by_name(NULL, "gc")) != NULL) { 29962306a36Sopenharmony_ci max_irqs = max_real_irqs = 32; 30062306a36Sopenharmony_ci } else if ((master = of_find_node_by_name(NULL, "ohare")) != NULL) { 30162306a36Sopenharmony_ci max_irqs = max_real_irqs = 32; 30262306a36Sopenharmony_ci /* We might have a second cascaded ohare */ 30362306a36Sopenharmony_ci slave = of_find_node_by_name(NULL, "pci106b,7"); 30462306a36Sopenharmony_ci if (slave) 30562306a36Sopenharmony_ci max_irqs = 64; 30662306a36Sopenharmony_ci } else if ((master = of_find_node_by_name(NULL, "mac-io")) != NULL) { 30762306a36Sopenharmony_ci max_irqs = max_real_irqs = 64; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci /* We might have a second cascaded heathrow */ 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci /* Compensate for of_node_put() in of_find_node_by_name() */ 31262306a36Sopenharmony_ci of_node_get(master); 31362306a36Sopenharmony_ci slave = of_find_node_by_name(master, "mac-io"); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci /* Check ordering of master & slave */ 31662306a36Sopenharmony_ci if (of_device_is_compatible(master, "gatwick")) { 31762306a36Sopenharmony_ci BUG_ON(slave == NULL); 31862306a36Sopenharmony_ci swap(master, slave); 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci /* We found a slave */ 32262306a36Sopenharmony_ci if (slave) 32362306a36Sopenharmony_ci max_irqs = 128; 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci BUG_ON(master == NULL); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci /* 32862306a36Sopenharmony_ci * Allocate an irq host 32962306a36Sopenharmony_ci */ 33062306a36Sopenharmony_ci pmac_pic_host = irq_domain_add_linear(master, max_irqs, 33162306a36Sopenharmony_ci &pmac_pic_host_ops, NULL); 33262306a36Sopenharmony_ci BUG_ON(pmac_pic_host == NULL); 33362306a36Sopenharmony_ci irq_set_default_host(pmac_pic_host); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci /* Get addresses of first controller if we have a node for it */ 33662306a36Sopenharmony_ci BUG_ON(of_address_to_resource(master, 0, &r)); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci /* Map interrupts of primary controller */ 33962306a36Sopenharmony_ci addr = (u8 __iomem *) ioremap(r.start, 0x40); 34062306a36Sopenharmony_ci i = 0; 34162306a36Sopenharmony_ci pmac_irq_hw[i++] = (volatile struct pmac_irq_hw __iomem *) 34262306a36Sopenharmony_ci (addr + 0x20); 34362306a36Sopenharmony_ci if (max_real_irqs > 32) 34462306a36Sopenharmony_ci pmac_irq_hw[i++] = (volatile struct pmac_irq_hw __iomem *) 34562306a36Sopenharmony_ci (addr + 0x10); 34662306a36Sopenharmony_ci of_node_put(master); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci printk(KERN_INFO "irq: Found primary Apple PIC %pOF for %d irqs\n", 34962306a36Sopenharmony_ci master, max_real_irqs); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci /* Map interrupts of cascaded controller */ 35262306a36Sopenharmony_ci if (slave && !of_address_to_resource(slave, 0, &r)) { 35362306a36Sopenharmony_ci addr = (u8 __iomem *)ioremap(r.start, 0x40); 35462306a36Sopenharmony_ci pmac_irq_hw[i++] = (volatile struct pmac_irq_hw __iomem *) 35562306a36Sopenharmony_ci (addr + 0x20); 35662306a36Sopenharmony_ci if (max_irqs > 64) 35762306a36Sopenharmony_ci pmac_irq_hw[i++] = 35862306a36Sopenharmony_ci (volatile struct pmac_irq_hw __iomem *) 35962306a36Sopenharmony_ci (addr + 0x10); 36062306a36Sopenharmony_ci pmac_irq_cascade = irq_of_parse_and_map(slave, 0); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci printk(KERN_INFO "irq: Found slave Apple PIC %pOF for %d irqs" 36362306a36Sopenharmony_ci " cascade: %d\n", slave, 36462306a36Sopenharmony_ci max_irqs - max_real_irqs, pmac_irq_cascade); 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci of_node_put(slave); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci /* Disable all interrupts in all controllers */ 36962306a36Sopenharmony_ci for (i = 0; i * 32 < max_irqs; ++i) 37062306a36Sopenharmony_ci out_le32(&pmac_irq_hw[i]->enable, 0); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci /* Hookup cascade irq */ 37362306a36Sopenharmony_ci if (slave && pmac_irq_cascade) { 37462306a36Sopenharmony_ci if (request_irq(pmac_irq_cascade, gatwick_action, 37562306a36Sopenharmony_ci IRQF_NO_THREAD, "cascade", NULL)) 37662306a36Sopenharmony_ci pr_err("Failed to register cascade interrupt\n"); 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci printk(KERN_INFO "irq: System has %d possible interrupts\n", max_irqs); 38062306a36Sopenharmony_ci#ifdef CONFIG_XMON 38162306a36Sopenharmony_ci i = irq_create_mapping(NULL, 20); 38262306a36Sopenharmony_ci if (request_irq(i, xmon_irq, IRQF_NO_THREAD, "NMI - XMON", NULL)) 38362306a36Sopenharmony_ci pr_err("Failed to register NMI-XMON interrupt\n"); 38462306a36Sopenharmony_ci#endif 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ciint of_irq_parse_oldworld(const struct device_node *device, int index, 38862306a36Sopenharmony_ci struct of_phandle_args *out_irq) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci const u32 *ints = NULL; 39162306a36Sopenharmony_ci int intlen; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci /* 39462306a36Sopenharmony_ci * Old machines just have a list of interrupt numbers 39562306a36Sopenharmony_ci * and no interrupt-controller nodes. We also have dodgy 39662306a36Sopenharmony_ci * cases where the APPL,interrupts property is completely 39762306a36Sopenharmony_ci * missing behind pci-pci bridges and we have to get it 39862306a36Sopenharmony_ci * from the parent (the bridge itself, as apple just wired 39962306a36Sopenharmony_ci * everything together on these) 40062306a36Sopenharmony_ci */ 40162306a36Sopenharmony_ci while (device) { 40262306a36Sopenharmony_ci ints = of_get_property(device, "AAPL,interrupts", &intlen); 40362306a36Sopenharmony_ci if (ints != NULL) 40462306a36Sopenharmony_ci break; 40562306a36Sopenharmony_ci device = device->parent; 40662306a36Sopenharmony_ci if (!of_node_is_type(device, "pci")) 40762306a36Sopenharmony_ci break; 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci if (ints == NULL) 41062306a36Sopenharmony_ci return -EINVAL; 41162306a36Sopenharmony_ci intlen /= sizeof(u32); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci if (index >= intlen) 41462306a36Sopenharmony_ci return -EINVAL; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci out_irq->np = NULL; 41762306a36Sopenharmony_ci out_irq->args[0] = ints[index]; 41862306a36Sopenharmony_ci out_irq->args_count = 1; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci return 0; 42162306a36Sopenharmony_ci} 42262306a36Sopenharmony_ci#endif /* CONFIG_PPC32 */ 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cistatic void __init pmac_pic_setup_mpic_nmi(struct mpic *mpic) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci#if defined(CONFIG_XMON) && defined(CONFIG_PPC32) 42762306a36Sopenharmony_ci struct device_node* pswitch; 42862306a36Sopenharmony_ci int nmi_irq; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci pswitch = of_find_node_by_name(NULL, "programmer-switch"); 43162306a36Sopenharmony_ci if (pswitch) { 43262306a36Sopenharmony_ci nmi_irq = irq_of_parse_and_map(pswitch, 0); 43362306a36Sopenharmony_ci if (nmi_irq) { 43462306a36Sopenharmony_ci mpic_irq_set_priority(nmi_irq, 9); 43562306a36Sopenharmony_ci if (request_irq(nmi_irq, xmon_irq, IRQF_NO_THREAD, 43662306a36Sopenharmony_ci "NMI - XMON", NULL)) 43762306a36Sopenharmony_ci pr_err("Failed to register NMI-XMON interrupt\n"); 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci of_node_put(pswitch); 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci#endif /* defined(CONFIG_XMON) && defined(CONFIG_PPC32) */ 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_cistatic struct mpic * __init pmac_setup_one_mpic(struct device_node *np, 44562306a36Sopenharmony_ci int master) 44662306a36Sopenharmony_ci{ 44762306a36Sopenharmony_ci const char *name = master ? " MPIC 1 " : " MPIC 2 "; 44862306a36Sopenharmony_ci struct mpic *mpic; 44962306a36Sopenharmony_ci unsigned int flags = master ? 0 : MPIC_SECONDARY; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci pmac_call_feature(PMAC_FTR_ENABLE_MPIC, np, 0, 0); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci if (of_property_read_bool(np, "big-endian")) 45462306a36Sopenharmony_ci flags |= MPIC_BIG_ENDIAN; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci /* Primary Big Endian means HT interrupts. This is quite dodgy 45762306a36Sopenharmony_ci * but works until I find a better way 45862306a36Sopenharmony_ci */ 45962306a36Sopenharmony_ci if (master && (flags & MPIC_BIG_ENDIAN)) 46062306a36Sopenharmony_ci flags |= MPIC_U3_HT_IRQS; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci mpic = mpic_alloc(np, 0, flags, 0, 0, name); 46362306a36Sopenharmony_ci if (mpic == NULL) 46462306a36Sopenharmony_ci return NULL; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci mpic_init(mpic); 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci return mpic; 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_cistatic int __init pmac_pic_probe_mpic(void) 47262306a36Sopenharmony_ci{ 47362306a36Sopenharmony_ci struct mpic *mpic1, *mpic2; 47462306a36Sopenharmony_ci struct device_node *np, *master = NULL, *slave = NULL; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci /* We can have up to 2 MPICs cascaded */ 47762306a36Sopenharmony_ci for_each_node_by_type(np, "open-pic") { 47862306a36Sopenharmony_ci if (master == NULL && !of_property_present(np, "interrupts")) 47962306a36Sopenharmony_ci master = of_node_get(np); 48062306a36Sopenharmony_ci else if (slave == NULL) 48162306a36Sopenharmony_ci slave = of_node_get(np); 48262306a36Sopenharmony_ci if (master && slave) { 48362306a36Sopenharmony_ci of_node_put(np); 48462306a36Sopenharmony_ci break; 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci /* Check for bogus setups */ 48962306a36Sopenharmony_ci if (master == NULL && slave != NULL) { 49062306a36Sopenharmony_ci master = slave; 49162306a36Sopenharmony_ci slave = NULL; 49262306a36Sopenharmony_ci } 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci /* Not found, default to good old pmac pic */ 49562306a36Sopenharmony_ci if (master == NULL) 49662306a36Sopenharmony_ci return -ENODEV; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci /* Set master handler */ 49962306a36Sopenharmony_ci ppc_md.get_irq = mpic_get_irq; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci /* Setup master */ 50262306a36Sopenharmony_ci mpic1 = pmac_setup_one_mpic(master, 1); 50362306a36Sopenharmony_ci BUG_ON(mpic1 == NULL); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci /* Install NMI if any */ 50662306a36Sopenharmony_ci pmac_pic_setup_mpic_nmi(mpic1); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci of_node_put(master); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci /* Set up a cascaded controller, if present */ 51162306a36Sopenharmony_ci if (slave) { 51262306a36Sopenharmony_ci mpic2 = pmac_setup_one_mpic(slave, 0); 51362306a36Sopenharmony_ci if (mpic2 == NULL) 51462306a36Sopenharmony_ci printk(KERN_ERR "Failed to setup slave MPIC\n"); 51562306a36Sopenharmony_ci of_node_put(slave); 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci return 0; 51962306a36Sopenharmony_ci} 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_civoid __init pmac_pic_init(void) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci /* We configure the OF parsing based on our oldworld vs. newworld 52562306a36Sopenharmony_ci * platform type and whether we were booted by BootX. 52662306a36Sopenharmony_ci */ 52762306a36Sopenharmony_ci#ifdef CONFIG_PPC32 52862306a36Sopenharmony_ci if (!pmac_newworld) 52962306a36Sopenharmony_ci of_irq_workarounds |= OF_IMAP_OLDWORLD_MAC; 53062306a36Sopenharmony_ci if (of_property_read_bool(of_chosen, "linux,bootx")) 53162306a36Sopenharmony_ci of_irq_workarounds |= OF_IMAP_NO_PHANDLE; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci /* If we don't have phandles on a newworld, then try to locate a 53462306a36Sopenharmony_ci * default interrupt controller (happens when booting with BootX). 53562306a36Sopenharmony_ci * We do a first match here, hopefully, that only ever happens on 53662306a36Sopenharmony_ci * machines with one controller. 53762306a36Sopenharmony_ci */ 53862306a36Sopenharmony_ci if (pmac_newworld && (of_irq_workarounds & OF_IMAP_NO_PHANDLE)) { 53962306a36Sopenharmony_ci struct device_node *np; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci for_each_node_with_property(np, "interrupt-controller") { 54262306a36Sopenharmony_ci /* Skip /chosen/interrupt-controller */ 54362306a36Sopenharmony_ci if (of_node_name_eq(np, "chosen")) 54462306a36Sopenharmony_ci continue; 54562306a36Sopenharmony_ci /* It seems like at least one person wants 54662306a36Sopenharmony_ci * to use BootX on a machine with an AppleKiwi 54762306a36Sopenharmony_ci * controller which happens to pretend to be an 54862306a36Sopenharmony_ci * interrupt controller too. */ 54962306a36Sopenharmony_ci if (of_node_name_eq(np, "AppleKiwi")) 55062306a36Sopenharmony_ci continue; 55162306a36Sopenharmony_ci /* I think we found one ! */ 55262306a36Sopenharmony_ci of_irq_dflt_pic = np; 55362306a36Sopenharmony_ci break; 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci } 55662306a36Sopenharmony_ci#endif /* CONFIG_PPC32 */ 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci /* We first try to detect Apple's new Core99 chipset, since mac-io 55962306a36Sopenharmony_ci * is quite different on those machines and contains an IBM MPIC2. 56062306a36Sopenharmony_ci */ 56162306a36Sopenharmony_ci if (pmac_pic_probe_mpic() == 0) 56262306a36Sopenharmony_ci return; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci#ifdef CONFIG_PPC32 56562306a36Sopenharmony_ci pmac_pic_probe_oldstyle(); 56662306a36Sopenharmony_ci#endif 56762306a36Sopenharmony_ci} 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci#if defined(CONFIG_PM) && defined(CONFIG_PPC32) 57062306a36Sopenharmony_ci/* 57162306a36Sopenharmony_ci * These procedures are used in implementing sleep on the powerbooks. 57262306a36Sopenharmony_ci * sleep_save_intrs() saves the states of all interrupt enables 57362306a36Sopenharmony_ci * and disables all interrupts except for the nominated one. 57462306a36Sopenharmony_ci * sleep_restore_intrs() restores the states of all interrupt enables. 57562306a36Sopenharmony_ci */ 57662306a36Sopenharmony_ciunsigned long sleep_save_mask[2]; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci/* This used to be passed by the PMU driver but that link got 57962306a36Sopenharmony_ci * broken with the new driver model. We use this tweak for now... 58062306a36Sopenharmony_ci * We really want to do things differently though... 58162306a36Sopenharmony_ci */ 58262306a36Sopenharmony_cistatic int pmacpic_find_viaint(void) 58362306a36Sopenharmony_ci{ 58462306a36Sopenharmony_ci int viaint = -1; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci#ifdef CONFIG_ADB_PMU 58762306a36Sopenharmony_ci struct device_node *np; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci if (pmu_get_model() != PMU_OHARE_BASED) 59062306a36Sopenharmony_ci goto not_found; 59162306a36Sopenharmony_ci np = of_find_node_by_name(NULL, "via-pmu"); 59262306a36Sopenharmony_ci if (np == NULL) 59362306a36Sopenharmony_ci goto not_found; 59462306a36Sopenharmony_ci viaint = irq_of_parse_and_map(np, 0); 59562306a36Sopenharmony_ci of_node_put(np); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_cinot_found: 59862306a36Sopenharmony_ci#endif /* CONFIG_ADB_PMU */ 59962306a36Sopenharmony_ci return viaint; 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_cistatic int pmacpic_suspend(void) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci int viaint = pmacpic_find_viaint(); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci sleep_save_mask[0] = ppc_cached_irq_mask[0]; 60762306a36Sopenharmony_ci sleep_save_mask[1] = ppc_cached_irq_mask[1]; 60862306a36Sopenharmony_ci ppc_cached_irq_mask[0] = 0; 60962306a36Sopenharmony_ci ppc_cached_irq_mask[1] = 0; 61062306a36Sopenharmony_ci if (viaint > 0) 61162306a36Sopenharmony_ci set_bit(viaint, ppc_cached_irq_mask); 61262306a36Sopenharmony_ci out_le32(&pmac_irq_hw[0]->enable, ppc_cached_irq_mask[0]); 61362306a36Sopenharmony_ci if (max_real_irqs > 32) 61462306a36Sopenharmony_ci out_le32(&pmac_irq_hw[1]->enable, ppc_cached_irq_mask[1]); 61562306a36Sopenharmony_ci (void)in_le32(&pmac_irq_hw[0]->event); 61662306a36Sopenharmony_ci /* make sure mask gets to controller before we return to caller */ 61762306a36Sopenharmony_ci mb(); 61862306a36Sopenharmony_ci (void)in_le32(&pmac_irq_hw[0]->enable); 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci return 0; 62162306a36Sopenharmony_ci} 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_cistatic void pmacpic_resume(void) 62462306a36Sopenharmony_ci{ 62562306a36Sopenharmony_ci int i; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci out_le32(&pmac_irq_hw[0]->enable, 0); 62862306a36Sopenharmony_ci if (max_real_irqs > 32) 62962306a36Sopenharmony_ci out_le32(&pmac_irq_hw[1]->enable, 0); 63062306a36Sopenharmony_ci mb(); 63162306a36Sopenharmony_ci for (i = 0; i < max_real_irqs; ++i) 63262306a36Sopenharmony_ci if (test_bit(i, sleep_save_mask)) 63362306a36Sopenharmony_ci pmac_unmask_irq(irq_get_irq_data(i)); 63462306a36Sopenharmony_ci} 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_cistatic struct syscore_ops pmacpic_syscore_ops = { 63762306a36Sopenharmony_ci .suspend = pmacpic_suspend, 63862306a36Sopenharmony_ci .resume = pmacpic_resume, 63962306a36Sopenharmony_ci}; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_cistatic int __init init_pmacpic_syscore(void) 64262306a36Sopenharmony_ci{ 64362306a36Sopenharmony_ci if (pmac_irq_hw[0]) 64462306a36Sopenharmony_ci register_syscore_ops(&pmacpic_syscore_ops); 64562306a36Sopenharmony_ci return 0; 64662306a36Sopenharmony_ci} 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_cimachine_subsys_initcall(powermac, init_pmacpic_syscore); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci#endif /* CONFIG_PM && CONFIG_PPC32 */ 651