summaryrefslogtreecommitdiffstats
path: root/sys/i386
diff options
context:
space:
mode:
authorjhb <jhb@FreeBSD.org>2004-05-04 21:02:56 +0000
committerjhb <jhb@FreeBSD.org>2004-05-04 21:02:56 +0000
commit415131cd6db5461be3f495e59f9e00d283adb995 (patch)
tree525d4d97f918c6c9d8fa13659c24f043dd7d4a6d /sys/i386
parente077495344dba7e35611886ce89b689353105df3 (diff)
downloadFreeBSD-src-415131cd6db5461be3f495e59f9e00d283adb995.zip
FreeBSD-src-415131cd6db5461be3f495e59f9e00d283adb995.tar.gz
- Add a new pic method pic_config_intr() to set the trigger mode and
polarity for a specified IRQ. The intr_config_intr() function wraps this pic method hiding the IRQ to interrupt source lookup. - Add a config_intr() method to the atpic(4) driver that reconfigures the interrupt using the ELCR if possible and returns an error otherwise. - Add a config_intr() method to the apic(4) driver that just logs any requests that would change the existing programming under bootverbose. Currently, the only changes the apic(4) driver receives are due to bugs in the acpi(4) driver and its handling of link devices, hence the reason for such requests currently being ignored. - Have the nexus(4) driver on i386 implement the bus_config_intr() function by calling intr_config_intr().
Diffstat (limited to 'sys/i386')
-rw-r--r--sys/i386/i386/intr_machdep.c11
-rw-r--r--sys/i386/i386/io_apic.c40
-rw-r--r--sys/i386/i386/nexus.c10
-rw-r--r--sys/i386/include/intr_machdep.h4
-rw-r--r--sys/i386/isa/atpic.c61
5 files changed, 124 insertions, 2 deletions
diff --git a/sys/i386/i386/intr_machdep.c b/sys/i386/i386/intr_machdep.c
index d023a86..f7f51c0 100644
--- a/sys/i386/i386/intr_machdep.c
+++ b/sys/i386/i386/intr_machdep.c
@@ -138,6 +138,17 @@ intr_remove_handler(void *cookie)
return (error);
}
+int
+intr_config_intr(int vector, enum intr_trigger trig, enum intr_polarity pol)
+{
+ struct intsrc *isrc;
+
+ isrc = intr_lookup_source(vector);
+ if (isrc == NULL)
+ return (EINVAL);
+ return (isrc->is_pic->pic_config_intr(isrc, trig, pol));
+}
+
void
intr_execute_handlers(struct intsrc *isrc, struct intrframe *iframe)
{
diff --git a/sys/i386/i386/io_apic.c b/sys/i386/i386/io_apic.c
index b3e760e..272ebae 100644
--- a/sys/i386/i386/io_apic.c
+++ b/sys/i386/i386/io_apic.c
@@ -127,6 +127,8 @@ static void ioapic_eoi_source(struct intsrc *isrc);
static void ioapic_enable_intr(struct intsrc *isrc);
static int ioapic_vector(struct intsrc *isrc);
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_program_destination(struct ioapic_intsrc *intpin);
@@ -137,7 +139,8 @@ static void ioapic_setup_mixed_mode(struct ioapic_intsrc *intpin);
struct pic ioapic_template = { ioapic_enable_source, ioapic_disable_source,
ioapic_eoi_source, ioapic_enable_intr,
ioapic_vector, ioapic_source_pending,
- ioapic_suspend, ioapic_resume };
+ ioapic_suspend, ioapic_resume,
+ ioapic_config_intr };
static int next_ioapic_base, logical_clusters, current_cluster;
@@ -290,6 +293,41 @@ ioapic_source_pending(struct intsrc *isrc)
return (lapic_intr_pending(intpin->io_vector));
}
+static int
+ioapic_config_intr(struct intsrc *isrc, enum intr_trigger trig,
+ enum intr_polarity pol)
+{
+ struct ioapic_intsrc *intpin = (struct ioapic_intsrc *)isrc;
+ struct ioapic *io = (struct ioapic *)isrc->is_pic;
+
+ KASSERT(!(trig == INTR_TRIGGER_CONFORM || pol == INTR_POLARITY_CONFORM),
+ ("%s: Conforming trigger or polarity\n", __func__));
+
+ /*
+ * For now we ignore any requests but do output any changes that
+ * would be made to the console it bootverbose is enabled. The only
+ * known causes of these messages so far is a bug in acpi(4) that
+ * causes the ISA IRQs used for PCI interrupts in PIC mode to be
+ * set to level/low when they aren't being used. There are possibly
+ * legitimate requests, so at some point when the acpi(4) driver is
+ * fixed this code can be changed to actually change the intpin as
+ * requested.
+ */
+ if (!bootverbose)
+ return (0);
+ if (intpin->io_edgetrigger != (trig == INTR_TRIGGER_EDGE))
+ printf(
+ "ioapic%u: Request to change trigger for pin %u to %s ignored\n",
+ io->io_id, intpin->io_intpin, trig == INTR_TRIGGER_EDGE ?
+ "edge" : "level");
+ if (intpin->io_activehi != (pol == INTR_POLARITY_HIGH))
+ printf(
+ "ioapic%u: Request to change polarity for pin %u to %s ignored\n",
+ io->io_id, intpin->io_intpin, pol == INTR_POLARITY_HIGH ?
+ "high" : "low");
+ return (0);
+}
+
static void
ioapic_suspend(struct intsrc *isrc)
{
diff --git a/sys/i386/i386/nexus.c b/sys/i386/i386/nexus.c
index ecfd035..818da86 100644
--- a/sys/i386/i386/nexus.c
+++ b/sys/i386/i386/nexus.c
@@ -88,6 +88,8 @@ static device_t nexus_add_child(device_t bus, int order, const char *name,
int unit);
static struct resource *nexus_alloc_resource(device_t, device_t, int, int *,
u_long, u_long, u_long, u_int);
+static int nexus_config_intr(device_t, int, enum intr_trigger,
+ enum intr_polarity);
static int nexus_activate_resource(device_t, device_t, int, int,
struct resource *);
static int nexus_deactivate_resource(device_t, device_t, int, int,
@@ -120,6 +122,7 @@ static device_method_t nexus_methods[] = {
DEVMETHOD(bus_deactivate_resource, nexus_deactivate_resource),
DEVMETHOD(bus_setup_intr, nexus_setup_intr),
DEVMETHOD(bus_teardown_intr, nexus_teardown_intr),
+ DEVMETHOD(bus_config_intr, nexus_config_intr),
DEVMETHOD(bus_set_resource, nexus_set_resource),
DEVMETHOD(bus_get_resource, nexus_get_resource),
DEVMETHOD(bus_delete_resource, nexus_delete_resource),
@@ -491,6 +494,13 @@ nexus_teardown_intr(device_t dev, device_t child, struct resource *r, void *ih)
}
static int
+nexus_config_intr(device_t dev, int irq, enum intr_trigger trig,
+ enum intr_polarity pol)
+{
+ return (intr_config_intr(irq, trig, pol));
+}
+
+static int
nexus_set_resource(device_t dev, device_t child, int type, int rid, u_long start, u_long count)
{
struct nexus_device *ndev = DEVTONX(child);
diff --git a/sys/i386/include/intr_machdep.h b/sys/i386/include/intr_machdep.h
index 6794751..d8256c9 100644
--- a/sys/i386/include/intr_machdep.h
+++ b/sys/i386/include/intr_machdep.h
@@ -57,6 +57,8 @@ struct pic {
int (*pic_source_pending)(struct intsrc *);
void (*pic_suspend)(struct intsrc *);
void (*pic_resume)(struct intsrc *);
+ int (*pic_config_intr)(struct intsrc *, enum intr_trigger,
+ enum intr_polarity);
};
/*
@@ -84,6 +86,8 @@ void elcr_resume(void);
void elcr_write_trigger(u_int irq, enum intr_trigger trigger);
int intr_add_handler(const char *name, int vector, driver_intr_t handler,
void *arg, enum intr_type flags, void **cookiep);
+int intr_config_intr(int vector, enum intr_trigger trig,
+ enum intr_polarity pol);
void intr_execute_handlers(struct intsrc *isrc, struct intrframe *iframe);
struct intsrc *intr_lookup_source(int vector);
int intr_register_source(struct intsrc *isrc);
diff --git a/sys/i386/isa/atpic.c b/sys/i386/isa/atpic.c
index 470783a..7567c91 100644
--- a/sys/i386/isa/atpic.c
+++ b/sys/i386/isa/atpic.c
@@ -115,7 +115,8 @@ inthand_t
#define ATPIC(io, base, eoi, imenptr) \
{ { atpic_enable_source, atpic_disable_source, (eoi), \
atpic_enable_intr, atpic_vector, atpic_source_pending, NULL, \
- atpic_resume }, (io), (base), IDT_IO_INTS + (base), (imenptr) }
+ atpic_resume, atpic_config_intr }, (io), (base), \
+ IDT_IO_INTS + (base), (imenptr) }
#define INTSRC(irq) \
{ { &atpics[(irq) / 8].at_pic }, IDTVEC(atpic_intr ## irq ), \
@@ -146,6 +147,8 @@ static void atpic_enable_intr(struct intsrc *isrc);
static int atpic_vector(struct intsrc *isrc);
static void atpic_resume(struct intsrc *isrc);
static int atpic_source_pending(struct intsrc *isrc);
+static int atpic_config_intr(struct intsrc *isrc, enum intr_trigger trig,
+ enum intr_polarity pol);
static void i8259_init(struct atpic *pic, int slave);
static struct atpic atpics[] = {
@@ -271,6 +274,62 @@ atpic_resume(struct intsrc *isrc)
}
}
+static int
+atpic_config_intr(struct intsrc *isrc, enum intr_trigger trig,
+ enum intr_polarity pol)
+{
+ struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
+ u_int vector;
+
+ /* Map conforming values to edge/hi and sanity check the values. */
+ if (trig == INTR_TRIGGER_CONFORM)
+ trig = INTR_TRIGGER_EDGE;
+ if (pol == INTR_POLARITY_CONFORM)
+ pol = INTR_POLARITY_HIGH;
+ vector = atpic_vector(isrc);
+ if ((trig == INTR_TRIGGER_EDGE && pol == INTR_POLARITY_LOW) ||
+ (trig == INTR_TRIGGER_LEVEL && pol == INTR_POLARITY_HIGH)) {
+ printf(
+ "atpic: Mismatched config for IRQ%u: trigger %s, polarity %s\n",
+ vector, trig == INTR_TRIGGER_EDGE ? "edge" : "level",
+ pol == INTR_POLARITY_HIGH ? "high" : "low");
+ return (EINVAL);
+ }
+
+ /* If there is no change, just return. */
+ if (ai->at_trigger == trig)
+ return (0);
+
+ /*
+ * Certain IRQs can never be level/lo, so don't try to set them
+ * that way if asked. At least some ELCR registers ignore setting
+ * these bits as well.
+ */
+ if ((vector == 0 || vector == 1 || vector == 2 || vector == 13) &&
+ trig == INTR_TRIGGER_LEVEL) {
+ if (bootverbose)
+ printf(
+ "atpic: Ignoring invalid level/low configuration for IRQ%u\n",
+ vector);
+ return (EINVAL);
+ }
+ if (!using_elcr) {
+ if (bootverbose)
+ printf("atpic: No ELCR to configure IRQ%u as %s\n",
+ vector, trig == INTR_TRIGGER_EDGE ? "edge/high" :
+ "level/low");
+ return (ENXIO);
+ }
+ if (bootverbose)
+ printf("atpic: Programming IRQ%u as %s\n", vector,
+ trig == INTR_TRIGGER_EDGE ? "edge/high" : "level/low");
+ mtx_lock_spin(&icu_lock);
+ elcr_write_trigger(atpic_vector(isrc), trig);
+ ai->at_trigger = trig;
+ mtx_unlock_spin(&icu_lock);
+ return (0);
+}
+
static void
i8259_init(struct atpic *pic, int slave)
{
OpenPOWER on IntegriCloud