The INTSTS register is read-only, so the attempt to clear interrupts
authorkettenis <kettenis@openbsd.org>
Fri, 17 May 2024 19:43:45 +0000 (19:43 +0000)
committerkettenis <kettenis@openbsd.org>
Fri, 17 May 2024 19:43:45 +0000 (19:43 +0000)
by writing to it doesn't actually work.  Therefore we have to keep
processing interrupts until all the bits corresponding to enabled
interrupts are cleared.  This makes MSIs actually work, also on the AMD
implementations for which we currently disable MSIs.

Based on some digging by martijn@

ok ratchov@, martijn@, stsp@

sys/dev/pci/azalia.c

index 4dc1a6c..b23a01a 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: azalia.c,v 1.286 2024/03/06 00:11:25 jsg Exp $        */
+/*     $OpenBSD: azalia.c,v 1.287 2024/05/17 19:43:45 kettenis Exp $   */
 /*     $NetBSD: azalia.c,v 1.20 2006/05/07 08:31:44 kent Exp $ */
 
 /*-
@@ -176,6 +176,7 @@ typedef struct azalia_t {
        int nistreams, nostreams, nbstreams;
        stream_t pstream;
        stream_t rstream;
+       uint32_t intctl;
 } azalia_t;
 #define XNAME(sc)              ((sc)->dev.dv_xname)
 #define AZ_READ_1(z, r)                bus_space_read_1((z)->iot, (z)->ioh, HDA_##r)
@@ -556,16 +557,6 @@ azalia_pci_attach(struct device *parent, struct device *self, void *aux)
                azalia_pci_write(sc->pc, sc->tag, ICH_PCI_MMC, reg);
        }
 
-       /* disable MSI for AMD Summit Ridge/Raven Ridge HD Audio */
-       if (PCI_VENDOR(sc->pciid) == PCI_VENDOR_AMD) {
-               switch (PCI_PRODUCT(sc->pciid)) {
-               case PCI_PRODUCT_AMD_17_HDA:
-               case PCI_PRODUCT_AMD_17_1X_HDA:
-               case PCI_PRODUCT_AMD_HUDSON2_HDA:
-                       pa->pa_flags &= ~PCI_FLAGS_MSI_ENABLED;
-               }
-       }
-
        /* interrupt */
        if (pci_intr_map_msi(pa, &ih) && pci_intr_map(pa, &ih)) {
                printf(": can't map interrupt\n");
@@ -684,7 +675,6 @@ azalia_pci_detach(struct device *self, int flags)
                AZ_WRITE_4(az, INTCTL, 0);
 
                DPRINTF(("%s: clear interrupts\n", __func__));
-               AZ_WRITE_4(az, INTSTS, HDA_INTSTS_CIS | HDA_INTSTS_GIS);
                AZ_WRITE_2(az, STATESTS, HDA_STATESTS_SDIWAKE);
                AZ_WRITE_1(az, RIRBSTS, HDA_RIRBSTS_RINTFL | HDA_RIRBSTS_RIRBOIS);
        }
@@ -711,29 +701,27 @@ azalia_intr(void *v)
        int ret = 0;
 
        mtx_enter(&audio_lock);
-       intsts = AZ_READ_4(az, INTSTS);
-       if (intsts == 0 || intsts == 0xffffffff) {
-               mtx_leave(&audio_lock);
-               return (ret);
-       }
-
-       AZ_WRITE_4(az, INTSTS, intsts);
+       for (;;) {
+               intsts = AZ_READ_4(az, INTSTS);
+               if ((intsts & az->intctl) == 0 || intsts == 0xffffffff)
+                       break;
 
-       if (intsts & az->pstream.intr_bit) {
-               azalia_stream_intr(&az->pstream);
-               ret = 1;
-       }
+               if (intsts & az->pstream.intr_bit) {
+                       azalia_stream_intr(&az->pstream);
+                       ret = 1;
+               }
 
-       if (intsts & az->rstream.intr_bit) {
-               azalia_stream_intr(&az->rstream);
-               ret = 1;
-       }
+               if (intsts & az->rstream.intr_bit) {
+                       azalia_stream_intr(&az->rstream);
+                       ret = 1;
+               }
 
-       if ((intsts & HDA_INTSTS_CIS) &&
-           (AZ_READ_1(az, RIRBCTL) & HDA_RIRBCTL_RINTCTL) &&
-           (AZ_READ_1(az, RIRBSTS) & HDA_RIRBSTS_RINTFL)) {
-               azalia_rirb_intr(az);
-               ret = 1;
+               if ((intsts & HDA_INTSTS_CIS) &&
+                   (AZ_READ_1(az, RIRBCTL) & HDA_RIRBCTL_RINTCTL) &&
+                   (AZ_READ_1(az, RIRBSTS) & HDA_RIRBSTS_RINTFL)) {
+                       azalia_rirb_intr(az);
+                       ret = 1;
+               }
        }
        mtx_leave(&audio_lock);
        return (ret);
@@ -918,7 +906,6 @@ azalia_init(azalia_t *az, int resuming)
        /* clear interrupt status */
        AZ_WRITE_2(az, STATESTS, HDA_STATESTS_SDIWAKE);
        AZ_WRITE_1(az, RIRBSTS, HDA_RIRBSTS_RINTFL | HDA_RIRBSTS_RIRBOIS);
-       AZ_WRITE_4(az, INTSTS, HDA_INTSTS_CIS | HDA_INTSTS_GIS);
        AZ_WRITE_4(az, DPLBASE, 0);
        AZ_WRITE_4(az, DPUBASE, 0);
 
@@ -932,8 +919,8 @@ azalia_init(azalia_t *az, int resuming)
        if (err)
                return(err);
 
-       AZ_WRITE_4(az, INTCTL,
-           AZ_READ_4(az, INTCTL) | HDA_INTCTL_CIE | HDA_INTCTL_GIE);
+       az->intctl = HDA_INTCTL_CIE | HDA_INTCTL_GIE;
+       AZ_WRITE_4(az, INTCTL, az->intctl);
 
        return(0);
 }
@@ -1421,7 +1408,6 @@ azalia_suspend(azalia_t *az)
 
        /* stop interrupts and clear status registers */
        AZ_WRITE_4(az, INTCTL, 0);
-       AZ_WRITE_4(az, INTSTS, HDA_INTSTS_CIS | HDA_INTSTS_GIS);
        AZ_WRITE_2(az, STATESTS, HDA_STATESTS_SDIWAKE);
        AZ_WRITE_1(az, RIRBSTS, HDA_RIRBSTS_RINTFL | HDA_RIRBSTS_RIRBOIS);
 
@@ -3723,7 +3709,6 @@ azalia_stream_start(stream_t *this)
        bdlist_entry_t *bdlist;
        bus_addr_t dmaaddr, dmaend;
        int err, index;
-       uint32_t intctl;
        uint8_t ctl2;
 
        err = azalia_stream_reset(this);
@@ -3768,9 +3753,8 @@ azalia_stream_start(stream_t *this)
        if (err)
                return EINVAL;
 
-       intctl = AZ_READ_4(this->az, INTCTL);
-       intctl |= this->intr_bit;
-       AZ_WRITE_4(this->az, INTCTL, intctl);
+       this->az->intctl |= this->intr_bit;
+       AZ_WRITE_4(this->az, INTCTL, this->az->intctl);
 
        STR_WRITE_1(this, CTL, STR_READ_1(this, CTL) |
            HDA_SD_CTL_DEIE | HDA_SD_CTL_FEIE | HDA_SD_CTL_IOCE |
@@ -3786,8 +3770,8 @@ azalia_stream_halt(stream_t *this)
        ctl = STR_READ_2(this, CTL);
        ctl &= ~(HDA_SD_CTL_DEIE | HDA_SD_CTL_FEIE | HDA_SD_CTL_IOCE | HDA_SD_CTL_RUN);
        STR_WRITE_2(this, CTL, ctl);
-       AZ_WRITE_4(this->az, INTCTL,
-           AZ_READ_4(this->az, INTCTL) & ~this->intr_bit);
+       this->az->intctl &= ~this->intr_bit;
+       AZ_WRITE_4(this->az, INTCTL, this->az->intctl);
        azalia_codec_disconnect_stream(this);
 
        return (0);