git: 44847114bd4c - main - vf_i2c: update I2C controller logic
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 29 Mar 2024 23:13:06 UTC
The branch main has been updated by bz: URL: https://cgit.FreeBSD.org/src/commit/?id=44847114bd4c61ed3dfcc879fa0d7deb452e12d3 commit 44847114bd4c61ed3dfcc879fa0d7deb452e12d3 Author: Pierre-Luc Drouin <pldrouin@pldrouin.net> AuthorDate: 2024-03-22 22:13:04 +0000 Commit: Bjoern A. Zeeb <bz@FreeBSD.org> CommitDate: 2024-03-29 23:05:03 +0000 vf_i2c: update I2C controller logic Update the I2C controller logic to be more consistent with the newer version of the controller reference manual. This makes it work better on modern LS/LX platforms and avoids unnecessary delays. Also fixes a lock leak. MFC after: 7 days Tested by: bz (LS1088a FDT), Pierre-Luc Drouin (Honeycomb, ACPI) Differential Revision: https://reviews.freebsd.org/D44021 --- sys/dev/iicbus/controller/vybrid/vf_i2c.c | 148 ++++++++++--------------- sys/dev/iicbus/controller/vybrid/vf_i2c.h | 3 +- sys/dev/iicbus/controller/vybrid/vf_i2c_acpi.c | 3 +- sys/dev/iicbus/controller/vybrid/vf_i2c_fdt.c | 3 +- 4 files changed, 65 insertions(+), 92 deletions(-) diff --git a/sys/dev/iicbus/controller/vybrid/vf_i2c.c b/sys/dev/iicbus/controller/vybrid/vf_i2c.c index 7a6ec3948e13..d3c3664af78c 100644 --- a/sys/dev/iicbus/controller/vybrid/vf_i2c.c +++ b/sys/dev/iicbus/controller/vybrid/vf_i2c.c @@ -28,7 +28,8 @@ /* * Vybrid Family Inter-Integrated Circuit (I2C) - * Chapter 48, Vybrid Reference Manual, Rev. 5, 07/2013 + * Originally based on Chapter 48, Vybrid Reference Manual, Rev. 5, 07/2013 + * Currently based on Chapter 21, LX2160A Reference Manual, Rev. 1, 10/2021 * * The current implementation is based on the original driver by Ruslan Bukin, * later modified by Dawid Górecki, and split into FDT and ACPI drivers by Val @@ -184,6 +185,7 @@ vf_i2c_attach_common(device_t dev) mtx_unlock(&sc->mutex); sc->iicbus = device_add_child(dev, "iicbus", -1); + if (sc->iicbus == NULL) { device_printf(dev, "could not add iicbus child"); mtx_destroy(&sc->mutex); @@ -233,24 +235,6 @@ i2c_detach(device_t dev) return (0); } -/* Wait for transfer interrupt flag */ -static int -wait_for_iif(struct vf_i2c_softc *sc) -{ - int retry; - - retry = 1000; - while (retry --) { - if (READ1(sc, I2C_IBSR) & IBSR_IBIF) { - WRITE1(sc, I2C_IBSR, IBSR_IBIF); - return (IIC_NOERR); - } - DELAY(10); - } - - return (IIC_ETIMEOUT); -} - /* Wait for free bus */ static int wait_for_nibb(struct vf_i2c_softc *sc) @@ -272,14 +256,24 @@ static int wait_for_icf(struct vf_i2c_softc *sc) { int retry; + uint8_t ibsr; + + vf_i2c_dbg(sc, "i2c wait for transfer complete + interrupt flag\n"); retry = 1000; while (retry --) { - if (READ1(sc, I2C_IBSR) & IBSR_TCF) { - if (READ1(sc, I2C_IBSR) & IBSR_IBIF) { - WRITE1(sc, I2C_IBSR, IBSR_IBIF); - return (IIC_NOERR); + ibsr = READ1(sc, I2C_IBSR); + + if (ibsr & IBSR_IBIF) { + WRITE1(sc, I2C_IBSR, IBSR_IBIF); + + if (ibsr & IBSR_IBAL) { + WRITE1(sc, I2C_IBSR, IBSR_IBAL); + return (IIC_EBUSBSY); } + + if (ibsr & IBSR_TCF) + return (IIC_NOERR); } DELAY(10); } @@ -309,30 +303,24 @@ i2c_repeated_start(device_t dev, u_char slave, int timeout) mtx_lock(&sc->mutex); - WRITE1(sc, I2C_IBAD, slave); - if ((READ1(sc, I2C_IBSR) & IBSR_IBB) == 0) { + vf_i2c_dbg(sc, "cant i2c repeat start: bus is no longer busy\n"); mtx_unlock(&sc->mutex); return (IIC_EBUSERR); } - /* Set repeated start condition */ - DELAY(10); - reg = READ1(sc, I2C_IBCR); reg |= (IBCR_RSTA | IBCR_IBIE); WRITE1(sc, I2C_IBCR, reg); - DELAY(10); - /* Write target address - LSB is R/W bit */ WRITE1(sc, I2C_IBDR, slave); - error = wait_for_iif(sc); + error = wait_for_icf(sc); if (!tx_acked(sc)) { - vf_i2c_dbg(sc, - "cant i2c start: missing ACK after slave addres\n"); + mtx_unlock(&sc->mutex); + vf_i2c_dbg(sc, "cant i2c repeat start: missing ACK after slave address\n"); return (IIC_ENOACK); } @@ -357,27 +345,32 @@ i2c_start(device_t dev, u_char slave, int timeout) mtx_lock(&sc->mutex); - WRITE1(sc, I2C_IBAD, slave); + error = wait_for_nibb(sc); - if (READ1(sc, I2C_IBSR) & IBSR_IBB) { + /* Reset controller if bus is still busy. */ + if (error == IIC_ETIMEOUT) { + WRITE1(sc, I2C_IBCR, IBCR_MDIS); + DELAY(1000); + WRITE1(sc, I2C_IBCR, IBCR_NOACK); + error = wait_for_nibb(sc); + } + + if (error != 0) { mtx_unlock(&sc->mutex); - vf_i2c_dbg(sc, "cant i2c start: IIC_EBUSBSY\n"); - return (IIC_EBUSERR); + vf_i2c_dbg(sc, "cant i2c start: %i\n", error); + return (error); } /* Set start condition */ - reg = (IBCR_MSSL | IBCR_NOACK | IBCR_IBIE); + reg = (IBCR_MSSL | IBCR_NOACK | IBCR_IBIE | IBCR_TXRX); WRITE1(sc, I2C_IBCR, reg); - DELAY(100); - - reg |= (IBCR_TXRX); - WRITE1(sc, I2C_IBCR, reg); + WRITE1(sc, I2C_IBSR, IBSR_IBIF); /* Write target address - LSB is R/W bit */ WRITE1(sc, I2C_IBDR, slave); - error = wait_for_iif(sc); + error = wait_for_icf(sc); if (error != 0) { mtx_unlock(&sc->mutex); vf_i2c_dbg(sc, "cant i2c start: iif error\n"); @@ -386,8 +379,7 @@ i2c_start(device_t dev, u_char slave, int timeout) mtx_unlock(&sc->mutex); if (!tx_acked(sc)) { - vf_i2c_dbg(sc, - "cant i2c start: missing QACK after slave addres\n"); + vf_i2c_dbg(sc, "cant i2c start: missing ACK after slave address\n"); return (IIC_ENOACK); } @@ -405,16 +397,9 @@ i2c_stop(device_t dev) mtx_lock(&sc->mutex); - WRITE1(sc, I2C_IBCR, IBCR_NOACK | IBCR_IBIE); - - DELAY(100); + if ((READ1(sc, I2C_IBCR) & IBCR_MSSL) != 0) + WRITE1(sc, I2C_IBCR, IBCR_NOACK | IBCR_IBIE); - /* Reset controller if bus still busy after STOP */ - if (wait_for_nibb(sc) == IIC_ETIMEOUT) { - WRITE1(sc, I2C_IBCR, IBCR_MDIS); - DELAY(1000); - WRITE1(sc, I2C_IBCR, IBCR_NOACK); - } mtx_unlock(&sc->mutex); return (IIC_NOERR); @@ -469,27 +454,14 @@ i2c_reset(device_t dev, u_char speed, u_char addr, u_char *oldadr) div_reg = i2c_get_div_val(dev); vf_i2c_dbg(sc, "i2c reset\n"); - switch (speed) { - case IIC_FAST: - case IIC_SLOW: - case IIC_UNKNOWN: - case IIC_FASTEST: - default: - break; - } - mtx_lock(&sc->mutex); WRITE1(sc, I2C_IBCR, IBCR_MDIS); - DELAY(1000); - if(div_reg != DIV_REG_UNSET) WRITE1(sc, I2C_IBFD, div_reg); WRITE1(sc, I2C_IBCR, 0x0); /* Enable i2c */ - DELAY(1000); - mtx_unlock(&sc->mutex); return (IIC_NOERR); @@ -511,36 +483,34 @@ i2c_read(device_t dev, char *buf, int len, int *read, int last, int delay) if (len) { if (len == 1) - WRITE1(sc, I2C_IBCR, IBCR_IBIE | IBCR_MSSL | \ - IBCR_NOACK); + WRITE1(sc, I2C_IBCR, IBCR_IBIE | IBCR_MSSL | IBCR_NOACK); else WRITE1(sc, I2C_IBCR, IBCR_IBIE | IBCR_MSSL); /* dummy read */ READ1(sc, I2C_IBDR); - DELAY(1000); - } - while (*read < len) { - error = wait_for_icf(sc); - if (error != 0) { - mtx_unlock(&sc->mutex); - return (error); - } + while (*read < len) { + error = wait_for_icf(sc); + if (error != 0) { + mtx_unlock(&sc->mutex); + return (error); + } - if ((*read == len - 2) && last) { - /* NO ACK on last byte */ - WRITE1(sc, I2C_IBCR, IBCR_IBIE | IBCR_MSSL | \ - IBCR_NOACK); - } + if (last) { + if (*read == len - 2) { + /* NO ACK on last byte */ + WRITE1(sc, I2C_IBCR, IBCR_IBIE | IBCR_MSSL | IBCR_NOACK); - if ((*read == len - 1) && last) { - /* Transfer done, remove master bit */ - WRITE1(sc, I2C_IBCR, IBCR_IBIE | IBCR_NOACK); - } + } else if (*read == len - 1) { + /* Transfer done, remove master bit */ + WRITE1(sc, I2C_IBCR, IBCR_IBIE | IBCR_NOACK); + } + } - *buf++ = READ1(sc, I2C_IBDR); - (*read)++; + *buf++ = READ1(sc, I2C_IBDR); + (*read)++; + } } mtx_unlock(&sc->mutex); @@ -563,7 +533,7 @@ i2c_write(device_t dev, const char *buf, int len, int *sent, int timeout) while (*sent < len) { WRITE1(sc, I2C_IBDR, *buf++); - error = wait_for_iif(sc); + error = wait_for_icf(sc); if (error != 0) { mtx_unlock(&sc->mutex); return (error); diff --git a/sys/dev/iicbus/controller/vybrid/vf_i2c.h b/sys/dev/iicbus/controller/vybrid/vf_i2c.h index 3498911dfd9c..ba391e70bafc 100644 --- a/sys/dev/iicbus/controller/vybrid/vf_i2c.h +++ b/sys/dev/iicbus/controller/vybrid/vf_i2c.h @@ -28,7 +28,8 @@ /* * Vybrid Family Inter-Integrated Circuit (I2C) - * Chapter 48, Vybrid Reference Manual, Rev. 5, 07/2013 + * Originally based on Chapter 48, Vybrid Reference Manual, Rev. 5, 07/2013 + * Currently based on Chapter 21, LX2160A Reference Manual, Rev. 1, 10/2021 * * The current implementation is based on the original driver by Ruslan Bukin, * later modified by Dawid Górecki, and split into FDT and ACPI drivers by Val diff --git a/sys/dev/iicbus/controller/vybrid/vf_i2c_acpi.c b/sys/dev/iicbus/controller/vybrid/vf_i2c_acpi.c index 03f5c99a45c6..0b18926ee2df 100644 --- a/sys/dev/iicbus/controller/vybrid/vf_i2c_acpi.c +++ b/sys/dev/iicbus/controller/vybrid/vf_i2c_acpi.c @@ -27,7 +27,8 @@ /* * Vybrid Family Inter-Integrated Circuit (I2C) - * Chapter 48, Vybrid Reference Manual, Rev. 5, 07/2013 + * Originally based on Chapter 48, Vybrid Reference Manual, Rev. 5, 07/2013 + * Currently based on Chapter 21, LX2160A Reference Manual, Rev. 1, 10/2021 * * The current implementation is based on the original driver by Ruslan Bukin, * later modified by Dawid Górecki, and split into FDT and ACPI drivers by Val diff --git a/sys/dev/iicbus/controller/vybrid/vf_i2c_fdt.c b/sys/dev/iicbus/controller/vybrid/vf_i2c_fdt.c index 935f389ab516..def5ce109caf 100644 --- a/sys/dev/iicbus/controller/vybrid/vf_i2c_fdt.c +++ b/sys/dev/iicbus/controller/vybrid/vf_i2c_fdt.c @@ -28,7 +28,8 @@ /* * Vybrid Family Inter-Integrated Circuit (I2C) - * Chapter 48, Vybrid Reference Manual, Rev. 5, 07/2013 + * Originally based on Chapter 48, Vybrid Reference Manual, Rev. 5, 07/2013 + * Currently based on Chapter 21, LX2160A Reference Manual, Rev. 1, 10/2021 * * The current implementation is based on the original driver by Ruslan Bukin, * later modified by Dawid Górecki, and split into FDT and ACPI drivers by Val