Permalink
Browse files

8254: Setup interrupt properly for new I/O APIC code

  • Loading branch information...
1 parent 16dac13 commit 6b809ec73a4658e71239916f8276dc080cc3992d Sepherosa Ziehau committed Mar 21, 2011
@@ -56,6 +56,7 @@
#include <sys/thread2.h>
+#include <machine_base/icu/icu_var.h>
#include <machine_base/apic/ioapic_abi.h>
#include <machine_base/apic/ioapic_ipl.h>
@@ -495,6 +496,8 @@ struct machintr_abi MachIntrABI_IOAPIC = {
.intr_config = ioapic_intr_config
};
+static int ioapic_abi_extint_irq = -1;
+
static int
ioapic_setvar(int varid, const void *buf)
{
@@ -847,4 +850,66 @@ ioapic_intr_config(int irq, enum intr_trigger trig, enum intr_polarity pola)
imen_unlock();
}
+int
+ioapic_abi_extint_irqmap(int irq)
+{
+ struct apic_intmapinfo *info;
+ struct ioapic_irqmap *map;
+ void *ioaddr;
+ int pin, error, vec;
+
+ vec = IDT_OFFSET + irq;
+
+ if (ioapic_abi_extint_irq == irq)
+ return 0;
+ else if (ioapic_abi_extint_irq >= 0)
+ return EEXIST;
+
+ error = icu_ioapic_extint(irq, vec);
+ if (error)
+ return error;
+
+ map = &ioapic_irqmaps[irq];
+
+ KKASSERT(map->im_type == IOAPIC_IMT_RESERVED ||
+ map->im_type == IOAPIC_IMT_LINE);
+ if (map->im_type == IOAPIC_IMT_LINE) {
+ if (map->im_flags & IOAPIC_IMF_CONF)
+ return EEXIST;
+ }
+ ioapic_abi_extint_irq = irq;
+
+ map->im_type = IOAPIC_IMT_LINE;
+ map->im_trig = INTR_TRIGGER_EDGE;
+ map->im_pola = INTR_POLARITY_HIGH;
+ map->im_flags = IOAPIC_IMF_CONF;
+
+ map->im_gsi = ioapic_extpin_gsi();
+ KKASSERT(map->im_gsi >= 0);
+
+ if (bootverbose) {
+ kprintf("IOAPIC: irq %d -> extint gsi %d E\n", irq,
+ map->im_gsi);
+ }
+
+ pin = ioapic_gsi_pin(map->im_gsi);
+ ioaddr = ioapic_gsi_ioaddr(map->im_gsi);
+
+ info = &int_to_apicintpin[irq];
+
+ imen_lock();
+
+ info->ioapic = 0; /* XXX unused */
+ info->int_pin = pin;
+ info->apic_address = ioaddr;
+ info->redirindex = IOAPIC_REDTBL + (2 * pin);
+ info->flags = IOAPIC_IM_FLAG_MASKED;
+
+ ioapic_extpin_setup(ioaddr, pin, vec);
+
+ imen_unlock();
+
+ return 0;
+}
+
#endif /* SMP */
@@ -49,6 +49,7 @@
extern struct machintr_abi MachIntrABI_IOAPIC;
+int ioapic_abi_extint_irqmap(int);
void ioapic_abi_set_irqmap(int, int, enum intr_trigger, enum intr_polarity);
void ioapic_abi_fixup_irqmap(void);
@@ -1398,6 +1398,12 @@ ioapic_extpin_setup(void *addr, int pin, int vec)
INTR_TRIGGER_CONFORM, INTR_POLARITY_CONFORM, IOART_DELEXINT);
}
+int
+ioapic_extpin_gsi(void)
+{
+ return 0;
+}
+
void
ioapic_pin_setup(void *addr, int pin, int vec,
enum intr_trigger trig, enum intr_polarity pola)
@@ -153,3 +153,30 @@ icu_irq_pending(void)
irr2 = inb(IO_ICU2);
return ((irr2 << 8) | irr1);
}
+
+int
+icu_ioapic_extint(int irq, int vec)
+{
+ uint8_t mask;
+
+ /*
+ * Only first 8 interrupt is supported.
+ * Don't allow setup for the slave link.
+ */
+ if (irq >= 8 || irq == 2)
+ return EOPNOTSUPP;
+
+ mask = ~(1 << irq);
+
+ /*
+ * Re-initialize master 8259:
+ * reset; prog 4 bytes, single ICU, edge triggered
+ */
+ outb(IO_ICU1, 0x13);
+ outb(IO_ICU1 + 1, vec); /* start vector (unused) */
+ outb(IO_ICU1 + 1, 0x00); /* ignore slave */
+ outb(IO_ICU1 + 1, 0x03); /* auto EOI, 8086 */
+ outb(IO_ICU1 + 1, mask);
+
+ return 0;
+}
@@ -43,4 +43,6 @@ void icu_reinit(void);
intrmask_t icu_irq_pending(void);
+int icu_ioapic_extint(int, int);
+
#endif /* !_ARCH_ICU_ICU_VAR_H_ */
@@ -173,6 +173,7 @@ int ioapic_gsi_pin(int);
void ioapic_pin_setup(void *, int, int,
enum intr_trigger, enum intr_polarity);
void ioapic_extpin_setup(void *, int, int);
+int ioapic_extpin_gsi(void);
int ioapic_gsi(int, int);
extern int apic_io_enable;
@@ -82,6 +82,9 @@
#include <machine/smp.h>
#include <machine/specialreg.h>
+#ifdef SMP
+#include <machine_base/apic/ioapic_abi.h>
+#endif
#include <machine_base/icu/icu.h>
#include <bus/isa/isa.h>
#include <bus/isa/rtc.h>
@@ -1001,6 +1004,40 @@ resettodr(void)
crit_exit();
}
+#ifdef SMP
+
+static int
+i8254_ioapic_trial(int irq, struct cputimer_intr *cti)
+{
+ sysclock_t base;
+ long lastcnt;
+
+ /*
+ * Following code assumes the 8254 is the cpu timer,
+ * so make sure it is.
+ */
+ KKASSERT(sys_cputimer == &i8254_cputimer);
+ KKASSERT(cti == &i8254_cputimer_intr);
+
+ lastcnt = get_interrupt_counter(irq);
+
+ /*
+ * Force an 8254 Timer0 interrupt and wait 1/100s for
+ * it to happen, then see if we got it.
+ */
+ kprintf("IOAPIC: testing 8254 interrupt delivery\n");
+
+ i8254_intr_reload(cti, 2);
+ base = sys_cputimer->count();
+ while (sys_cputimer->count() - base < sys_cputimer->freq / 100)
+ ; /* nothing */
+
+ if (get_interrupt_counter(irq) - lastcnt == 0)
+ return ENOENT;
+ return 0;
+}
+
+#endif /* SMP */
/*
* Start both clocks running. DragonFly note: the stat clock is no longer
@@ -1013,16 +1050,13 @@ i8254_intr_initclock(struct cputimer_intr *cti, boolean_t selected)
#ifdef SMP /* APIC-IO */
int apic_8254_trial = 0;
void *clkdesc = NULL;
- int irq = 0;
+ int irq = 0, mixed_mode = 0, error;
#endif
callout_init(&sysbeepstop_ch);
- if (!selected && i8254_intr_disable) {
- i8254_nointr = 1; /* don't try to register again */
- cputimer_intr_deregister(cti);
- return;
- }
+ if (!selected && i8254_intr_disable)
+ goto nointr;
/*
* The stat interrupt mask is different without the
@@ -1057,6 +1091,37 @@ if (apic_io_enable) {
INTR_NOPOLL | INTR_MPSAFE |
INTR_NOENTROPY);
machintr_intren(irq);
+ } else {
+ irq = ioapic_abi_find_irq(0, INTR_TRIGGER_EDGE,
+ INTR_POLARITY_HIGH);
+ if (irq < 0) {
+mixed_mode_setup:
+ error = ioapic_abi_extint_irqmap(0);
+ if (!error) {
+ irq = ioapic_abi_find_irq(0, INTR_TRIGGER_EDGE,
+ INTR_POLARITY_HIGH);
+ if (irq < 0)
+ error = ENOENT;
+ }
+
+ if (error) {
+ if (!selected) {
+ kprintf("IOAPIC: setup mixed mode for "
+ "irq 0 failed: %d\n", error);
+ goto nointr;
+ } else {
+ panic("IOAPIC: setup mixed mode for "
+ "irq 0 failed: %d\n", error);
+ }
+ }
+ mixed_mode = 1;
+ }
+ clkdesc = register_int(irq, clkintr, NULL, "clk",
+ NULL,
+ INTR_EXCL | INTR_CLOCK |
+ INTR_NOPOLL | INTR_MPSAFE |
+ INTR_NOENTROPY);
+ machintr_intren(irq);
}
} else {
#endif
@@ -1074,7 +1139,8 @@ if (apic_io_enable) {
writertc(RTC_STATUSB, RTCSB_24HR);
#ifdef SMP /* APIC-IO */
-if (apic_io_enable && ioapic_use_old) {
+if (apic_io_enable) {
+if (ioapic_use_old) {
if (apic_8254_trial) {
sysclock_t base;
long lastcnt;
@@ -1142,8 +1208,34 @@ if (apic_io_enable && ioapic_use_old) {
kprintf("APIC_IO: "
"routing 8254 via 8259 and IOAPIC #0 intpin 0\n");
}
+} else { /* !ioapic_use_old */
+ error = i8254_ioapic_trial(irq, cti);
+ if (error) {
+ if (mixed_mode) {
+ if (!selected) {
+ kprintf("IOAPIC: mixed mode for irq %d "
+ "trial failed: %d\n", irq, error);
+ goto nointr;
+ } else {
+ panic("IOAPIC: mixed mode for irq %d "
+ "trial failed: %d\n", irq, error);
+ }
+ } else {
+ kprintf("IOAPIC: warning 8254 is not connected "
+ "to the correct pin, try mixed mode\n");
+ machintr_intrdis(irq);
+ unregister_int(clkdesc);
+ goto mixed_mode_setup;
+ }
+ }
+} /* ioapic_use_old */
}
#endif
+ return;
+
+nointr:
+ i8254_nointr = 1; /* don't try to register again */
+ cputimer_intr_deregister(cti);
}
#ifdef SMP /* APIC-IO */
@@ -56,6 +56,7 @@
#include <sys/thread2.h>
+#include <machine_base/icu/icu_var.h>
#include <machine_base/apic/ioapic_abi.h>
#include <machine_base/apic/ioapic_ipl.h>
@@ -495,6 +496,8 @@ struct machintr_abi MachIntrABI_IOAPIC = {
.intr_config = ioapic_intr_config
};
+static int ioapic_abi_extint_irq = -1;
+
static int
ioapic_setvar(int varid, const void *buf)
{
@@ -840,4 +843,66 @@ ioapic_intr_config(int irq, enum intr_trigger trig, enum intr_polarity pola)
imen_unlock();
}
+int
+ioapic_abi_extint_irqmap(int irq)
+{
+ struct apic_intmapinfo *info;
+ struct ioapic_irqmap *map;
+ void *ioaddr;
+ int pin, error, vec;
+
+ vec = IDT_OFFSET + irq;
+
+ if (ioapic_abi_extint_irq == irq)
+ return 0;
+ else if (ioapic_abi_extint_irq >= 0)
+ return EEXIST;
+
+ error = icu_ioapic_extint(irq, vec);
+ if (error)
+ return error;
+
+ map = &ioapic_irqmaps[irq];
+
+ KKASSERT(map->im_type == IOAPIC_IMT_RESERVED ||
+ map->im_type == IOAPIC_IMT_LINE);
+ if (map->im_type == IOAPIC_IMT_LINE) {
+ if (map->im_flags & IOAPIC_IMF_CONF)
+ return EEXIST;
+ }
+ ioapic_abi_extint_irq = irq;
+
+ map->im_type = IOAPIC_IMT_LINE;
+ map->im_trig = INTR_TRIGGER_EDGE;
+ map->im_pola = INTR_POLARITY_HIGH;
+ map->im_flags = IOAPIC_IMF_CONF;
+
+ map->im_gsi = ioapic_extpin_gsi();
+ KKASSERT(map->im_gsi >= 0);
+
+ if (bootverbose) {
+ kprintf("IOAPIC: irq %d -> extint gsi %d E\n", irq,
+ map->im_gsi);
+ }
+
+ pin = ioapic_gsi_pin(map->im_gsi);
+ ioaddr = ioapic_gsi_ioaddr(map->im_gsi);
+
+ info = &int_to_apicintpin[irq];
+
+ imen_lock();
+
+ info->ioapic = 0; /* XXX unused */
+ info->int_pin = pin;
+ info->apic_address = ioaddr;
+ info->redirindex = IOAPIC_REDTBL + (2 * pin);
+ info->flags = IOAPIC_IM_FLAG_MASKED;
+
+ ioapic_extpin_setup(ioaddr, pin, vec);
+
+ imen_unlock();
+
+ return 0;
+}
+
#endif /* SMP */
@@ -49,6 +49,7 @@
extern struct machintr_abi MachIntrABI_IOAPIC;
+int ioapic_abi_extint_irqmap(int);
void ioapic_abi_set_irqmap(int, int, enum intr_trigger, enum intr_polarity);
void ioapic_abi_fixup_irqmap(void);
Oops, something went wrong.

0 comments on commit 6b809ec

Please sign in to comment.