[PATCH 3/3] i2c: imx_lpi2c: Support read transfers longer than 256 bytes
Marek Vasut
marex at denx.de
Wed Jul 24 12:14:29 CEST 2024
On 7/24/24 11:59 AM, fedorross at gmail.com wrote:
> From: Fedor Ross <fedor.ross at ifm.com>
>
> The TXFIFO register of LPI2C only has one byte length, and if the length
> of the data that needs to be read exceeds 256 bytes, it needs to be
> written to TXFIFO multiple times.
>
> Signed-off-by: Fedor Ross <fedor.ross at ifm.com>
Add ---
> Cc: Heiko Schocher <hs at denx.de>
> Cc: Tom Rini <trini at konsulko.com>
> Cc: Marek Vasut <marex at denx.de>
> ---
> drivers/i2c/imx_lpi2c.c | 72 +++++++++++++++++++++++++++--------------
> 1 file changed, 47 insertions(+), 25 deletions(-)
>
> diff --git a/drivers/i2c/imx_lpi2c.c b/drivers/i2c/imx_lpi2c.c
> index 7b10b2b252d..dd97228ddc7 100644
> --- a/drivers/i2c/imx_lpi2c.c
> +++ b/drivers/i2c/imx_lpi2c.c
> @@ -19,6 +19,8 @@
> #define LPI2C_NACK_TOUT_MS 1
> #define LPI2C_TIMEOUT_MS 100
>
> +#define CHUNK_DATA 256
Use LPI2C_CHUNK_DATA to namespace this macro.
> static int bus_i2c_init(struct udevice *bus, int speed);
>
> /* Weak linked function for overridden by some SoC power function */
> @@ -118,8 +120,10 @@ static int bus_i2c_send(struct udevice *bus, u8 *txbuf, int len)
>
> static int bus_i2c_receive(struct udevice *bus, u8 *rxbuf, int len)
> {
> + struct dm_i2c_bus *i2c = dev_get_uclass_priv(bus);
> struct imx_lpi2c_bus *i2c_bus = dev_get_priv(bus);
> struct imx_lpi2c_reg *regs = (struct imx_lpi2c_reg *)(i2c_bus->base);
> + unsigned int chunk_len, rx_remain, timeout;
> lpi2c_status_t result = LPI2C_SUCESS;
> u32 val;
> ulong start_time = get_timer(0);
> @@ -128,33 +132,51 @@ static int bus_i2c_receive(struct udevice *bus, u8 *rxbuf, int len)
> if (!len)
> return result;
>
> - result = bus_i2c_wait_for_tx_ready(regs);
> - if (result) {
> - debug("i2c: receive wait for tx ready: %d\n", result);
> - return result;
> - }
> + /*
> + * Extend the timeout for a bulk read if needed.
> + * The calculated timeout is the result of multiplying the
> + * transfer length with 8 bit + ACK + one clock of extra time,
> + * considering the I2C bus frequency.
> + */
> + timeout = max(len * 10 * 1000 / i2c->speed_hz, LPI2C_TIMEOUT_MS);
>
> - /* clear all status flags */
> - writel(0x7f00, ®s->msr);
> - /* send receive command */
> - val = LPI2C_MTDR_CMD(0x1) | LPI2C_MTDR_DATA(len - 1);
> - writel(val, ®s->mtdr);
> + rx_remain = len;
> + while (rx_remain > 0) {
> + chunk_len = clamp(rx_remain, 1, CHUNK_DATA) - 1;
>
> - while (len--) {
> - do {
> - result = imx_lpci2c_check_clear_error(regs);
> - if (result) {
> - debug("i2c: receive check clear error: %d\n",
> - result);
> - return result;
> - }
> - if (get_timer(start_time) > LPI2C_TIMEOUT_MS) {
> - debug("i2c: receive mrdr: timeout\n");
> - return -1;
> - }
> - val = readl(®s->mrdr);
> - } while (val & LPI2C_MRDR_RXEMPTY_MASK);
> - *rxbuf++ = LPI2C_MRDR_DATA(val);
> + result = bus_i2c_wait_for_tx_ready(regs);
> + if (result) {
> + debug("i2c: receive wait for tx ready: %d\n", result);
> + return result;
> + }
> +
> + /* clear all status flags */
> + writel(0x7f00, ®s->msr);
> + /* send receive command */
> + val = LPI2C_MTDR_CMD(0x1) | LPI2C_MTDR_DATA(chunk_len);
> + writel(val, ®s->mtdr);
writel(LPI2C_MTDR_CMD(0x1) | LPI2C_MTDR_DATA(chunk_len);, ®s->mtdr);
and drop the val = assignment here.
> + rx_remain = rx_remain - (chunk_len & 0xff) - 1;
> +
> + while (len--) {
> + do {
> + result = imx_lpci2c_check_clear_error(regs);
> + if (result) {
> + debug("i2c: receive check clear error: %d\n",
> + result);
> + return result;
> + }
> + if (get_timer(start_time) > timeout) {
> + debug("i2c: receive mrdr: timeout\n");
> + return -1;
> + }
> + val = readl(®s->mrdr);
> + } while (val & LPI2C_MRDR_RXEMPTY_MASK);
> + *rxbuf++ = LPI2C_MRDR_DATA(val);
> +
> + /* send next receive command before controller NACKs last byte */
> + if ((len - rx_remain) < 2 && rx_remain > 0)
> + break;
> + }
> }
More information about the U-Boot
mailing list