[ig4] Fix I/O timeout issue with Designware I2C controller on AMD platforms

Due to hardware limitation AMD I2C controller can't trigger pending
interrupt if interrupt status has been changed after clearing
interrupt status bits.  So, I2C will lose the interrupt and IO will be
timed out. Implements a workaround to disable I2C controller interrupt
and re-enable I2C interrupt before existing interrupt handler.

Submitted by:	rajfbsd@gmail.com
MFC after:	2 weeks
Differential Revision:	https://reviews.freebsd.org/D16720
This commit is contained in:
Oleksandr Tymoshenko 2018-08-22 22:56:01 +00:00
parent 4ca8c1efe4
commit ad4c75f74a
3 changed files with 27 additions and 3 deletions

View File

@ -68,11 +68,21 @@ static char *ig4iic_ids[] = {
static int
ig4iic_acpi_probe(device_t dev)
{
ig4iic_softc_t *sc;
char *hid;
if (acpi_disabled("ig4iic") ||
ACPI_ID_PROBE(device_get_parent(dev), dev, ig4iic_ids) == NULL)
sc = device_get_softc(dev);
if (acpi_disabled("ig4iic"))
return (ENXIO);
hid = ACPI_ID_PROBE(device_get_parent(dev), dev, ig4iic_ids);
if (hid == NULL)
return (ENXIO);
if (strcmp("AMDI0010", hid) == 0)
sc->access_intr_mask = 1;
device_set_desc(dev, "Designware I2C Controller");
return (0);
}

View File

@ -724,6 +724,19 @@ ig4iic_intr(void *cookie)
++sc->rnext;
status = reg_read(sc, IG4_REG_I2C_STA);
}
/*
* Workaround to trigger pending interrupt if IG4_REG_INTR_STAT
* is changed after clearing it
*/
if(sc->access_intr_mask) {
status = reg_read(sc, IG4_REG_INTR_MASK);
if(status) {
reg_write(sc, IG4_REG_INTR_MASK, 0);
reg_write(sc, IG4_REG_INTR_MASK, status);
}
}
wakeup(sc);
mtx_unlock(&sc->io_lock);
}

View File

@ -72,6 +72,7 @@ struct ig4iic_softc {
int slave_valid : 1;
int read_started : 1;
int write_started : 1;
int access_intr_mask : 1;
/*
* Locking semantics: