📄 io_apic.c
字号:
int tmp = irq; if (use_pci_vector()) { if (!platform_legacy_irq(tmp)) if ((tmp = vector_to_irq(tmp)) == -1) continue; } if (IO_APIC_IRQ(tmp) && !IO_APIC_VECTOR(tmp)) { /* * Hmm.. We don't have an entry for this, * so default to an old-fashioned 8259 * interrupt if we can.. */ if (irq < 16) make_8259A_irq(irq); else /* Strange. Oh, well.. */ irq_desc[irq].handler = &no_irq_type; } }}static void enable_lapic_irq (unsigned int irq){ unsigned long v; v = apic_read(APIC_LVT0); apic_write_around(APIC_LVT0, v & ~APIC_LVT_MASKED);}static void disable_lapic_irq (unsigned int irq){ unsigned long v; v = apic_read(APIC_LVT0); apic_write_around(APIC_LVT0, v | APIC_LVT_MASKED);}static void ack_lapic_irq (unsigned int irq){ ack_APIC_irq();}static void end_lapic_irq (unsigned int i) { /* nothing */ }static struct hw_interrupt_type lapic_irq_type __read_mostly = { .typename = "local-APIC-edge", .startup = NULL, /* startup_irq() not used for IRQ0 */ .shutdown = NULL, /* shutdown_irq() not used for IRQ0 */ .enable = enable_lapic_irq, .disable = disable_lapic_irq, .ack = ack_lapic_irq, .end = end_lapic_irq,};static void setup_nmi (void){ /* * Dirty trick to enable the NMI watchdog ... * We put the 8259A master into AEOI mode and * unmask on all local APICs LVT0 as NMI. * * The idea to use the 8259A in AEOI mode ('8259A Virtual Wire') * is from Maciej W. Rozycki - so we do not have to EOI from * the NMI handler or the timer interrupt. */ printk(KERN_INFO "activating NMI Watchdog ..."); enable_NMI_through_LVT0(NULL); printk(" done.\n");}/* * This looks a bit hackish but it's about the only one way of sending * a few INTA cycles to 8259As and any associated glue logic. ICR does * not support the ExtINT mode, unfortunately. We need to send these * cycles as some i82489DX-based boards have glue logic that keeps the * 8259A interrupt line asserted until INTA. --macro */static inline void unlock_ExtINT_logic(void){ int pin, i; struct IO_APIC_route_entry entry0, entry1; unsigned char save_control, save_freq_select; unsigned long flags; pin = find_isa_irq_pin(8, mp_INT); if (pin == -1) return; spin_lock_irqsave(&ioapic_lock, flags); *(((int *)&entry0) + 1) = io_apic_read(0, 0x11 + 2 * pin); *(((int *)&entry0) + 0) = io_apic_read(0, 0x10 + 2 * pin); spin_unlock_irqrestore(&ioapic_lock, flags); clear_IO_APIC_pin(0, pin); memset(&entry1, 0, sizeof(entry1)); entry1.dest_mode = 0; /* physical delivery */ entry1.mask = 0; /* unmask IRQ now */ entry1.dest.physical.physical_dest = hard_smp_processor_id(); entry1.delivery_mode = dest_ExtINT; entry1.polarity = entry0.polarity; entry1.trigger = 0; entry1.vector = 0; spin_lock_irqsave(&ioapic_lock, flags); io_apic_write(0, 0x11 + 2 * pin, *(((int *)&entry1) + 1)); io_apic_write(0, 0x10 + 2 * pin, *(((int *)&entry1) + 0)); spin_unlock_irqrestore(&ioapic_lock, flags); save_control = CMOS_READ(RTC_CONTROL); save_freq_select = CMOS_READ(RTC_FREQ_SELECT); CMOS_WRITE((save_freq_select & ~RTC_RATE_SELECT) | 0x6, RTC_FREQ_SELECT); CMOS_WRITE(save_control | RTC_PIE, RTC_CONTROL); i = 100; while (i-- > 0) { mdelay(10); if ((CMOS_READ(RTC_INTR_FLAGS) & RTC_PF) == RTC_PF) i -= 10; } CMOS_WRITE(save_control, RTC_CONTROL); CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT); clear_IO_APIC_pin(0, pin); spin_lock_irqsave(&ioapic_lock, flags); io_apic_write(0, 0x11 + 2 * pin, *(((int *)&entry0) + 1)); io_apic_write(0, 0x10 + 2 * pin, *(((int *)&entry0) + 0)); spin_unlock_irqrestore(&ioapic_lock, flags);}/* * This code may look a bit paranoid, but it's supposed to cooperate with * a wide range of boards and BIOS bugs. Fortunately only the timer IRQ * is so screwy. Thanks to Brian Perkins for testing/hacking this beast * fanatically on his truly buggy board. */static inline void check_timer(void){ int pin1, pin2; int vector; /* * get/set the timer IRQ vector: */ disable_8259A_irq(0); vector = assign_irq_vector(0); set_intr_gate(vector, interrupt[0]); /* * Subtle, code in do_timer_interrupt() expects an AEOI * mode for the 8259A whenever interrupts are routed * through I/O APICs. Also IRQ0 has to be enabled in * the 8259A which implies the virtual wire has to be * disabled in the local APIC. */ apic_write_around(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_EXTINT); init_8259A(1); enable_8259A_irq(0); pin1 = find_isa_irq_pin(0, mp_INT); pin2 = find_isa_irq_pin(0, mp_ExtINT); apic_printk(APIC_VERBOSE,KERN_INFO "..TIMER: vector=0x%02X pin1=%d pin2=%d\n", vector, pin1, pin2); if (pin1 != -1) { /* * Ok, does IRQ0 through the IOAPIC work? */ unmask_IO_APIC_irq(0); if (!no_timer_check && timer_irq_works()) { nmi_watchdog_default(); if (nmi_watchdog == NMI_IO_APIC) { disable_8259A_irq(0); setup_nmi(); enable_8259A_irq(0); } if (disable_timer_pin_1 > 0) clear_IO_APIC_pin(0, pin1); return; } clear_IO_APIC_pin(0, pin1); apic_printk(APIC_QUIET,KERN_ERR "..MP-BIOS bug: 8254 timer not connected to IO-APIC\n"); } apic_printk(APIC_VERBOSE,KERN_INFO "...trying to set up timer (IRQ0) through the 8259A ... "); if (pin2 != -1) { apic_printk(APIC_VERBOSE,"\n..... (found pin %d) ...", pin2); /* * legacy devices should be connected to IO APIC #0 */ setup_ExtINT_IRQ0_pin(pin2, vector); if (timer_irq_works()) { printk("works.\n"); nmi_watchdog_default(); if (nmi_watchdog == NMI_IO_APIC) { setup_nmi(); } return; } /* * Cleanup, just in case ... */ clear_IO_APIC_pin(0, pin2); } printk(" failed.\n"); if (nmi_watchdog) { printk(KERN_WARNING "timer doesn't work through the IO-APIC - disabling NMI Watchdog!\n"); nmi_watchdog = 0; } apic_printk(APIC_VERBOSE, KERN_INFO "...trying to set up timer as Virtual Wire IRQ..."); disable_8259A_irq(0); irq_desc[0].handler = &lapic_irq_type; apic_write_around(APIC_LVT0, APIC_DM_FIXED | vector); /* Fixed mode */ enable_8259A_irq(0); if (timer_irq_works()) { apic_printk(APIC_QUIET, " works.\n"); return; } apic_write_around(APIC_LVT0, APIC_LVT_MASKED | APIC_DM_FIXED | vector); apic_printk(APIC_VERBOSE," failed.\n"); apic_printk(APIC_VERBOSE, KERN_INFO "...trying to set up timer as ExtINT IRQ..."); init_8259A(0); make_8259A_irq(0); apic_write_around(APIC_LVT0, APIC_DM_EXTINT); unlock_ExtINT_logic(); if (timer_irq_works()) { apic_printk(APIC_VERBOSE," works.\n"); return; } apic_printk(APIC_VERBOSE," failed :(.\n"); panic("IO-APIC + timer doesn't work! Try using the 'noapic' kernel parameter\n");}static int __init notimercheck(char *s){ no_timer_check = 1; return 1;}__setup("no_timer_check", notimercheck);/* * * IRQ's that are handled by the PIC in the MPS IOAPIC case. * - IRQ2 is the cascade IRQ, and cannot be a io-apic IRQ. * Linux doesn't really care, as it's not actually used * for any interrupt handling anyway. */#define PIC_IRQS (1<<2)void __init setup_IO_APIC(void){ enable_IO_APIC(); if (acpi_ioapic) io_apic_irqs = ~0; /* all IRQs go through IOAPIC */ else io_apic_irqs = ~PIC_IRQS; apic_printk(APIC_VERBOSE, "ENABLING IO-APIC IRQs\n"); /* * Set up the IO-APIC IRQ routing table. */ if (!acpi_ioapic) setup_ioapic_ids_from_mpc(); sync_Arb_IDs(); setup_IO_APIC_irqs(); init_IO_APIC_traps(); check_timer(); if (!acpi_ioapic) print_IO_APIC();}struct sysfs_ioapic_data { struct sys_device dev; struct IO_APIC_route_entry entry[0];};static struct sysfs_ioapic_data * mp_ioapic_data[MAX_IO_APICS];static int ioapic_suspend(struct sys_device *dev, pm_message_t state){ struct IO_APIC_route_entry *entry; struct sysfs_ioapic_data *data; unsigned long flags; int i; data = container_of(dev, struct sysfs_ioapic_data, dev); entry = data->entry; spin_lock_irqsave(&ioapic_lock, flags); for (i = 0; i < nr_ioapic_registers[dev->id]; i ++, entry ++ ) { *(((int *)entry) + 1) = io_apic_read(dev->id, 0x11 + 2 * i); *(((int *)entry) + 0) = io_apic_read(dev->id, 0x10 + 2 * i); } spin_unlock_irqrestore(&ioapic_lock, flags); return 0;}static int ioapic_resume(struct sys_device *dev){ struct IO_APIC_route_entry *entry; struct sysfs_ioapic_data *data; unsigned long flags; union IO_APIC_reg_00 reg_00; int i; data = container_of(dev, struct sysfs_ioapic_data, dev); entry = data->entry; spin_lock_irqsave(&ioapic_lock, flags); reg_00.raw = io_apic_read(dev->id, 0); if (reg_00.bits.ID != mp_ioapics[dev->id].mpc_apicid) { reg_00.bits.ID = mp_ioapics[dev->id].mpc_apicid; io_apic_write(dev->id, 0, reg_00.raw); } for (i = 0; i < nr_ioapic_registers[dev->id]; i ++, entry ++ ) { io_apic_write(dev->id, 0x11+2*i, *(((int *)entry)+1)); io_apic_write(dev->id, 0x10+2*i, *(((int *)entry)+0)); } spin_unlock_irqrestore(&ioapic_lock, flags); return 0;}static struct sysdev_class ioapic_sysdev_class = { set_kset_name("ioapic"), .suspend = ioapic_suspend, .resume = ioapic_resume,};static int __init ioapic_init_sysfs(void){ struct sys_device * dev; int i, size, error = 0; error = sysdev_class_register(&ioapic_sysdev_class); if (error) return error; for (i = 0; i < nr_ioapics; i++ ) { size = sizeof(struct sys_device) + nr_ioapic_registers[i] * sizeof(struct IO_APIC_route_entry); mp_ioapic_data[i] = kmalloc(size, GFP_KERNEL); if (!mp_ioapic_data[i]) { printk(KERN_ERR "Can't suspend/resume IOAPIC %d\n", i); continue; } memset(mp_ioapic_data[i], 0, size); dev = &mp_ioapic_data[i]->dev; dev->id = i; dev->cls = &ioapic_sysdev_class; error = sysdev_register(dev); if (error) { kfree(mp_ioapic_data[i]); mp_ioapic_data[i] = NULL; printk(KERN_ERR "Can't suspend/resume IOAPIC %d\n", i); continue; } } return 0;}device_initcall(ioapic_init_sysfs);/* -------------------------------------------------------------------------- ACPI-based IOAPIC Configuration -------------------------------------------------------------------------- */#ifdef CONFIG_ACPI#define IO_APIC_MAX_ID 0xFEint __init io_apic_get_version (int ioapic){ union IO_APIC_reg_01 reg_01; unsigned long flags; spin_lock_irqsave(&ioapic_lock, flags); reg_01.raw = io_apic_read(ioapic, 1); spin_unlock_irqrestore(&ioapic_lock, flags); return reg_01.bits.version;}int __init io_apic_get_redir_entries (int ioapic){ union IO_APIC_reg_01 reg_01; unsigned long flags; spin_lock_irqsave(&ioapic_lock, flags); reg_01.raw = io_apic_read(ioapic, 1); spin_unlock_irqrestore(&ioapic_lock, flags); return reg_01.bits.entries;}int io_apic_set_pci_routing (int ioapic, int pin, int irq, int edge_level, int active_high_low){ struct IO_APIC_route_entry entry; unsigned long flags; if (!IO_APIC_IRQ(irq)) { apic_printk(APIC_QUIET,KERN_ERR "IOAPIC[%d]: Invalid reference to IRQ 0\n", ioapic); return -EINVAL; } /* * Generate a PCI IRQ routing entry and program the IOAPIC accordingly. * Note that we mask (disable) IRQs now -- these get enabled when the * corresponding device driver registers for this IRQ. */ memset(&entry,0,sizeof(entry)); entry.delivery_mode = INT_DELIVERY_MODE; entry.dest_mode = INT_DEST_MODE; entry.dest.logical.logical_dest = cpu_mask_to_apicid(TARGET_CPUS); entry.trigger = edge_level; entry.polarity = active_high_low; entry.mask = 1; /* Disabled (masked) */ /* * IRQs < 16 are already in the irq_2_pin[] map */ if (irq >= 16) add_pin_to_irq(irq, ioapic, pin); entry.vector = assign_irq_vector(irq); apic_printk(APIC_VERBOSE,KERN_DEBUG "IOAPIC[%d]: Set PCI routing entry (%d-%d -> 0x%x -> " "IRQ %d Mode:%i Active:%i)\n", ioapic, mp_ioapics[ioapic].mpc_apicid, pin, entry.vector, irq, edge_level, active_high_low); ioapic_register_intr(irq, entry.vector, edge_level); if (!ioapic && (irq < 16)) disable_8259A_irq(irq); spin_lock_irqsave(&ioapic_lock, flags); io_apic_write(ioapic, 0x11+2*pin, *(((int *)&entry)+1)); io_apic_write(ioapic, 0x10+2*pin, *(((int *)&entry)+0)); set_native_irq_info(use_pci_vector() ? entry.vector : irq, TARGET_CPUS); spin_unlock_irqrestore(&ioapic_lock, flags); return 0;}#endif /* CONFIG_ACPI *//* * This function currently is only a helper for the i386 smp boot process where * we need to reprogram the ioredtbls to cater for the cpus which have come online * so mask in all cases should simply be TARGET_CPUS */#ifdef CONFIG_SMPvoid __init setup_ioapic_dest(void){ int pin, ioapic, irq, irq_entry; if (skip_ioapic_setup == 1) return; for (ioapic = 0; ioapic < nr_ioapics; ioapic++) { for (pin = 0; pin < nr_ioapic_registers[ioapic]; pin++) { irq_entry = find_irq_entry(ioapic, pin, mp_INT); if (irq_entry == -1) continue; irq = pin_2_irq(irq_entry, ioapic, pin); set_ioapic_affinity_irq(irq, TARGET_CPUS); } }}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -