The current acpiprt(4) code doesn't handle non-standard polarity and
trigger mode correctly.  Typically not a problem for real hardware,
but some virtualization stuff gets creative.  The diff below tries to
do a better job.  It fixes the qemu "power button".  But before I can
commit this, it needs testing on more than just my thinkpad laptops.

So please give this a spin on your hardware, especially servers and
desktop machines.


Index: acpimadt.c
===================================================================
RCS file: /cvs/src/sys/dev/acpi/acpimadt.c,v
retrieving revision 1.26
diff -u -p -r1.26 acpimadt.c
--- acpimadt.c  7 Jan 2012 20:13:16 -0000       1.26
+++ acpimadt.c  27 Apr 2014 15:57:31 -0000
@@ -216,8 +216,7 @@ acpimadt_attach(struct device *parent, s
        arg.type = AML_OBJTYPE_INTEGER;
        arg.v_integer = 1;
 
-       if (aml_evalname(acpi_sc, NULL, "\\_PIC", 1, &arg, NULL) != 0)
-               return;
+       aml_evalname(acpi_sc, NULL, "\\_PIC", 1, &arg, NULL);
 
        mp_busses = acpimadt_busses;
        mp_nbusses = nitems(acpimadt_busses);
Index: acpiprt.c
===================================================================
RCS file: /cvs/src/sys/dev/acpi/acpiprt.c,v
retrieving revision 1.44
diff -u -p -r1.44 acpiprt.c
--- acpiprt.c   22 Dec 2013 18:55:25 -0000      1.44
+++ acpiprt.c   27 Apr 2014 15:57:31 -0000
@@ -41,6 +41,13 @@
 
 #include "ioapic.h"
 
+struct acpiprt_irq {
+       int _int;
+       int _shr;
+       int _ll;
+       int _he;
+};
+
 struct acpiprt_map {
        int bus, dev;
        int pin;
@@ -134,16 +141,29 @@ acpiprt_attach(struct device *parent, st
 int
 acpiprt_getirq(union acpi_resource *crs, void *arg)
 {
-       int *irq = (int *)arg;
-       int typ;
+       struct acpiprt_irq *irq = arg;
+       int typ, len;
+
+       irq->_shr = 0;
+       irq->_ll = 0;
+       irq->_he = 1;
 
        typ = AML_CRSTYPE(crs);
+       len = AML_CRSLEN(crs);
        switch (typ) {
        case SR_IRQ:
-               *irq = ffs(letoh16(crs->sr_irq.irq_mask)) - 1;
+               irq->_int= ffs(letoh16(crs->sr_irq.irq_mask)) - 1;
+               if (len > 2) {
+                       irq->_shr = (crs->sr_irq.irq_flags & SR_IRQ_SHR);
+                       irq->_ll = (crs->sr_irq.irq_flags & SR_IRQ_POLARITY);
+                       irq->_he = (crs->sr_irq.irq_flags & SR_IRQ_MODE);
+               }
                break;
        case LR_EXTIRQ:
-               *irq = letoh32(crs->lr_extirq.irq[0]);
+               irq->_int = letoh32(crs->lr_extirq.irq[0]);
+               irq->_shr = (crs->lr_extirq.flags & LR_EXTIRQ_SHR);
+               irq->_ll = (crs->lr_extirq.flags & LR_EXTIRQ_POLARITY);
+               irq->_he = (crs->lr_extirq.flags & LR_EXTIRQ_MODE);
                break;
        default:
                printf("unknown interrupt: %x\n", typ);
@@ -174,35 +194,48 @@ acpiprt_pri[16] = {
 int
 acpiprt_chooseirq(union acpi_resource *crs, void *arg)
 {
-       int *irq = (int *)arg;
-       int typ, i, pri = -1;
+       struct acpiprt_irq *irq = arg;
+       int typ, len, i, pri = -1;
+
+       irq->_shr = 0;
+       irq->_ll = 0;
+       irq->_he = 1;
 
        typ = AML_CRSTYPE(crs);
+       len = AML_CRSLEN(crs);
        switch (typ) {
        case SR_IRQ:
                for (i = 0; i < sizeof(crs->sr_irq.irq_mask) * 8; i++) {
                        if (crs->sr_irq.irq_mask & (1 << i) &&
                            acpiprt_pri[i] > pri) {
-                               *irq = i;
-                               pri = acpiprt_pri[*irq];
+                               irq->_int = i;
+                               pri = acpiprt_pri[irq->_int];
                        }
                }
+               if (len > 2) {
+                       irq->_shr = (crs->sr_irq.irq_flags & SR_IRQ_SHR);
+                       irq->_ll = (crs->sr_irq.irq_flags & SR_IRQ_POLARITY);
+                       irq->_he = (crs->sr_irq.irq_flags & SR_IRQ_MODE);
+               }
                break;
        case LR_EXTIRQ:
                /* First try non-8259 interrupts. */
                for (i = 0; i < crs->lr_extirq.irq_count; i++) {
                        if (crs->lr_extirq.irq[i] > 15) {
-                               *irq = crs->lr_extirq.irq[i];
+                               irq->_int = crs->lr_extirq.irq[i];
                                return (0);
                        }
                }
 
                for (i = 0; i < crs->lr_extirq.irq_count; i++) {
                        if (acpiprt_pri[crs->lr_extirq.irq[i]] > pri) {
-                               *irq = crs->lr_extirq.irq[i];
-                               pri = acpiprt_pri[*irq];
+                               irq->_int = crs->lr_extirq.irq[i];
+                               pri = acpiprt_pri[irq->_int];
                        }
                }
+               irq->_shr = (crs->lr_extirq.flags & LR_EXTIRQ_SHR);
+               irq->_ll = (crs->lr_extirq.flags & LR_EXTIRQ_POLARITY);
+               irq->_he = (crs->lr_extirq.flags & LR_EXTIRQ_MODE);
                break;
        default:
                printf("unknown interrupt: %x\n", typ);
@@ -215,8 +248,9 @@ acpiprt_prt_add(struct acpiprt_softc *sc
 {
        struct aml_node *node;
        struct aml_value res, *pp;
+       struct acpiprt_irq irq;
        u_int64_t addr;
-       int pin, irq;
+       int pin;
        int64_t sta;
 #if NIOAPIC > 0
        struct mp_intr_map *map;
@@ -283,7 +317,7 @@ acpiprt_prt_add(struct acpiprt_softc *sc
                aml_freevalue(&res);
 
                /* Pick a new IRQ if necessary. */
-               if ((irq == 0 || irq == 2 || irq == 13) &&
+               if ((irq._int == 0 || irq._int == 2 || irq._int == 13) &&
                    !aml_evalname(sc->sc_acpi, node, "_PRS", 0, NULL, &res)){
                        aml_parse_resource(&res, acpiprt_chooseirq, &irq);
                        aml_freevalue(&res);
@@ -294,24 +328,28 @@ acpiprt_prt_add(struct acpiprt_softc *sc
                p->bus = sc->sc_bus;
                p->dev = ACPI_PCI_DEV(addr << 16);
                p->pin = pin;
-               p->irq = irq;
+               p->irq = irq._int;
                p->sc = sc;
                p->node = node;
                SIMPLEQ_INSERT_TAIL(&acpiprt_map_list, p, list);
        } else {
-               irq = aml_val2int(v->v_package[3]);
+               irq._int = aml_val2int(v->v_package[3]);
+               irq._shr = 1;
+               irq._ll = 1;
+               irq._he = 0;
        }
 
 #ifdef ACPI_DEBUG
        printf("%s: %s addr 0x%llx pin %d irq %d\n",
-           DEVNAME(sc), aml_nodename(pp->node), addr, pin, irq);
+           DEVNAME(sc), aml_nodename(pp->node), addr, pin, irq._int);
 #endif
 
 #if NIOAPIC > 0
        if (nioapics > 0) {
-               apic = ioapic_find_bybase(irq);
+               apic = ioapic_find_bybase(irq._int);
                if (apic == NULL) {
-                       printf("%s: no apic found for irq %d\n", DEVNAME(sc), 
irq);
+                       printf("%s: no apic found for irq %d\n",
+                           DEVNAME(sc), irq._int);
                        return;
                }
 
@@ -320,10 +358,30 @@ acpiprt_prt_add(struct acpiprt_softc *sc
                        return;
 
                map->ioapic = apic;
-               map->ioapic_pin = irq - apic->sc_apic_vecbase;
+               map->ioapic_pin = irq._int - apic->sc_apic_vecbase;
                map->bus_pin = ((addr >> 14) & 0x7c) | (pin & 0x3);
-               map->redir = IOAPIC_REDLO_ACTLO | IOAPIC_REDLO_LEVEL;
-               map->redir |= (IOAPIC_REDLO_DEL_LOPRI << 
IOAPIC_REDLO_DEL_SHIFT);
+               if (irq._ll)
+                       map->flags |= (MPS_INTPO_ACTLO << MPS_INTPO_SHIFT);
+               else
+                       map->flags |= (MPS_INTPO_ACTHI << MPS_INTPO_SHIFT);
+               if (irq._he)
+                       map->flags |= (MPS_INTTR_EDGE << MPS_INTTR_SHIFT);
+               else
+                       map->flags |= (MPS_INTTR_LEVEL << MPS_INTTR_SHIFT);
+
+               map->redir = (IOAPIC_REDLO_DEL_LOPRI << IOAPIC_REDLO_DEL_SHIFT);
+               switch ((map->flags >> MPS_INTPO_SHIFT) & MPS_INTPO_MASK) {
+               case MPS_INTPO_DEF:
+               case MPS_INTPO_ACTLO:
+                       map->redir |= IOAPIC_REDLO_ACTLO;
+                       break;
+               }
+               switch ((map->flags >> MPS_INTTR_SHIFT) & MPS_INTTR_MASK) {
+               case MPS_INTTR_DEF:
+               case MPS_INTTR_LEVEL:
+                       map->redir |= IOAPIC_REDLO_LEVEL;
+                       break;
+               }
 
                map->ioapic_ih = APIC_INT_VIA_APIC |
                    ((apic->sc_apicid << APIC_INT_APIC_SHIFT) |
@@ -353,7 +411,7 @@ acpiprt_prt_add(struct acpiprt_softc *sc
                reg = pci_conf_read(pc, tag, PCI_INTERRUPT_REG);
                if (PCI_INTERRUPT_PIN(reg) == pin + 1) {
                        reg &= ~(PCI_INTERRUPT_LINE_MASK << 
PCI_INTERRUPT_LINE_SHIFT);
-                       reg |= irq << PCI_INTERRUPT_LINE_SHIFT;
+                       reg |= irq._int << PCI_INTERRUPT_LINE_SHIFT;
                        pci_conf_write(pc, tag, PCI_INTERRUPT_REG, reg);
                }
        }
@@ -372,10 +430,11 @@ acpiprt_route_interrupt(int bus, int dev
 {
        struct acpiprt_softc *sc;
        struct acpiprt_map *p;
+       struct acpiprt_irq irq;
        struct aml_node *node = NULL;
        struct aml_value res, res2;
        union acpi_resource *crs;
-       int irq, newirq;
+       int newirq;
        int64_t sta;
 
        SIMPLEQ_FOREACH(p, &acpiprt_map_list, list) {
@@ -408,7 +467,7 @@ acpiprt_route_interrupt(int bus, int dev
        aml_parse_resource(&res, acpiprt_getirq, &irq);
 
        /* Only re-route interrupts when necessary. */
-       if ((sta & STA_ENABLED) && irq == newirq) {
+       if ((sta & STA_ENABLED) && irq._int == newirq) {
                aml_freevalue(&res);
                return;
        }
Index: dsdt.h
===================================================================
RCS file: /cvs/src/sys/dev/acpi/dsdt.h,v
retrieving revision 1.61
diff -u -p -r1.61 dsdt.h
--- dsdt.h      18 Jan 2013 04:07:06 -0000      1.61
+++ dsdt.h      27 Apr 2014 15:57:31 -0000
@@ -168,6 +168,9 @@ union acpi_resource {
                uint8_t  typecode;
                uint16_t length;
                uint8_t  flags;
+#define LR_EXTIRQ_SHR          (1L << 3)
+#define LR_EXTIRQ_POLARITY     (1L << 2)
+#define LR_EXTIRQ_MODE         (1L << 1)
                uint8_t  irq_count;
                uint32_t irq[1];
        } __packed lr_extirq;

Reply via email to