A couple of problems were causing occasional hangs during suspend to S3
on my laptop. The hang is caused by the while loop in hdac_one_intr(),
which loops forever if the RIRBSTS read returns all ones following entry
to the D3 state.
First, hdac_intr_handler() loads the interrupts status register before
locking the softc. This can race with hdac_suspend()->hdac_reset() such
that the reset occurs between a load of the status register and the call
to hdac_one_intr(). In this case, the load from RIRBSTS returns 0xff
and the following loop never terminates.
Second, if the interrupt handler is running after BUS_SUSPEND_CHILD()
powers down the device (e.g., because it was blocking on the softc
mutex), reading INTSTS can return all ones. Explicitly check for that
and simply bail in that case. I note that the Linux HDA driver does
this as well.
I'm not sure if this is the right solution, but it's simple and seems to
be robust in my testing. In particular, I don't see any interfaces that
allow one to "drain" a suspended interrupt handler.