📄 apic_64.c
字号:
/* * Local APIC handling, local APIC timers * * (c) 1999, 2000 Ingo Molnar <mingo@redhat.com> * * Fixes * Maciej W. Rozycki : Bits for genuine 82489DX APICs; * thanks to Eric Gilmore * and Rolf G. Tews * for testing these extensively. * Maciej W. Rozycki : Various updates and fixes. * Mikael Pettersson : Power Management for UP-APIC. * Pavel Machek and * Mikael Pettersson : PM converted to driver model. */#include <linux/init.h>#include <linux/mm.h>#include <linux/delay.h>#include <linux/bootmem.h>#include <linux/interrupt.h>#include <linux/mc146818rtc.h>#include <linux/kernel_stat.h>#include <linux/sysdev.h>#include <linux/module.h>#include <linux/ioport.h>#include <linux/clockchips.h>#include <asm/atomic.h>#include <asm/smp.h>#include <asm/mtrr.h>#include <asm/mpspec.h>#include <asm/pgalloc.h>#include <asm/mach_apic.h>#include <asm/nmi.h>#include <asm/idle.h>#include <asm/proto.h>#include <asm/timex.h>#include <asm/hpet.h>#include <asm/apic.h>int apic_verbosity;int disable_apic_timer __cpuinitdata;static int apic_calibrate_pmtmr __initdata;/* Local APIC timer works in C2? */int local_apic_timer_c2_ok;EXPORT_SYMBOL_GPL(local_apic_timer_c2_ok);static struct resource *ioapic_resources;static struct resource lapic_resource = { .name = "Local APIC", .flags = IORESOURCE_MEM | IORESOURCE_BUSY,};static unsigned int calibration_result;static int lapic_next_event(unsigned long delta, struct clock_event_device *evt);static void lapic_timer_setup(enum clock_event_mode mode, struct clock_event_device *evt);static void lapic_timer_broadcast(cpumask_t mask);static void __setup_APIC_LVTT(unsigned int clocks, int oneshot, int irqen);static struct clock_event_device lapic_clockevent = { .name = "lapic", .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_C3STOP | CLOCK_EVT_FEAT_DUMMY, .shift = 32, .set_mode = lapic_timer_setup, .set_next_event = lapic_next_event, .broadcast = lapic_timer_broadcast, .rating = 100, .irq = -1,};static DEFINE_PER_CPU(struct clock_event_device, lapic_events);static int lapic_next_event(unsigned long delta, struct clock_event_device *evt){ apic_write(APIC_TMICT, delta); return 0;}static void lapic_timer_setup(enum clock_event_mode mode, struct clock_event_device *evt){ unsigned long flags; unsigned int v; /* Lapic used as dummy for broadcast ? */ if (evt->features & CLOCK_EVT_FEAT_DUMMY) return; local_irq_save(flags); switch (mode) { case CLOCK_EVT_MODE_PERIODIC: case CLOCK_EVT_MODE_ONESHOT: __setup_APIC_LVTT(calibration_result, mode != CLOCK_EVT_MODE_PERIODIC, 1); break; case CLOCK_EVT_MODE_UNUSED: case CLOCK_EVT_MODE_SHUTDOWN: v = apic_read(APIC_LVTT); v |= (APIC_LVT_MASKED | LOCAL_TIMER_VECTOR); apic_write(APIC_LVTT, v); break; case CLOCK_EVT_MODE_RESUME: /* Nothing to do here */ break; } local_irq_restore(flags);}/* * Local APIC timer broadcast function */static void lapic_timer_broadcast(cpumask_t mask){#ifdef CONFIG_SMP send_IPI_mask(mask, LOCAL_TIMER_VECTOR);#endif}static void apic_pm_activate(void);void apic_wait_icr_idle(void){ while (apic_read(APIC_ICR) & APIC_ICR_BUSY) cpu_relax();}unsigned int safe_apic_wait_icr_idle(void){ unsigned int send_status; int timeout; timeout = 0; do { send_status = apic_read(APIC_ICR) & APIC_ICR_BUSY; if (!send_status) break; udelay(100); } while (timeout++ < 1000); return send_status;}void enable_NMI_through_LVT0 (void * dummy){ unsigned int v; /* unmask and set to NMI */ v = APIC_DM_NMI; apic_write(APIC_LVT0, v);}int get_maxlvt(void){ unsigned int v, maxlvt; v = apic_read(APIC_LVR); maxlvt = GET_APIC_MAXLVT(v); return maxlvt;}/* * 'what should we do if we get a hw irq event on an illegal vector'. * each architecture has to answer this themselves. */void ack_bad_irq(unsigned int irq){ printk("unexpected IRQ trap at vector %02x\n", irq); /* * Currently unexpected vectors happen only on SMP and APIC. * We _must_ ack these because every local APIC has only N * irq slots per priority level, and a 'hanging, unacked' IRQ * holds up an irq slot - in excessive cases (when multiple * unexpected vectors occur) that might lock up the APIC * completely. * But don't ack when the APIC is disabled. -AK */ if (!disable_apic) ack_APIC_irq();}void clear_local_APIC(void){ int maxlvt; unsigned int v; maxlvt = get_maxlvt(); /* * Masking an LVT entry can trigger a local APIC error * if the vector is zero. Mask LVTERR first to prevent this. */ if (maxlvt >= 3) { v = ERROR_APIC_VECTOR; /* any non-zero vector will do */ apic_write(APIC_LVTERR, v | APIC_LVT_MASKED); } /* * Careful: we have to set masks only first to deassert * any level-triggered sources. */ v = apic_read(APIC_LVTT); apic_write(APIC_LVTT, v | APIC_LVT_MASKED); v = apic_read(APIC_LVT0); apic_write(APIC_LVT0, v | APIC_LVT_MASKED); v = apic_read(APIC_LVT1); apic_write(APIC_LVT1, v | APIC_LVT_MASKED); if (maxlvt >= 4) { v = apic_read(APIC_LVTPC); apic_write(APIC_LVTPC, v | APIC_LVT_MASKED); } /* * Clean APIC state for other OSs: */ apic_write(APIC_LVTT, APIC_LVT_MASKED); apic_write(APIC_LVT0, APIC_LVT_MASKED); apic_write(APIC_LVT1, APIC_LVT_MASKED); if (maxlvt >= 3) apic_write(APIC_LVTERR, APIC_LVT_MASKED); if (maxlvt >= 4) apic_write(APIC_LVTPC, APIC_LVT_MASKED); apic_write(APIC_ESR, 0); apic_read(APIC_ESR);}void disconnect_bsp_APIC(int virt_wire_setup){ /* Go back to Virtual Wire compatibility mode */ unsigned long value; /* For the spurious interrupt use vector F, and enable it */ value = apic_read(APIC_SPIV); value &= ~APIC_VECTOR_MASK; value |= APIC_SPIV_APIC_ENABLED; value |= 0xf; apic_write(APIC_SPIV, value); if (!virt_wire_setup) { /* * For LVT0 make it edge triggered, active high, * external and enabled */ value = apic_read(APIC_LVT0); value &= ~(APIC_MODE_MASK | APIC_SEND_PENDING | APIC_INPUT_POLARITY | APIC_LVT_REMOTE_IRR | APIC_LVT_LEVEL_TRIGGER | APIC_LVT_MASKED ); value |= APIC_LVT_REMOTE_IRR | APIC_SEND_PENDING; value = SET_APIC_DELIVERY_MODE(value, APIC_MODE_EXTINT); apic_write(APIC_LVT0, value); } else { /* Disable LVT0 */ apic_write(APIC_LVT0, APIC_LVT_MASKED); } /* For LVT1 make it edge triggered, active high, nmi and enabled */ value = apic_read(APIC_LVT1); value &= ~(APIC_MODE_MASK | APIC_SEND_PENDING | APIC_INPUT_POLARITY | APIC_LVT_REMOTE_IRR | APIC_LVT_LEVEL_TRIGGER | APIC_LVT_MASKED); value |= APIC_LVT_REMOTE_IRR | APIC_SEND_PENDING; value = SET_APIC_DELIVERY_MODE(value, APIC_MODE_NMI); apic_write(APIC_LVT1, value);}void disable_local_APIC(void){ unsigned int value; clear_local_APIC(); /* * Disable APIC (implies clearing of registers * for 82489DX!). */ value = apic_read(APIC_SPIV); value &= ~APIC_SPIV_APIC_ENABLED; apic_write(APIC_SPIV, value);}void lapic_shutdown(void){ unsigned long flags; if (!cpu_has_apic) return; local_irq_save(flags); disable_local_APIC(); local_irq_restore(flags);}/* * This is to verify that we're looking at a real local APIC. * Check these against your board if the CPUs aren't getting * started for no apparent reason. */int __init verify_local_APIC(void){ unsigned int reg0, reg1; /* * The version register is read-only in a real APIC. */ reg0 = apic_read(APIC_LVR); apic_printk(APIC_DEBUG, "Getting VERSION: %x\n", reg0); apic_write(APIC_LVR, reg0 ^ APIC_LVR_MASK); reg1 = apic_read(APIC_LVR); apic_printk(APIC_DEBUG, "Getting VERSION: %x\n", reg1); /* * The two version reads above should print the same * numbers. If the second one is different, then we * poke at a non-APIC. */ if (reg1 != reg0) return 0; /* * Check if the version looks reasonably. */ reg1 = GET_APIC_VERSION(reg0); if (reg1 == 0x00 || reg1 == 0xff) return 0; reg1 = get_maxlvt(); if (reg1 < 0x02 || reg1 == 0xff) return 0; /* * The ID register is read/write in a real APIC. */ reg0 = apic_read(APIC_ID); apic_printk(APIC_DEBUG, "Getting ID: %x\n", reg0); apic_write(APIC_ID, reg0 ^ APIC_ID_MASK); reg1 = apic_read(APIC_ID); apic_printk(APIC_DEBUG, "Getting ID: %x\n", reg1); apic_write(APIC_ID, reg0); if (reg1 != (reg0 ^ APIC_ID_MASK)) return 0; /* * The next two are just to see if we have sane values. * They're only really relevant if we're in Virtual Wire * compatibility mode, but most boxes are anymore. */ reg0 = apic_read(APIC_LVT0); apic_printk(APIC_DEBUG,"Getting LVT0: %x\n", reg0); reg1 = apic_read(APIC_LVT1); apic_printk(APIC_DEBUG, "Getting LVT1: %x\n", reg1); return 1;}void __init sync_Arb_IDs(void){ /* Unsupported on P4 - see Intel Dev. Manual Vol. 3, Ch. 8.6.1 */ unsigned int ver = GET_APIC_VERSION(apic_read(APIC_LVR)); if (ver >= 0x14) /* P4 or higher */ return; /* * Wait for idle. */ apic_wait_icr_idle(); apic_printk(APIC_DEBUG, "Synchronizing Arb IDs.\n"); apic_write(APIC_ICR, APIC_DEST_ALLINC | APIC_INT_LEVELTRIG | APIC_DM_INIT);}/* * An initial setup of the virtual wire mode. */void __init init_bsp_APIC(void){ unsigned int value; /* * Don't do the setup now if we have a SMP BIOS as the * through-I/O-APIC virtual wire mode might be active. */ if (smp_found_config || !cpu_has_apic) return; value = apic_read(APIC_LVR); /* * Do not trust the local APIC being empty at bootup. */ clear_local_APIC(); /* * Enable APIC. */ value = apic_read(APIC_SPIV); value &= ~APIC_VECTOR_MASK; value |= APIC_SPIV_APIC_ENABLED; value |= APIC_SPIV_FOCUS_DISABLED; value |= SPURIOUS_APIC_VECTOR; apic_write(APIC_SPIV, value); /* * Set up the virtual wire mode. */ apic_write(APIC_LVT0, APIC_DM_EXTINT); value = APIC_DM_NMI; apic_write(APIC_LVT1, value);}void __cpuinit setup_local_APIC (void){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -