mpic.c
来自「linux 内核源代码」· C语言 代码 · 共 1,607 行 · 第 1/3 页
C
1,607 行
mpic_scan_ht_pic(mpic, devbase, devfn, l); mpic_scan_ht_msi(mpic, devbase, devfn); next: /* next device, if function 0 */ if (PCI_FUNC(devfn) == 0 && (hdr_type & 0x80) == 0) devfn += 7; }}#else /* CONFIG_MPIC_U3_HT_IRQS */static inline int mpic_is_ht_interrupt(struct mpic *mpic, unsigned int source){ return 0;}static void __init mpic_scan_ht_pics(struct mpic *mpic){}#endif /* CONFIG_MPIC_U3_HT_IRQS */#define mpic_irq_to_hw(virq) ((unsigned int)irq_map[virq].hwirq)/* Find an mpic associated with a given linux interrupt */static struct mpic *mpic_find(unsigned int irq, unsigned int *is_ipi){ unsigned int src = mpic_irq_to_hw(irq); struct mpic *mpic; if (irq < NUM_ISA_INTERRUPTS) return NULL; mpic = irq_desc[irq].chip_data; if (is_ipi) *is_ipi = (src >= mpic->ipi_vecs[0] && src <= mpic->ipi_vecs[3]); return mpic;}/* Convert a cpu mask from logical to physical cpu numbers. */static inline u32 mpic_physmask(u32 cpumask){ int i; u32 mask = 0; for (i = 0; i < NR_CPUS; ++i, cpumask >>= 1) mask |= (cpumask & 1) << get_hard_smp_processor_id(i); return mask;}#ifdef CONFIG_SMP/* Get the mpic structure from the IPI number */static inline struct mpic * mpic_from_ipi(unsigned int ipi){ return irq_desc[ipi].chip_data;}#endif/* Get the mpic structure from the irq number */static inline struct mpic * mpic_from_irq(unsigned int irq){ return irq_desc[irq].chip_data;}/* Send an EOI */static inline void mpic_eoi(struct mpic *mpic){ mpic_cpu_write(MPIC_INFO(CPU_EOI), 0); (void)mpic_cpu_read(MPIC_INFO(CPU_WHOAMI));}#ifdef CONFIG_SMPstatic irqreturn_t mpic_ipi_action(int irq, void *dev_id){ struct mpic *mpic; mpic = mpic_find(irq, NULL); smp_message_recv(mpic_irq_to_hw(irq) - mpic->ipi_vecs[0]); return IRQ_HANDLED;}#endif /* CONFIG_SMP *//* * Linux descriptor level callbacks */void mpic_unmask_irq(unsigned int irq){ unsigned int loops = 100000; struct mpic *mpic = mpic_from_irq(irq); unsigned int src = mpic_irq_to_hw(irq); DBG("%p: %s: enable_irq: %d (src %d)\n", mpic, mpic->name, irq, src); mpic_irq_write(src, MPIC_INFO(IRQ_VECTOR_PRI), mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) & ~MPIC_VECPRI_MASK); /* make sure mask gets to controller before we return to user */ do { if (!loops--) { printk(KERN_ERR "mpic_enable_irq timeout\n"); break; } } while(mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) & MPIC_VECPRI_MASK);}void mpic_mask_irq(unsigned int irq){ unsigned int loops = 100000; struct mpic *mpic = mpic_from_irq(irq); unsigned int src = mpic_irq_to_hw(irq); DBG("%s: disable_irq: %d (src %d)\n", mpic->name, irq, src); mpic_irq_write(src, MPIC_INFO(IRQ_VECTOR_PRI), mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) | MPIC_VECPRI_MASK); /* make sure mask gets to controller before we return to user */ do { if (!loops--) { printk(KERN_ERR "mpic_enable_irq timeout\n"); break; } } while(!(mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)) & MPIC_VECPRI_MASK));}void mpic_end_irq(unsigned int irq){ struct mpic *mpic = mpic_from_irq(irq);#ifdef DEBUG_IRQ DBG("%s: end_irq: %d\n", mpic->name, irq);#endif /* We always EOI on end_irq() even for edge interrupts since that * should only lower the priority, the MPIC should have properly * latched another edge interrupt coming in anyway */ mpic_eoi(mpic);}#ifdef CONFIG_MPIC_U3_HT_IRQSstatic void mpic_unmask_ht_irq(unsigned int irq){ struct mpic *mpic = mpic_from_irq(irq); unsigned int src = mpic_irq_to_hw(irq); mpic_unmask_irq(irq); if (irq_desc[irq].status & IRQ_LEVEL) mpic_ht_end_irq(mpic, src);}static unsigned int mpic_startup_ht_irq(unsigned int irq){ struct mpic *mpic = mpic_from_irq(irq); unsigned int src = mpic_irq_to_hw(irq); mpic_unmask_irq(irq); mpic_startup_ht_interrupt(mpic, src, irq_desc[irq].status); return 0;}static void mpic_shutdown_ht_irq(unsigned int irq){ struct mpic *mpic = mpic_from_irq(irq); unsigned int src = mpic_irq_to_hw(irq); mpic_shutdown_ht_interrupt(mpic, src, irq_desc[irq].status); mpic_mask_irq(irq);}static void mpic_end_ht_irq(unsigned int irq){ struct mpic *mpic = mpic_from_irq(irq); unsigned int src = mpic_irq_to_hw(irq);#ifdef DEBUG_IRQ DBG("%s: end_irq: %d\n", mpic->name, irq);#endif /* We always EOI on end_irq() even for edge interrupts since that * should only lower the priority, the MPIC should have properly * latched another edge interrupt coming in anyway */ if (irq_desc[irq].status & IRQ_LEVEL) mpic_ht_end_irq(mpic, src); mpic_eoi(mpic);}#endif /* !CONFIG_MPIC_U3_HT_IRQS */#ifdef CONFIG_SMPstatic void mpic_unmask_ipi(unsigned int irq){ struct mpic *mpic = mpic_from_ipi(irq); unsigned int src = mpic_irq_to_hw(irq) - mpic->ipi_vecs[0]; DBG("%s: enable_ipi: %d (ipi %d)\n", mpic->name, irq, src); mpic_ipi_write(src, mpic_ipi_read(src) & ~MPIC_VECPRI_MASK);}static void mpic_mask_ipi(unsigned int irq){ /* NEVER disable an IPI... that's just plain wrong! */}static void mpic_end_ipi(unsigned int irq){ struct mpic *mpic = mpic_from_ipi(irq); /* * IPIs are marked IRQ_PER_CPU. This has the side effect of * preventing the IRQ_PENDING/IRQ_INPROGRESS logic from * applying to them. We EOI them late to avoid re-entering. * We mark IPI's with IRQF_DISABLED as they must run with * irqs disabled. */ mpic_eoi(mpic);}#endif /* CONFIG_SMP */void mpic_set_affinity(unsigned int irq, cpumask_t cpumask){ struct mpic *mpic = mpic_from_irq(irq); unsigned int src = mpic_irq_to_hw(irq); cpumask_t tmp; cpus_and(tmp, cpumask, cpu_online_map); mpic_irq_write(src, MPIC_INFO(IRQ_DESTINATION), mpic_physmask(cpus_addr(tmp)[0])); }static unsigned int mpic_type_to_vecpri(struct mpic *mpic, unsigned int type){ /* Now convert sense value */ switch(type & IRQ_TYPE_SENSE_MASK) { case IRQ_TYPE_EDGE_RISING: return MPIC_INFO(VECPRI_SENSE_EDGE) | MPIC_INFO(VECPRI_POLARITY_POSITIVE); case IRQ_TYPE_EDGE_FALLING: case IRQ_TYPE_EDGE_BOTH: return MPIC_INFO(VECPRI_SENSE_EDGE) | MPIC_INFO(VECPRI_POLARITY_NEGATIVE); case IRQ_TYPE_LEVEL_HIGH: return MPIC_INFO(VECPRI_SENSE_LEVEL) | MPIC_INFO(VECPRI_POLARITY_POSITIVE); case IRQ_TYPE_LEVEL_LOW: default: return MPIC_INFO(VECPRI_SENSE_LEVEL) | MPIC_INFO(VECPRI_POLARITY_NEGATIVE); }}int mpic_set_irq_type(unsigned int virq, unsigned int flow_type){ struct mpic *mpic = mpic_from_irq(virq); unsigned int src = mpic_irq_to_hw(virq); struct irq_desc *desc = get_irq_desc(virq); unsigned int vecpri, vold, vnew; DBG("mpic: set_irq_type(mpic:@%p,virq:%d,src:0x%x,type:0x%x)\n", mpic, virq, src, flow_type); if (src >= mpic->irq_count) return -EINVAL; if (flow_type == IRQ_TYPE_NONE) if (mpic->senses && src < mpic->senses_count) flow_type = mpic->senses[src]; if (flow_type == IRQ_TYPE_NONE) flow_type = IRQ_TYPE_LEVEL_LOW; desc->status &= ~(IRQ_TYPE_SENSE_MASK | IRQ_LEVEL); desc->status |= flow_type & IRQ_TYPE_SENSE_MASK; if (flow_type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) desc->status |= IRQ_LEVEL; if (mpic_is_ht_interrupt(mpic, src)) vecpri = MPIC_VECPRI_POLARITY_POSITIVE | MPIC_VECPRI_SENSE_EDGE; else vecpri = mpic_type_to_vecpri(mpic, flow_type); vold = mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI)); vnew = vold & ~(MPIC_INFO(VECPRI_POLARITY_MASK) | MPIC_INFO(VECPRI_SENSE_MASK)); vnew |= vecpri; if (vold != vnew) mpic_irq_write(src, MPIC_INFO(IRQ_VECTOR_PRI), vnew); return 0;}static struct irq_chip mpic_irq_chip = { .mask = mpic_mask_irq, .unmask = mpic_unmask_irq, .eoi = mpic_end_irq, .set_type = mpic_set_irq_type,};#ifdef CONFIG_SMPstatic struct irq_chip mpic_ipi_chip = { .mask = mpic_mask_ipi, .unmask = mpic_unmask_ipi, .eoi = mpic_end_ipi,};#endif /* CONFIG_SMP */#ifdef CONFIG_MPIC_U3_HT_IRQSstatic struct irq_chip mpic_irq_ht_chip = { .startup = mpic_startup_ht_irq, .shutdown = mpic_shutdown_ht_irq, .mask = mpic_mask_irq, .unmask = mpic_unmask_ht_irq, .eoi = mpic_end_ht_irq, .set_type = mpic_set_irq_type,};#endif /* CONFIG_MPIC_U3_HT_IRQS */static int mpic_host_match(struct irq_host *h, struct device_node *node){ /* Exact match, unless mpic node is NULL */ return h->of_node == NULL || h->of_node == node;}static int mpic_host_map(struct irq_host *h, unsigned int virq, irq_hw_number_t hw){ struct mpic *mpic = h->host_data; struct irq_chip *chip; DBG("mpic: map virq %d, hwirq 0x%lx\n", virq, hw); if (hw == mpic->spurious_vec) return -EINVAL; if (mpic->protected && test_bit(hw, mpic->protected)) return -EINVAL;#ifdef CONFIG_SMP else if (hw >= mpic->ipi_vecs[0]) { WARN_ON(!(mpic->flags & MPIC_PRIMARY)); DBG("mpic: mapping as IPI\n"); set_irq_chip_data(virq, mpic); set_irq_chip_and_handler(virq, &mpic->hc_ipi, handle_percpu_irq); return 0; }#endif /* CONFIG_SMP */ if (hw >= mpic->irq_count) return -EINVAL; mpic_msi_reserve_hwirq(mpic, hw); /* Default chip */ chip = &mpic->hc_irq;#ifdef CONFIG_MPIC_U3_HT_IRQS /* Check for HT interrupts, override vecpri */ if (mpic_is_ht_interrupt(mpic, hw)) chip = &mpic->hc_ht_irq;#endif /* CONFIG_MPIC_U3_HT_IRQS */ DBG("mpic: mapping to irq chip @%p\n", chip); set_irq_chip_data(virq, mpic); set_irq_chip_and_handler(virq, chip, handle_fasteoi_irq); /* Set default irq type */ set_irq_type(virq, IRQ_TYPE_NONE); return 0;}static int mpic_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){ static unsigned char map_mpic_senses[4] = { IRQ_TYPE_EDGE_RISING, IRQ_TYPE_LEVEL_LOW, IRQ_TYPE_LEVEL_HIGH, IRQ_TYPE_EDGE_FALLING, }; *out_hwirq = intspec[0]; if (intsize > 1) { u32 mask = 0x3; /* Apple invented a new race of encoding on machines with * an HT APIC. They encode, among others, the index within * the HT APIC. We don't care about it here since thankfully, * it appears that they have the APIC already properly * configured, and thus our current fixup code that reads the * APIC config works fine. However, we still need to mask out * bits in the specifier to make sure we only get bit 0 which * is the level/edge bit (the only sense bit exposed by Apple), * as their bit 1 means something else. */ if (machine_is(powermac)) mask = 0x1; *out_flags = map_mpic_senses[intspec[1] & mask]; } else *out_flags = IRQ_TYPE_NONE; DBG("mpic: xlate (%d cells: 0x%08x 0x%08x) to line 0x%lx sense 0x%x\n", intsize, intspec[0], intspec[1], *out_hwirq, *out_flags); return 0;}static struct irq_host_ops mpic_host_ops = { .match = mpic_host_match, .map = mpic_host_map, .xlate = mpic_host_xlate,};/* * Exported functions */struct mpic * __init mpic_alloc(struct device_node *node, phys_addr_t phys_addr, unsigned int flags, unsigned int isu_size, unsigned int irq_count, const char *name){ struct mpic *mpic; u32 reg; const char *vers; int i; int intvec_top; u64 paddr = phys_addr; mpic = alloc_bootmem(sizeof(struct mpic)); if (mpic == NULL) return NULL; memset(mpic, 0, sizeof(struct mpic)); mpic->name = name; mpic->irqhost = irq_alloc_host(of_node_get(node), IRQ_HOST_MAP_LINEAR, isu_size, &mpic_host_ops, flags & MPIC_LARGE_VECTORS ? 2048 : 256); if (mpic->irqhost == NULL) { of_node_put(node); return NULL; } mpic->irqhost->host_data = mpic; mpic->hc_irq = mpic_irq_chip; mpic->hc_irq.typename = name; if (flags & MPIC_PRIMARY) mpic->hc_irq.set_affinity = mpic_set_affinity;#ifdef CONFIG_MPIC_U3_HT_IRQS mpic->hc_ht_irq = mpic_irq_ht_chip; mpic->hc_ht_irq.typename = name; if (flags & MPIC_PRIMARY) mpic->hc_ht_irq.set_affinity = mpic_set_affinity;#endif /* CONFIG_MPIC_U3_HT_IRQS */#ifdef CONFIG_SMP mpic->hc_ipi = mpic_ipi_chip; mpic->hc_ipi.typename = name;#endif /* CONFIG_SMP */ mpic->flags = flags; mpic->isu_size = isu_size; mpic->irq_count = irq_count; mpic->num_sources = 0; /* so far */ if (flags & MPIC_LARGE_VECTORS) intvec_top = 2047; else intvec_top = 255; mpic->timer_vecs[0] = intvec_top - 8; mpic->timer_vecs[1] = intvec_top - 7; mpic->timer_vecs[2] = intvec_top - 6; mpic->timer_vecs[3] = intvec_top - 5; mpic->ipi_vecs[0] = intvec_top - 4; mpic->ipi_vecs[1] = intvec_top - 3; mpic->ipi_vecs[2] = intvec_top - 2; mpic->ipi_vecs[3] = intvec_top - 1; mpic->spurious_vec = intvec_top; /* Check for "big-endian" in device-tree */ if (node && of_get_property(node, "big-endian", NULL) != NULL) mpic->flags |= MPIC_BIG_ENDIAN; /* Look for protected sources */ if (node) { unsigned int psize, bits, mapsize; const u32 *psrc = of_get_property(node, "protected-sources", &psize); if (psrc) { psize /= 4; bits = intvec_top + 1; mapsize = BITS_TO_LONGS(bits) * sizeof(unsigned long); mpic->protected = alloc_bootmem(mapsize); BUG_ON(mpic->protected == NULL); memset(mpic->protected, 0, mapsize); for (i = 0; i < psize; i++) { if (psrc[i] > intvec_top) continue; __set_bit(psrc[i], mpic->protected); } } }#ifdef CONFIG_MPIC_WEIRD mpic->hw_set = mpic_infos[MPIC_GET_REGSET(flags)];#endif /* default register type */ mpic->reg_type = (flags & MPIC_BIG_ENDIAN) ? mpic_access_mmio_be : mpic_access_mmio_le;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?