--- //depot/vendor/freebsd/src/sys/amd64/amd64/intr_machdep.c 2006/07/12 21:25:59 +++ //depot/user/jhb/acpipci/amd64/amd64/intr_machdep.c 2006/09/26 16:55:45 @@ -37,6 +37,7 @@ * that source. */ +#include "opt_atpic.h" #include "opt_ddb.h" #include @@ -62,6 +63,7 @@ static int intrcnt_index; static struct intsrc *interrupt_sources[NUM_IO_INTS]; static struct mtx intr_table_lock; +static STAILQ_HEAD(, pic) pics; #ifdef SMP static int assign_cpu; @@ -70,10 +72,45 @@ #endif static void intr_init(void *__dummy); +static int intr_pic_registered(struct pic *pic); static void intrcnt_setname(const char *name, int index); static void intrcnt_updatename(struct intsrc *is); static void intrcnt_register(struct intsrc *is); +static int +intr_pic_registered(struct pic *pic) +{ + struct pic *p; + + STAILQ_FOREACH(p, &pics, pics) { + if (p == pic) + return (1); + } + return (0); +} + +/* + * Register a new interrupt controller (PIC). This is to support suspend + * and resume where we suspend/resume controllers rather than individual + * sources. This also allows controllers with no active sources (such as + * 8259As in a system using the APICs) to participate in suspend and resume. + */ +int +intr_pic_register(struct pic *pic) +{ + int error; + + mtx_lock_spin(&intr_table_lock); + if (intr_pic_registered(pic)) + error = EBUSY; + else { + STAILQ_INSERT_TAIL(&pics, pic, pics); + error = 0; + } + mtx_unlock_spin(&intr_table_lock); + return (error); +} + /* * Register a new interrupt source with the global interrupt system. * The global interrupts need to be disabled when this function is @@ -84,6 +121,7 @@ { int error, vector; + KASSERT(intr_pic_registered(isrc->is_pic), ("unregistered PIC")); vector = isrc->is_pic->pic_vector(isrc); if (interrupt_sources[vector] != NULL) return (EEXIST); @@ -255,26 +293,29 @@ void intr_resume(void) { - struct intsrc **isrc; - int i; + struct pic *pic; +#ifndef DEV_ATPIC + atpic_reset(); +#endif mtx_lock_spin(&intr_table_lock); - for (i = 0, isrc = interrupt_sources; i < NUM_IO_INTS; i++, isrc++) - if (*isrc != NULL && (*isrc)->is_pic->pic_resume != NULL) - (*isrc)->is_pic->pic_resume(*isrc); + STAILQ_FOREACH(pic, &pics, pics) { + if (pic->pic_resume != NULL) + pic->pic_resume(pic); + } mtx_unlock_spin(&intr_table_lock); } void intr_suspend(void) { - struct intsrc **isrc; - int i; + struct pic *pic; mtx_lock_spin(&intr_table_lock); - for (i = 0, isrc = interrupt_sources; i < NUM_IO_INTS; i++, isrc++) - if (*isrc != NULL && (*isrc)->is_pic->pic_suspend != NULL) - (*isrc)->is_pic->pic_suspend(*isrc); + STAILQ_FOREACH(pic, &pics, pics) { + if (pic->pic_suspend != NULL) + pic->pic_suspend(pic); + } mtx_unlock_spin(&intr_table_lock); } @@ -327,10 +368,33 @@ intrcnt_setname("???", 0); intrcnt_index = 1; + STAILQ_INIT(&pics); mtx_init(&intr_table_lock, "intr table", NULL, MTX_SPIN); } SYSINIT(intr_init, SI_SUB_INTR, SI_ORDER_FIRST, intr_init, NULL) +#ifndef DEV_ATPIC +/* Initialize the two 8259A's to a known-good shutdown state. */ +void +atpic_reset(void) +{ + + outb(IO_ICU1, ICW1_RESET | ICW1_IC4); + outb(IO_ICU1 + ICU_IMR_OFFSET, IDT_IO_INTS); + outb(IO_ICU1 + ICU_IMR_OFFSET, 1 << 2); + outb(IO_ICU1 + ICU_IMR_OFFSET, ICW4_8086); + outb(IO_ICU1 + ICU_IMR_OFFSET, 0xff); + outb(IO_ICU1, OCW3_SEL | OCW3_RR); + + outb(IO_ICU2, ICW1_RESET | ICW1_IC4); + outb(IO_ICU2 + ICU_IMR_OFFSET, IDT_IO_INTS + 8); + outb(IO_ICU2 + ICU_IMR_OFFSET, 2); + outb(IO_ICU2 + ICU_IMR_OFFSET, ICW4_8086); + outb(IO_ICU2 + ICU_IMR_OFFSET, 0xff); + outb(IO_ICU2, OCW3_SEL | OCW3_RR); +} +#endif + #ifdef DDB /* * Dump data about interrupt handlers --- //depot/vendor/freebsd/src/sys/amd64/amd64/io_apic.c 2006/04/05 20:45:58 +++ //depot/user/jhb/acpipci/amd64/amd64/io_apic.c 2006/09/26 16:55:45 @@ -727,6 +727,7 @@ io->io_intbase + io->io_numintr - 1); /* Register valid pins as interrupt sources. */ + intr_register_pic(&io->io_pic); for (i = 0, pin = io->io_pins; i < io->io_numintr; i++, pin++) if (pin->io_irq < NUM_IO_INTS) intr_register_source(&pin->io_intsrc); --- //depot/vendor/freebsd/src/sys/amd64/amd64/local_apic.c 2006/09/27 22:11:45 +++ //depot/user/jhb/acpipci/amd64/amd64/local_apic.c 2006/10/02 18:06:22 @@ -151,12 +151,15 @@ static u_long lapic_timer_divisor, lapic_timer_period, lapic_timer_hz; static void lapic_enable(void); +static void lapic_resume(struct pic *pic); static void lapic_timer_enable_intr(void); static void lapic_timer_oneshot(u_int count); static void lapic_timer_periodic(u_int count); static void lapic_timer_set_divisor(u_int divisor); static uint32_t lvt_mode(struct lapic *la, u_int pin, uint32_t value); +struct pic lapic_pic = { .pic_resume = lapic_resume }; + static uint32_t lvt_mode(struct lapic *la, u_int pin, uint32_t value) { @@ -277,7 +280,7 @@ } void -lapic_setup(void) +lapic_setup(int boot) { struct lapic *la; u_int32_t maxlvt; @@ -306,9 +309,13 @@ /* Program timer LVT and setup handler. */ lapic->lvt_timer = lvt_mode(la, LVT_TIMER, lapic->lvt_timer); - snprintf(buf, sizeof(buf), "cpu%d: timer", PCPU_GET(cpuid)); - intrcnt_add(buf, &la->la_timer_count); - if (PCPU_GET(cpuid) != 0) { + if (boot) { + snprintf(buf, sizeof(buf), "cpu%d: timer", PCPU_GET(cpuid)); + intrcnt_add(buf, &la->la_timer_count); + } + + /* We don't setup the timer during boot on the BSP until later. */ + if (!(boot && PCPU_GET(cpuid) == 0)) { KASSERT(lapic_timer_period != 0, ("lapic%u: zero divisor", lapic_id())); lapic_timer_set_divisor(lapic_timer_divisor); @@ -398,6 +405,14 @@ lapic->svr = value; } +/* Reset the local APIC on the BSP during resume. */ +static void +lapic_resume(struct pic *pic) +{ + + lapic_setup(0); +} + int lapic_id(void) { @@ -983,7 +998,8 @@ * Finish setting up the local APIC on the BSP once we know how to * properly program the LINT pins. */ - lapic_setup(); + lapic_setup(1); + intr_register_pic(&lapic_pic); if (bootverbose) lapic_dump("BSP"); } --- //depot/vendor/freebsd/src/sys/amd64/amd64/machdep.c 2006/10/02 15:47:34 +++ //depot/user/jhb/acpipci/amd64/amd64/machdep.c 2006/10/02 18:06:22 @@ -1212,19 +1212,7 @@ atpic_startup(); #else /* Reset and mask the atpics and leave them shut down. */ - outb(IO_ICU1, ICW1_RESET | ICW1_IC4); - outb(IO_ICU1 + ICU_IMR_OFFSET, IDT_IO_INTS); - outb(IO_ICU1 + ICU_IMR_OFFSET, 1 << 2); - outb(IO_ICU1 + ICU_IMR_OFFSET, ICW4_8086); - outb(IO_ICU1 + ICU_IMR_OFFSET, 0xff); - outb(IO_ICU1, OCW3_SEL | OCW3_RR); - - outb(IO_ICU2, ICW1_RESET | ICW1_IC4); - outb(IO_ICU2 + ICU_IMR_OFFSET, IDT_IO_INTS + 8); - outb(IO_ICU2 + ICU_IMR_OFFSET, 2); - outb(IO_ICU2 + ICU_IMR_OFFSET, ICW4_8086); - outb(IO_ICU2 + ICU_IMR_OFFSET, 0xff); - outb(IO_ICU2, OCW3_SEL | OCW3_RR); + atpic_reset(); /* * Point the ICU spurious interrupt vectors at the APIC spurious --- //depot/vendor/freebsd/src/sys/amd64/amd64/mp_machdep.c 2006/09/11 20:17:00 +++ //depot/user/jhb/acpipci/amd64/amd64/mp_machdep.c 2006/10/02 17:54:16 @@ -543,7 +543,7 @@ mtx_lock_spin(&ap_boot_mtx); /* Init local apic for irq's */ - lapic_setup(); + lapic_setup(1); /* Set memory range attributes for this CPU to match the BSP */ mem_range_AP_init(); --- //depot/vendor/freebsd/src/sys/amd64/include/apicvar.h 2006/05/01 21:38:04 +++ //depot/user/jhb/acpipci/amd64/include/apicvar.h 2006/10/02 17:54:16 @@ -211,7 +211,7 @@ int lapic_set_lvt_triggermode(u_int apic_id, u_int lvt, enum intr_trigger trigger); void lapic_set_tpr(u_int vector); -void lapic_setup(void); +void lapic_setup(int boot); int lapic_setup_clock(void); #endif /* !LOCORE */ --- //depot/vendor/freebsd/src/sys/amd64/include/intr_machdep.h 2006/02/28 22:25:18 +++ //depot/user/jhb/acpipci/amd64/include/intr_machdep.h 2006/09/26 16:55:45 @@ -81,11 +81,12 @@ void (*pic_enable_intr)(struct intsrc *); int (*pic_vector)(struct intsrc *); int (*pic_source_pending)(struct intsrc *); - void (*pic_suspend)(struct intsrc *); - void (*pic_resume)(struct intsrc *); + void (*pic_suspend)(struct pic *); + void (*pic_resume)(struct pic *); int (*pic_config_intr)(struct intsrc *, enum intr_trigger, enum intr_polarity); void (*pic_assign_cpu)(struct intsrc *, u_int apic_id); + STAILQ_ENTRY(pic) pics; }; /* Flags for pic_disable_source() */ @@ -114,6 +115,9 @@ extern struct mtx icu_lock; extern int elcr_found; +#ifndef DEV_ATPIC +void atpic_reset(void); +#endif /* XXX: The elcr_* prototypes probably belong somewhere else. */ int elcr_probe(void); enum intr_trigger elcr_read_trigger(u_int irq); @@ -130,6 +134,7 @@ enum intr_polarity pol); void intr_execute_handlers(struct intsrc *isrc, struct trapframe *frame); struct intsrc *intr_lookup_source(int vector); +int intr_register_pic(struct pic *pic); int intr_register_source(struct intsrc *isrc); int intr_remove_handler(void *cookie); void intr_resume(void); --- //depot/vendor/freebsd/src/sys/amd64/isa/atpic.c 2006/02/28 22:25:18 +++ //depot/user/jhb/acpipci/amd64/isa/atpic.c 2006/09/26 16:55:45 @@ -465,6 +465,14 @@ int i; /* + * Register our PICs, even if we aren't going to use any of their + * pins so that they are suspended and resumed. + */ + if (intr_register_pic(&atpics[0].at_pic) != 0 || + intr_register_pic(&atpics[1].at_pic) != 0) + panic("Unable to register ATPICs"); + + /* * If any of the ISA IRQs have an interrupt source already, then * assume that the APICs are being used and don't register any * of our interrupt sources. This makes sure we don't accidentally --- //depot/vendor/freebsd/src/sys/i386/i386/intr_machdep.c 2006/07/12 21:25:59 +++ //depot/user/jhb/acpipci/i386/i386/intr_machdep.c 2006/09/14 20:59:52 @@ -62,6 +62,7 @@ static int intrcnt_index; static struct intsrc *interrupt_sources[NUM_IO_INTS]; static struct mtx intr_table_lock; +static STAILQ_HEAD(, pic) pics; #ifdef SMP static int assign_cpu; @@ -70,10 +71,45 @@ #endif static void intr_init(void *__dummy); +static int intr_pic_registered(struct pic *pic); static void intrcnt_setname(const char *name, int index); static void intrcnt_updatename(struct intsrc *is); static void intrcnt_register(struct intsrc *is); +static int +intr_pic_registered(struct pic *pic) +{ + struct pic *p; + + STAILQ_FOREACH(p, &pics, pics) { + if (p == pic) + return (1); + } + return (0); +} + +/* + * Register a new interrupt controller (PIC). This is to support suspend + * and resume where we suspend/resume controllers rather than individual + * sources. This also allows controllers with no active sources (such as + * 8259As in a system using the APICs) to participate in suspend and resume. + */ +int +intr_register_pic(struct pic *pic) +{ + int error; + + mtx_lock_spin(&intr_table_lock); + if (intr_pic_registered(pic)) + error = EBUSY; + else { + STAILQ_INSERT_TAIL(&pics, pic, pics); + error = 0; + } + mtx_unlock_spin(&intr_table_lock); + return (error); +} + /* * Register a new interrupt source with the global interrupt system. * The global interrupts need to be disabled when this function is @@ -84,6 +120,7 @@ { int error, vector; + KASSERT(intr_pic_registered(isrc->is_pic), ("unregistered PIC")); vector = isrc->is_pic->pic_vector(isrc); if (interrupt_sources[vector] != NULL) return (EEXIST); @@ -255,26 +292,26 @@ void intr_resume(void) { - struct intsrc **isrc; - int i; + struct pic *pic; mtx_lock_spin(&intr_table_lock); - for (i = 0, isrc = interrupt_sources; i < NUM_IO_INTS; i++, isrc++) - if (*isrc != NULL && (*isrc)->is_pic->pic_resume != NULL) - (*isrc)->is_pic->pic_resume(*isrc); + STAILQ_FOREACH(pic, &pics, pics) { + if (pic->pic_resume != NULL) + pic->pic_resume(pic); + } mtx_unlock_spin(&intr_table_lock); } void intr_suspend(void) { - struct intsrc **isrc; - int i; + struct pic *pic; mtx_lock_spin(&intr_table_lock); - for (i = 0, isrc = interrupt_sources; i < NUM_IO_INTS; i++, isrc++) - if (*isrc != NULL && (*isrc)->is_pic->pic_suspend != NULL) - (*isrc)->is_pic->pic_suspend(*isrc); + STAILQ_FOREACH(pic, &pics, pics) { + if (pic->pic_suspend != NULL) + pic->pic_suspend(pic); + } mtx_unlock_spin(&intr_table_lock); } @@ -327,6 +364,7 @@ intrcnt_setname("???", 0); intrcnt_index = 1; + STAILQ_INIT(&pics); mtx_init(&intr_table_lock, "intr table", NULL, MTX_SPIN); } SYSINIT(intr_init, SI_SUB_INTR, SI_ORDER_FIRST, intr_init, NULL) --- //depot/vendor/freebsd/src/sys/i386/i386/io_apic.c 2006/04/05 20:45:58 +++ //depot/user/jhb/acpipci/i386/i386/io_apic.c 2006/09/14 20:59:52 @@ -114,8 +114,8 @@ static int ioapic_source_pending(struct intsrc *isrc); static int ioapic_config_intr(struct intsrc *isrc, enum intr_trigger trig, enum intr_polarity pol); -static void ioapic_suspend(struct intsrc *isrc); -static void ioapic_resume(struct intsrc *isrc); +static void ioapic_suspend(struct pic *pic); +static void ioapic_resume(struct pic *pic); static void ioapic_assign_cpu(struct intsrc *isrc, u_int apic_id); static void ioapic_program_intpin(struct ioapic_intsrc *intpin); @@ -418,17 +418,20 @@ } static void -ioapic_suspend(struct intsrc *isrc) +ioapic_suspend(struct pic *pic) { TODO; } static void -ioapic_resume(struct intsrc *isrc) +ioapic_resume(struct pic *pic) { + struct ioapic *io = (struct ioapic *)pic; + int i; - ioapic_program_intpin((struct ioapic_intsrc *)isrc); + for (i = 0; i < io->io_numintr; i++) + ioapic_program_intpin(&io->io_pins[i]); } /* @@ -726,6 +729,7 @@ io->io_intbase + io->io_numintr - 1); /* Register valid pins as interrupt sources. */ + intr_register_pic(&io->io_pic); for (i = 0, pin = io->io_pins; i < io->io_numintr; i++, pin++) if (pin->io_irq < NUM_IO_INTS) intr_register_source(&pin->io_intsrc); --- //depot/vendor/freebsd/src/sys/i386/i386/local_apic.c 2006/09/27 22:11:45 +++ //depot/user/jhb/acpipci/i386/i386/local_apic.c 2006/10/02 18:06:22 @@ -151,12 +151,15 @@ static u_long lapic_timer_divisor, lapic_timer_period, lapic_timer_hz; static void lapic_enable(void); +static void lapic_resume(struct pic *pic); static void lapic_timer_enable_intr(void); static void lapic_timer_oneshot(u_int count); static void lapic_timer_periodic(u_int count); static void lapic_timer_set_divisor(u_int divisor); static uint32_t lvt_mode(struct lapic *la, u_int pin, uint32_t value); +struct pic lapic_pic = { .pic_resume = lapic_resume }; + static uint32_t lvt_mode(struct lapic *la, u_int pin, uint32_t value) { @@ -279,7 +282,7 @@ } void -lapic_setup(void) +lapic_setup(int boot) { struct lapic *la; u_int32_t maxlvt; @@ -308,9 +311,13 @@ /* Program timer LVT and setup handler. */ lapic->lvt_timer = lvt_mode(la, LVT_TIMER, lapic->lvt_timer); - snprintf(buf, sizeof(buf), "cpu%d: timer", PCPU_GET(cpuid)); - intrcnt_add(buf, &la->la_timer_count); - if (PCPU_GET(cpuid) != 0) { + if (boot) { + snprintf(buf, sizeof(buf), "cpu%d: timer", PCPU_GET(cpuid)); + intrcnt_add(buf, &la->la_timer_count); + } + + /* We don't setup the timer during boot on the BSP until later. */ + if (!(boot && PCPU_GET(cpuid) == 0)) { KASSERT(lapic_timer_period != 0, ("lapic%u: zero divisor", lapic_id())); lapic_timer_set_divisor(lapic_timer_divisor); @@ -400,6 +407,14 @@ lapic->svr = value; } +/* Reset the local APIC on the BSP during resume. */ +static void +lapic_resume(struct pic *pic) +{ + + lapic_setup(0); +} + int lapic_id(void) { @@ -986,7 +1001,8 @@ * Finish setting up the local APIC on the BSP once we know how to * properly program the LINT pins. */ - lapic_setup(); + lapic_setup(1); + intr_register_pic(&lapic_pic); if (bootverbose) lapic_dump("BSP"); } --- //depot/vendor/freebsd/src/sys/i386/i386/mp_machdep.c 2006/09/11 20:11:45 +++ //depot/user/jhb/acpipci/i386/i386/mp_machdep.c 2006/10/02 17:54:16 @@ -594,7 +594,7 @@ mtx_lock_spin(&ap_boot_mtx); /* Init local apic for irq's */ - lapic_setup(); + lapic_setup(1); /* Set memory range attributes for this CPU to match the BSP */ mem_range_AP_init(); --- //depot/vendor/freebsd/src/sys/i386/include/apicvar.h 2006/05/01 21:38:04 +++ //depot/user/jhb/acpipci/i386/include/apicvar.h 2006/10/02 17:54:16 @@ -210,7 +210,7 @@ int lapic_set_lvt_triggermode(u_int apic_id, u_int lvt, enum intr_trigger trigger); void lapic_set_tpr(u_int vector); -void lapic_setup(void); +void lapic_setup(int boot); int lapic_setup_clock(void); #endif /* !LOCORE */ --- //depot/vendor/freebsd/src/sys/i386/include/intr_machdep.h 2006/02/28 22:25:18 +++ //depot/user/jhb/acpipci/i386/include/intr_machdep.h 2006/08/15 03:29:09 @@ -81,11 +81,12 @@ void (*pic_enable_intr)(struct intsrc *); int (*pic_vector)(struct intsrc *); int (*pic_source_pending)(struct intsrc *); - void (*pic_suspend)(struct intsrc *); - void (*pic_resume)(struct intsrc *); + void (*pic_suspend)(struct pic *); + void (*pic_resume)(struct pic *); int (*pic_config_intr)(struct intsrc *, enum intr_trigger, enum intr_polarity); void (*pic_assign_cpu)(struct intsrc *, u_int apic_id); + STAILQ_ENTRY(pic) pics; }; /* Flags for pic_disable_source() */ @@ -130,6 +131,7 @@ enum intr_polarity pol); void intr_execute_handlers(struct intsrc *isrc, struct trapframe *frame); struct intsrc *intr_lookup_source(int vector); +int intr_register_pic(struct pic *pic); int intr_register_source(struct intsrc *isrc); int intr_remove_handler(void *cookie); void intr_resume(void); --- //depot/vendor/freebsd/src/sys/i386/isa/atpic.c 2006/02/28 22:25:18 +++ //depot/user/jhb/acpipci/i386/isa/atpic.c 2006/09/14 20:59:52 @@ -156,7 +156,7 @@ static void atpic_eoi_slave(struct intsrc *isrc); static void atpic_enable_intr(struct intsrc *isrc); static int atpic_vector(struct intsrc *isrc); -static void atpic_resume(struct intsrc *isrc); +static void atpic_resume(struct pic *pic); static int atpic_source_pending(struct intsrc *isrc); static int atpic_config_intr(struct intsrc *isrc, enum intr_trigger trig, enum intr_polarity pol); @@ -303,18 +303,15 @@ } static void -atpic_resume(struct intsrc *isrc) +atpic_resume(struct pic *pic) { - struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc; - struct atpic *ap = (struct atpic *)isrc->is_pic; + struct atpic *ap = (struct atpic *)pic; - if (ai->at_irq == 0) { - i8259_init(ap, ap == &atpics[SLAVE]); + i8259_init(ap, ap == &atpics[SLAVE]); #ifndef PC98 - if (ap == &atpics[SLAVE] && elcr_found) - elcr_resume(); + if (ap == &atpics[SLAVE] && elcr_found) + elcr_resume(); #endif - } } static int @@ -529,6 +526,14 @@ int i; /* + * Register our PICs, even if we aren't going to use any of their + * pins so that they are suspended and resumed. + */ + if (intr_register_pic(&atpics[0].at_pic) != 0 || + intr_register_pic(&atpics[1].at_pic) != 0) + panic("Unable to register ATPICs"); + + /* * If any of the ISA IRQs have an interrupt source already, then * assume that the APICs are being used and don't register any * of our interrupt sources. This makes sure we don't accidentally