📄 pic.c
字号:
/* * Support for the interrupt controllers found on Power Macintosh, * currently Apple's "Grand Central" interrupt controller in all * it's incarnations. OpenPIC support used on newer machines is * in a separate file * * Copyright (C) 1997 Paul Mackerras (paulus@samba.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 * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * */#include <linux/stddef.h>#include <linux/init.h>#include <linux/sched.h>#include <linux/signal.h>#include <linux/pci.h>#include <linux/interrupt.h>#include <linux/sysdev.h>#include <linux/adb.h>#include <linux/pmu.h>#include <linux/module.h>#include <asm/sections.h>#include <asm/io.h>#include <asm/smp.h>#include <asm/prom.h>#include <asm/pci-bridge.h>#include <asm/time.h>#include <asm/pmac_feature.h>#include <asm/mpic.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_PPC32struct pmac_irq_hw { unsigned int event; unsigned int enable; unsigned int ack; unsigned int level;};/* 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 0x1ff00000static int max_irqs;static int max_real_irqs;static u32 level_mask[4];static DEFINE_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];static int pmac_irq_cascade = -1;static struct irq_host *pmac_pic_host;static void __pmac_retrigger(unsigned int irq_nr){ 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); set_dec(1); }}static void pmac_mask_and_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); __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 { /* make sure ack gets to controller before we enable interrupts */ mb(); } while((in_le32(&pmac_irq_hw[i]->enable) & bit) != (ppc_cached_irq_mask[i] & bit)); spin_unlock_irqrestore(&pmac_pic_lock, flags);}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; if ((unsigned)irq_nr >= max_irqs) return; /* enable unmasked interrupts */ out_le32(&pmac_irq_hw[i]->enable, ppc_cached_irq_mask[i]); do { /* make sure mask gets to controller before we return to user */ mb(); } while((in_le32(&pmac_irq_hw[i]->enable) & bit) != (ppc_cached_irq_mask[i] & bit)); /* * Unfortunately, setting the bit in the enable register * when the device interrupt is already on *doesn't* set * the bit in the flag register or request another interrupt. */ if (bit & ppc_cached_irq_mask[i] & in_le32(&pmac_irq_hw[i]->level)) __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 virq){ unsigned long flags; unsigned int src = irq_map[virq].hwirq; 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) 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); return 0;}static void pmac_mask_irq(unsigned int virq){ 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, 1); spin_unlock_irqrestore(&pmac_pic_lock, flags);}static void pmac_unmask_irq(unsigned int virq){ 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 int pmac_retrigger(unsigned int virq){ 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;}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,};static irqreturn_t gatwick_action(int cpl, void *dev_id){ 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]; /* We must read level interrupts from the level register */ bits |= (in_le32(&pmac_irq_hw[i]->level) & level_mask[i]); 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); rc = IRQ_HANDLED; } spin_unlock_irqrestore(&pmac_pic_lock, flags); return rc;}static unsigned int pmac_pic_get_irq(void){ int irq; 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 */ }#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]; /* We must read level interrupts from the level register */ bits |= (in_le32(&pmac_irq_hw[i]->level) & level_mask[i]); bits &= ppc_cached_irq_mask[i]; if (bits == 0) continue; irq += __ilog2(bits); break; } spin_unlock_irqrestore(&pmac_pic_lock, flags); if (unlikely(irq < 0)) return NO_IRQ; return irq_linear_revmap(pmac_pic_host, irq);}#ifdef CONFIG_XMONstatic struct irqaction xmon_action = { .handler = xmon_irq, .flags = 0, .mask = CPU_MASK_NONE, .name = "NMI - XMON"};#endifstatic 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){ /* 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, 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); 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 = { .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 */ 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) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -