Fix ig4 operation for certain machines

Some machine BIOSes use the I2C bus and leave it in a state that causes
interrupts to not work properly due to a pending interrupt having been
latched.

Refactor the code a bit to clear pending interrupts when I2C is enabled.
This fixes the primary problem.

Also fix a possible race condition in the interrupt handler where the
interrupt was being cleared after reading the status instead of before.

Reported by:	pfg
Reviewed by:	jhb
Approved by:	jhb
Obtained from:	DragonFly BSD
Differential Revision:	https://reviews.freebsd.org/D6586
This commit is contained in:
grembo 2016-05-30 09:05:24 +00:00
parent 2ba028b676
commit 3c8326d3d8

View File

@ -108,6 +108,17 @@ set_controller(ig4iic_softc_t *sc, uint32_t ctl)
int error;
uint32_t v;
/*
* When the controller is enabled, interrupt on STOP detect
* or receive character ready and clear pending interrupts.
*/
if (ctl & IG4_I2C_ENABLE) {
reg_write(sc, IG4_REG_INTR_MASK, IG4_INTR_STOP_DET |
IG4_INTR_RX_FULL);
reg_read(sc, IG4_REG_CLR_INTR);
} else
reg_write(sc, IG4_REG_INTR_MASK, 0);
reg_write(sc, IG4_REG_I2C_EN, ctl);
error = SMB_ETIMEOUT;
@ -553,11 +564,6 @@ ig4iic_attach(ig4iic_softc_t *sc)
reg_write(sc, IG4_REG_RESETS, IG4_RESETS_DEASSERT);
#endif
/*
* Interrupt on STOP detect or receive character ready
*/
reg_write(sc, IG4_REG_INTR_MASK, IG4_INTR_STOP_DET |
IG4_INTR_RX_FULL);
mtx_lock(&sc->io_lock);
if (set_controller(sc, 0))
device_printf(sc->dev, "controller error during attach-1\n");
@ -574,7 +580,8 @@ ig4iic_attach(ig4iic_softc_t *sc)
sc->enum_hook.ich_func = ig4iic_start;
sc->enum_hook.ich_arg = sc->dev;
/* We have to wait until interrupts are enabled. I2C read and write
/*
* We have to wait until interrupts are enabled. I2C read and write
* only works if the interrupts are available.
*/
if (config_intrhook_establish(&sc->enum_hook) != 0)
@ -628,7 +635,6 @@ ig4iic_detach(ig4iic_softc_t *sc)
sc->smb = NULL;
sc->intr_handle = NULL;
reg_write(sc, IG4_REG_INTR_MASK, 0);
reg_read(sc, IG4_REG_CLR_INTR);
set_controller(sc, 0);
mtx_unlock(&sc->io_lock);
@ -917,6 +923,7 @@ ig4iic_intr(void *cookie)
mtx_lock(&sc->io_lock);
/* reg_write(sc, IG4_REG_INTR_MASK, IG4_INTR_STOP_DET);*/
reg_read(sc, IG4_REG_CLR_INTR);
status = reg_read(sc, IG4_REG_I2C_STA);
while (status & IG4_STATUS_RX_NOTEMPTY) {
sc->rbuf[sc->rnext & IG4_RBUFMASK] =
@ -924,7 +931,6 @@ ig4iic_intr(void *cookie)
++sc->rnext;
status = reg_read(sc, IG4_REG_I2C_STA);
}
reg_read(sc, IG4_REG_CLR_INTR);
wakeup(sc);
mtx_unlock(&sc->io_lock);
}