📄 irq.c
字号:
pr_debug("irq: Default host set to @0x%p\n", host); irq_default_host = host;}void irq_set_virq_count(unsigned int count){ pr_debug("irq: Trying to set virq count to %d\n", count); BUG_ON(count < NUM_ISA_INTERRUPTS); if (count < NR_IRQS) irq_virq_count = count;}/* radix tree not lockless safe ! we use a brlock-type mecanism * for now, until we can use a lockless radix tree */static void irq_radix_wrlock(unsigned long *flags){ unsigned int cpu, ok; spin_lock_irqsave(&irq_big_lock, *flags); irq_radix_writer = 1; smp_mb(); do { barrier(); ok = 1; for_each_possible_cpu(cpu) { if (per_cpu(irq_radix_reader, cpu)) { ok = 0; break; } } if (!ok) cpu_relax(); } while(!ok);}static void irq_radix_wrunlock(unsigned long flags){ smp_wmb(); irq_radix_writer = 0; spin_unlock_irqrestore(&irq_big_lock, flags);}static void irq_radix_rdlock(unsigned long *flags){ local_irq_save(*flags); __get_cpu_var(irq_radix_reader) = 1; smp_mb(); if (likely(irq_radix_writer == 0)) return; __get_cpu_var(irq_radix_reader) = 0; smp_wmb(); spin_lock(&irq_big_lock); __get_cpu_var(irq_radix_reader) = 1; spin_unlock(&irq_big_lock);}static void irq_radix_rdunlock(unsigned long flags){ __get_cpu_var(irq_radix_reader) = 0; local_irq_restore(flags);}static int irq_setup_virq(struct irq_host *host, unsigned int virq, irq_hw_number_t hwirq){ /* Clear IRQ_NOREQUEST flag */ get_irq_desc(virq)->status &= ~IRQ_NOREQUEST; /* map it */ smp_wmb(); irq_map[virq].hwirq = hwirq; smp_mb(); if (host->ops->map(host, virq, hwirq)) { pr_debug("irq: -> mapping failed, freeing\n"); irq_free_virt(virq, 1); return -1; } return 0;}unsigned int irq_create_direct_mapping(struct irq_host *host){ unsigned int virq; if (host == NULL) host = irq_default_host; BUG_ON(host == NULL); WARN_ON(host->revmap_type != IRQ_HOST_MAP_NOMAP); virq = irq_alloc_virt(host, 1, 0); if (virq == NO_IRQ) { pr_debug("irq: create_direct virq allocation failed\n"); return NO_IRQ; } pr_debug("irq: create_direct obtained virq %d\n", virq); if (irq_setup_virq(host, virq, virq)) return NO_IRQ; return virq;}unsigned int irq_create_mapping(struct irq_host *host, irq_hw_number_t hwirq){ unsigned int virq, hint; pr_debug("irq: irq_create_mapping(0x%p, 0x%lx)\n", host, hwirq); /* Look for default host if nececssary */ if (host == NULL) host = irq_default_host; if (host == NULL) { printk(KERN_WARNING "irq_create_mapping called for" " NULL host, hwirq=%lx\n", hwirq); WARN_ON(1); return NO_IRQ; } pr_debug("irq: -> using host @%p\n", host); /* Check if mapping already exist, if it does, call * host->ops->map() to update the flags */ virq = irq_find_mapping(host, hwirq); if (virq != NO_IRQ) { if (host->ops->remap) host->ops->remap(host, virq, hwirq); pr_debug("irq: -> existing mapping on virq %d\n", virq); return virq; } /* Get a virtual interrupt number */ if (host->revmap_type == IRQ_HOST_MAP_LEGACY) { /* Handle legacy */ virq = (unsigned int)hwirq; if (virq == 0 || virq >= NUM_ISA_INTERRUPTS) return NO_IRQ; return virq; } else { /* Allocate a virtual interrupt number */ hint = hwirq % irq_virq_count; virq = irq_alloc_virt(host, 1, hint); if (virq == NO_IRQ) { pr_debug("irq: -> virq allocation failed\n"); return NO_IRQ; } } pr_debug("irq: -> obtained virq %d\n", virq); if (irq_setup_virq(host, virq, hwirq)) return NO_IRQ; return virq;}EXPORT_SYMBOL_GPL(irq_create_mapping);unsigned int irq_create_of_mapping(struct device_node *controller, u32 *intspec, unsigned int intsize){ struct irq_host *host; irq_hw_number_t hwirq; unsigned int type = IRQ_TYPE_NONE; unsigned int virq; if (controller == NULL) host = irq_default_host; else host = irq_find_host(controller); if (host == NULL) { printk(KERN_WARNING "irq: no irq host found for %s !\n", controller->full_name); return NO_IRQ; } /* If host has no translation, then we assume interrupt line */ if (host->ops->xlate == NULL) hwirq = intspec[0]; else { if (host->ops->xlate(host, controller, intspec, intsize, &hwirq, &type)) return NO_IRQ; } /* Create mapping */ virq = irq_create_mapping(host, hwirq); if (virq == NO_IRQ) return virq; /* Set type if specified and different than the current one */ if (type != IRQ_TYPE_NONE && type != (get_irq_desc(virq)->status & IRQF_TRIGGER_MASK)) set_irq_type(virq, type); return virq;}EXPORT_SYMBOL_GPL(irq_create_of_mapping);unsigned int irq_of_parse_and_map(struct device_node *dev, int index){ struct of_irq oirq; if (of_irq_map_one(dev, index, &oirq)) return NO_IRQ; return irq_create_of_mapping(oirq.controller, oirq.specifier, oirq.size);}EXPORT_SYMBOL_GPL(irq_of_parse_and_map);void irq_dispose_mapping(unsigned int virq){ struct irq_host *host; irq_hw_number_t hwirq; unsigned long flags; if (virq == NO_IRQ) return; host = irq_map[virq].host; WARN_ON (host == NULL); if (host == NULL) return; /* Never unmap legacy interrupts */ if (host->revmap_type == IRQ_HOST_MAP_LEGACY) return; /* remove chip and handler */ set_irq_chip_and_handler(virq, NULL, NULL); /* Make sure it's completed */ synchronize_irq(virq); /* Tell the PIC about it */ if (host->ops->unmap) host->ops->unmap(host, virq); smp_mb(); /* Clear reverse map */ hwirq = irq_map[virq].hwirq; switch(host->revmap_type) { case IRQ_HOST_MAP_LINEAR: if (hwirq < host->revmap_data.linear.size) host->revmap_data.linear.revmap[hwirq] = NO_IRQ; break; case IRQ_HOST_MAP_TREE: /* Check if radix tree allocated yet */ if (host->revmap_data.tree.gfp_mask == 0) break; irq_radix_wrlock(&flags); radix_tree_delete(&host->revmap_data.tree, hwirq); irq_radix_wrunlock(flags); break; } /* Destroy map */ smp_mb(); irq_map[virq].hwirq = host->inval_irq; /* Set some flags */ get_irq_desc(virq)->status |= IRQ_NOREQUEST; /* Free it */ irq_free_virt(virq, 1);}EXPORT_SYMBOL_GPL(irq_dispose_mapping);unsigned int irq_find_mapping(struct irq_host *host, irq_hw_number_t hwirq){ unsigned int i; unsigned int hint = hwirq % irq_virq_count; /* Look for default host if nececssary */ if (host == NULL) host = irq_default_host; if (host == NULL) return NO_IRQ; /* legacy -> bail early */ if (host->revmap_type == IRQ_HOST_MAP_LEGACY) return hwirq; /* Slow path does a linear search of the map */ if (hint < NUM_ISA_INTERRUPTS) hint = NUM_ISA_INTERRUPTS; i = hint; do { if (irq_map[i].host == host && irq_map[i].hwirq == hwirq) return i; i++; if (i >= irq_virq_count) i = NUM_ISA_INTERRUPTS; } while(i != hint); return NO_IRQ;}EXPORT_SYMBOL_GPL(irq_find_mapping);unsigned int irq_radix_revmap(struct irq_host *host, irq_hw_number_t hwirq){ struct radix_tree_root *tree; struct irq_map_entry *ptr; unsigned int virq; unsigned long flags; WARN_ON(host->revmap_type != IRQ_HOST_MAP_TREE); /* Check if the radix tree exist yet. We test the value of * the gfp_mask for that. Sneaky but saves another int in the * structure. If not, we fallback to slow mode */ tree = &host->revmap_data.tree; if (tree->gfp_mask == 0) return irq_find_mapping(host, hwirq); /* Now try to resolve */ irq_radix_rdlock(&flags); ptr = radix_tree_lookup(tree, hwirq); irq_radix_rdunlock(flags); /* Found it, return */ if (ptr) { virq = ptr - irq_map; return virq; } /* If not there, try to insert it */ virq = irq_find_mapping(host, hwirq); if (virq != NO_IRQ) { irq_radix_wrlock(&flags); radix_tree_insert(tree, hwirq, &irq_map[virq]); irq_radix_wrunlock(flags); } return virq;}unsigned int irq_linear_revmap(struct irq_host *host, irq_hw_number_t hwirq){ unsigned int *revmap; WARN_ON(host->revmap_type != IRQ_HOST_MAP_LINEAR); /* Check revmap bounds */ if (unlikely(hwirq >= host->revmap_data.linear.size)) return irq_find_mapping(host, hwirq); /* Check if revmap was allocated */ revmap = host->revmap_data.linear.revmap; if (unlikely(revmap == NULL)) return irq_find_mapping(host, hwirq); /* Fill up revmap with slow path if no mapping found */ if (unlikely(revmap[hwirq] == NO_IRQ)) revmap[hwirq] = irq_find_mapping(host, hwirq); return revmap[hwirq];}unsigned int irq_alloc_virt(struct irq_host *host, unsigned int count, unsigned int hint){ unsigned long flags; unsigned int i, j, found = NO_IRQ; if (count == 0 || count > (irq_virq_count - NUM_ISA_INTERRUPTS)) return NO_IRQ; spin_lock_irqsave(&irq_big_lock, flags); /* Use hint for 1 interrupt if any */ if (count == 1 && hint >= NUM_ISA_INTERRUPTS && hint < irq_virq_count && irq_map[hint].host == NULL) { found = hint; goto hint_found; } /* Look for count consecutive numbers in the allocatable * (non-legacy) space */ for (i = NUM_ISA_INTERRUPTS, j = 0; i < irq_virq_count; i++) { if (irq_map[i].host != NULL) j = 0; else j++; if (j == count) { found = i - count + 1; break; } } if (found == NO_IRQ) { spin_unlock_irqrestore(&irq_big_lock, flags); return NO_IRQ; } hint_found: for (i = found; i < (found + count); i++) { irq_map[i].hwirq = host->inval_irq; smp_wmb(); irq_map[i].host = host; } spin_unlock_irqrestore(&irq_big_lock, flags); return found;}void irq_free_virt(unsigned int virq, unsigned int count){ unsigned long flags; unsigned int i; WARN_ON (virq < NUM_ISA_INTERRUPTS); WARN_ON (count == 0 || (virq + count) > irq_virq_count); spin_lock_irqsave(&irq_big_lock, flags); for (i = virq; i < (virq + count); i++) { struct irq_host *host; if (i < NUM_ISA_INTERRUPTS || (virq + count) > irq_virq_count) continue; host = irq_map[i].host; irq_map[i].hwirq = host->inval_irq; smp_wmb(); irq_map[i].host = NULL; } spin_unlock_irqrestore(&irq_big_lock, flags);}void irq_early_init(void){ unsigned int i; for (i = 0; i < NR_IRQS; i++) get_irq_desc(i)->status |= IRQ_NOREQUEST;}/* We need to create the radix trees late */static int irq_late_init(void){ struct irq_host *h; unsigned long flags; irq_radix_wrlock(&flags); list_for_each_entry(h, &irq_hosts, link) { if (h->revmap_type == IRQ_HOST_MAP_TREE) INIT_RADIX_TREE(&h->revmap_data.tree, GFP_ATOMIC); } irq_radix_wrunlock(flags); return 0;}arch_initcall(irq_late_init);#ifdef CONFIG_VIRQ_DEBUGstatic int virq_debug_show(struct seq_file *m, void *private){ unsigned long flags; irq_desc_t *desc; const char *p; char none[] = "none"; int i; seq_printf(m, "%-5s %-7s %-15s %s\n", "virq", "hwirq", "chip name", "host name"); for (i = 1; i < NR_IRQS; i++) { desc = get_irq_desc(i); spin_lock_irqsave(&desc->lock, flags); if (desc->action && desc->action->handler) { seq_printf(m, "%5d ", i); seq_printf(m, "0x%05lx ", virq_to_hw(i)); if (desc->chip && desc->chip->typename) p = desc->chip->typename; else p = none; seq_printf(m, "%-15s ", p); if (irq_map[i].host && irq_map[i].host->of_node) p = irq_map[i].host->of_node->full_name; else p = none; seq_printf(m, "%s\n", p); } spin_unlock_irqrestore(&desc->lock, flags); } return 0;}static int virq_debug_open(struct inode *inode, struct file *file){ return single_open(file, virq_debug_show, inode->i_private);}static const struct file_operations virq_debug_fops = { .open = virq_debug_open, .read = seq_read, .llseek = seq_lseek, .release = single_release,};static int __init irq_debugfs_init(void){ if (debugfs_create_file("virq_mapping", S_IRUGO, powerpc_debugfs_root, NULL, &virq_debug_fops)) return -ENOMEM; return 0;}__initcall(irq_debugfs_init);#endif /* CONFIG_VIRQ_DEBUG */#endif /* CONFIG_PPC_MERGE */#ifdef CONFIG_PPC64static int __init setup_noirqdistrib(char *str){ distribute_irqs = 0; return 1;}__setup("noirqdistrib", setup_noirqdistrib);#endif /* CONFIG_PPC64 */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -