[U-Boot] [PATCH V2 1/4] imx_lpi2c: Update lpi2c driver to support imx8

From: Ye Li ye.li@nxp.com
Add compatible string for i.MX8 and move imx_lpi2c.h from mx7ulp directory to u-boot include directory as a common header file.
Signed-off-by: Ye Li ye.li@nxp.com Signed-off-by: Peng Fan peng.fan@nxp.com Reviewed-by: Heiko Schocher hs@denx.de ---
V2: Add review tag
drivers/i2c/imx_lpi2c.c | 3 ++- {arch/arm/include/asm/arch-mx7ulp => include}/imx_lpi2c.h | 0 2 files changed, 2 insertions(+), 1 deletion(-) rename {arch/arm/include/asm/arch-mx7ulp => include}/imx_lpi2c.h (100%)
diff --git a/drivers/i2c/imx_lpi2c.c b/drivers/i2c/imx_lpi2c.c index 8d3e0555a1..a6e41c5c91 100644 --- a/drivers/i2c/imx_lpi2c.c +++ b/drivers/i2c/imx_lpi2c.c @@ -8,7 +8,7 @@ #include <asm/io.h> #include <asm/arch/clock.h> #include <asm/arch/imx-regs.h> -#include <asm/arch/imx_lpi2c.h> +#include <imx_lpi2c.h> #include <asm/arch/sys_proto.h> #include <dm.h> #include <fdtdec.h> @@ -447,6 +447,7 @@ static const struct dm_i2c_ops imx_lpi2c_ops = {
static const struct udevice_id imx_lpi2c_ids[] = { { .compatible = "fsl,imx7ulp-lpi2c", }, + { .compatible = "fsl,imx8qm-lpi2c", }, {} };
diff --git a/arch/arm/include/asm/arch-mx7ulp/imx_lpi2c.h b/include/imx_lpi2c.h similarity index 100% rename from arch/arm/include/asm/arch-mx7ulp/imx_lpi2c.h rename to include/imx_lpi2c.h

From: Gao Pan pandy.gao@nxp.com
For LPI2C IP, NACK is detected by the rising edge of the ninth clock. In current uboot driver, once NACK is detected, it will reset and then disable LPI2C master. As a result, we can never see the falling edge of the ninth clock.
Signed-off-by: Gao Pan pandy.gao@nxp.com Signed-off-by: Peng Fan peng.fan@nxp.com Reviewed-by: Heiko Schocher hs@denx.de ---
V2: Add review tag
drivers/i2c/imx_lpi2c.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/drivers/i2c/imx_lpi2c.c b/drivers/i2c/imx_lpi2c.c index a6e41c5c91..d2e11b411b 100644 --- a/drivers/i2c/imx_lpi2c.c +++ b/drivers/i2c/imx_lpi2c.c @@ -15,6 +15,7 @@ #include <i2c.h>
#define LPI2C_FIFO_SIZE 4 +#define LPI2C_NACK_TOUT_MS 1 #define LPI2C_TIMEOUT_MS 100
/* Weak linked function for overridden by some SoC power function */ @@ -184,6 +185,7 @@ static int bus_i2c_stop(struct imx_lpi2c_reg *regs) { lpi2c_status_t result; u32 status; + ulong start_time;
result = bus_i2c_wait_for_tx_ready(regs); if (result) { @@ -194,7 +196,8 @@ static int bus_i2c_stop(struct imx_lpi2c_reg *regs) /* send stop command */ writel(LPI2C_MTDR_CMD(0x2), ®s->mtdr);
- while (result == LPI2C_SUCESS) { + start_time = get_timer(0); + while (1) { status = readl(®s->msr); result = imx_lpci2c_check_clear_error(regs); /* stop detect flag */ @@ -204,6 +207,11 @@ static int bus_i2c_stop(struct imx_lpi2c_reg *regs) writel(status, ®s->msr); break; } + + if (get_timer(start_time) > LPI2C_NACK_TOUT_MS) { + debug("stop timeout\n"); + return -ETIMEDOUT; + } }
return result; @@ -363,10 +371,8 @@ static int imx_lpi2c_probe_chip(struct udevice *bus, u32 chip, }
result = bus_i2c_stop(regs); - if (result) { + if (result) bus_i2c_init(bus, 100000); - return -result; - }
return result; }

Hello Peng,
Am 08.07.2018 um 05:46 schrieb Peng Fan:
From: Gao Pan pandy.gao@nxp.com
For LPI2C IP, NACK is detected by the rising edge of the ninth clock. In current uboot driver, once NACK is detected, it will reset and then disable LPI2C master. As a result, we can never see the falling edge of the ninth clock.
Signed-off-by: Gao Pan pandy.gao@nxp.com Signed-off-by: Peng Fan peng.fan@nxp.com Reviewed-by: Heiko Schocher hs@denx.de
V2: Add review tag
drivers/i2c/imx_lpi2c.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-)
Applied to u-boot-i2c.git master
Thanks!
bye, Heiko

From: Ye Li ye.li@nxp.com
In xfer function, both bus_i2c_read and bus_i2c_write will send a STOP command. This causes a problem when reading register data from i2c device.
Generally two operations comprise the register data reading: 1. Write the register address to i2c device. START | chip_addr | W | ACK | register_addr | ACK |
2. Read the Data from i2c device. START | chip_addr | R | ACK | DATA | NACK | STOP
The STOP command should happen at the end of the transfer, otherwise we will always get data from register address 0
Signed-off-by: Ye Li ye.li@nxp.com Acked-by: Peng Fan peng.fan@nxp.com Reviewed-by: Heiko Schocher hs@denx.de ---
V2: Add review tag
drivers/i2c/imx_lpi2c.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/drivers/i2c/imx_lpi2c.c b/drivers/i2c/imx_lpi2c.c index d2e11b411b..585787a35c 100644 --- a/drivers/i2c/imx_lpi2c.c +++ b/drivers/i2c/imx_lpi2c.c @@ -225,9 +225,6 @@ static int bus_i2c_read(struct imx_lpi2c_reg *regs, u32 chip, u8 *buf, int len) if (result) return result; result = bus_i2c_receive(regs, buf, len); - if (result) - return result; - result = bus_i2c_stop(regs); if (result) return result;
@@ -242,9 +239,6 @@ static int bus_i2c_write(struct imx_lpi2c_reg *regs, u32 chip, u8 *buf, int len) if (result) return result; result = bus_i2c_send(regs, buf, len); - if (result) - return result; - result = bus_i2c_stop(regs); if (result) return result;
@@ -380,7 +374,7 @@ static int imx_lpi2c_probe_chip(struct udevice *bus, u32 chip, static int imx_lpi2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs) { struct imx_lpi2c_reg *regs; - int ret = 0; + int ret = 0, ret_stop;
regs = (struct imx_lpi2c_reg *)devfdt_get_addr(bus); for (; nmsgs > 0; nmsgs--, msg++) { @@ -398,6 +392,12 @@ static int imx_lpi2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs) if (ret) debug("i2c_write: error sending\n");
+ ret_stop = bus_i2c_stop(regs); + if (ret_stop) + debug("i2c_xfer: stop bus error\n"); + + ret |= ret_stop; + return ret; }

Hello Peng,
Am 08.07.2018 um 05:46 schrieb Peng Fan:
From: Ye Li ye.li@nxp.com
In xfer function, both bus_i2c_read and bus_i2c_write will send a STOP command. This causes a problem when reading register data from i2c device.
Generally two operations comprise the register data reading: 1. Write the register address to i2c device. START | chip_addr | W | ACK | register_addr | ACK |
2. Read the Data from i2c device. START | chip_addr | R | ACK | DATA | NACK | STOP
The STOP command should happen at the end of the transfer, otherwise we will always get data from register address 0
Signed-off-by: Ye Li ye.li@nxp.com Acked-by: Peng Fan peng.fan@nxp.com Reviewed-by: Heiko Schocher hs@denx.de
V2: Add review tag
drivers/i2c/imx_lpi2c.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-)
Applied to u-boot-i2c.git master
Thanks!
bye, Heiko

From: Ye Li ye.li@nxp.com
When doing "i2c dev 4; i2c probe" with ENET daughter card connected on iMX8QXP MEK board, we met a i2c bus busy issue, that the BBF of lpi2c always show busy, but the master is idle, and stop is detected (SDF set).
This patch addes a handling to re-init the lpi2c master for this case. Then the issue can be worked around.
Signed-off-by: Ye Li ye.li@nxp.com Acked-by: Peng Fan peng.fan@nxp.com Reviewed-by: Heiko Schocher hs@denx.de ---
V2: Add review tag. Heiko, I have no idea why the busy show, from the bus, we measured it is idle, but the controller bit shows busy. We followed Linux kernel's method to reinit the bus and all works.
drivers/i2c/imx_lpi2c.c | 53 +++++++++++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 21 deletions(-)
diff --git a/drivers/i2c/imx_lpi2c.c b/drivers/i2c/imx_lpi2c.c index 585787a35c..ff07ca34aa 100644 --- a/drivers/i2c/imx_lpi2c.c +++ b/drivers/i2c/imx_lpi2c.c @@ -18,6 +18,8 @@ #define LPI2C_NACK_TOUT_MS 1 #define LPI2C_TIMEOUT_MS 100
+static int bus_i2c_init(struct udevice *bus, int speed); + /* Weak linked function for overridden by some SoC power function */ int __weak init_i2c_power(unsigned i2c_num) { @@ -91,8 +93,9 @@ static int bus_i2c_wait_for_tx_ready(struct imx_lpi2c_reg *regs) return result; }
-static int bus_i2c_send(struct imx_lpi2c_reg *regs, u8 *txbuf, int len) +static int bus_i2c_send(struct udevice *bus, u8 *txbuf, int len) { + struct imx_lpi2c_reg *regs = (struct imx_lpi2c_reg *)devfdt_get_addr(bus); lpi2c_status_t result = LPI2C_SUCESS;
/* empty tx */ @@ -111,8 +114,9 @@ static int bus_i2c_send(struct imx_lpi2c_reg *regs, u8 *txbuf, int len) return result; }
-static int bus_i2c_receive(struct imx_lpi2c_reg *regs, u8 *rxbuf, int len) +static int bus_i2c_receive(struct udevice *bus, u8 *rxbuf, int len) { + struct imx_lpi2c_reg *regs = (struct imx_lpi2c_reg *)devfdt_get_addr(bus); lpi2c_status_t result = LPI2C_SUCESS; u32 val; ulong start_time = get_timer(0); @@ -153,15 +157,24 @@ static int bus_i2c_receive(struct imx_lpi2c_reg *regs, u8 *rxbuf, int len) return result; }
-static int bus_i2c_start(struct imx_lpi2c_reg *regs, u8 addr, u8 dir) +static int bus_i2c_start(struct udevice *bus, u8 addr, u8 dir) { lpi2c_status_t result; + struct imx_lpi2c_reg *regs = + (struct imx_lpi2c_reg *)devfdt_get_addr(bus); u32 val;
result = imx_lpci2c_check_busy_bus(regs); if (result) { debug("i2c: start check busy bus: 0x%x\n", result); - return result; + + /* Try to init the lpi2c then check the bus busy again */ + bus_i2c_init(bus, 100000); + result = imx_lpci2c_check_busy_bus(regs); + if (result) { + printf("i2c: Error check busy bus: 0x%x\n", result); + return result; + } } /* clear all status flags */ writel(0x7f00, ®s->msr); @@ -181,9 +194,11 @@ static int bus_i2c_start(struct imx_lpi2c_reg *regs, u8 addr, u8 dir) return result; }
-static int bus_i2c_stop(struct imx_lpi2c_reg *regs) +static int bus_i2c_stop(struct udevice *bus) { lpi2c_status_t result; + struct imx_lpi2c_reg *regs = + (struct imx_lpi2c_reg *)devfdt_get_addr(bus); u32 status; ulong start_time;
@@ -217,28 +232,28 @@ static int bus_i2c_stop(struct imx_lpi2c_reg *regs) return result; }
-static int bus_i2c_read(struct imx_lpi2c_reg *regs, u32 chip, u8 *buf, int len) +static int bus_i2c_read(struct udevice *bus, u32 chip, u8 *buf, int len) { lpi2c_status_t result;
- result = bus_i2c_start(regs, chip, 1); + result = bus_i2c_start(bus, chip, 1); if (result) return result; - result = bus_i2c_receive(regs, buf, len); + result = bus_i2c_receive(bus, buf, len); if (result) return result;
return result; }
-static int bus_i2c_write(struct imx_lpi2c_reg *regs, u32 chip, u8 *buf, int len) +static int bus_i2c_write(struct udevice *bus, u32 chip, u8 *buf, int len) { lpi2c_status_t result;
- result = bus_i2c_start(regs, chip, 0); + result = bus_i2c_start(bus, chip, 0); if (result) return result; - result = bus_i2c_send(regs, buf, len); + result = bus_i2c_send(bus, buf, len); if (result) return result;
@@ -353,18 +368,16 @@ static int bus_i2c_init(struct udevice *bus, int speed) static int imx_lpi2c_probe_chip(struct udevice *bus, u32 chip, u32 chip_flags) { - struct imx_lpi2c_reg *regs; lpi2c_status_t result;
- regs = (struct imx_lpi2c_reg *)devfdt_get_addr(bus); - result = bus_i2c_start(regs, chip, 0); + result = bus_i2c_start(bus, chip, 0); if (result) { - bus_i2c_stop(regs); + bus_i2c_stop(bus); bus_i2c_init(bus, 100000); return result; }
- result = bus_i2c_stop(regs); + result = bus_i2c_stop(bus); if (result) bus_i2c_init(bus, 100000);
@@ -373,16 +386,14 @@ static int imx_lpi2c_probe_chip(struct udevice *bus, u32 chip,
static int imx_lpi2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs) { - struct imx_lpi2c_reg *regs; int ret = 0, ret_stop;
- regs = (struct imx_lpi2c_reg *)devfdt_get_addr(bus); for (; nmsgs > 0; nmsgs--, msg++) { debug("i2c_xfer: chip=0x%x, len=0x%x\n", msg->addr, msg->len); if (msg->flags & I2C_M_RD) - ret = bus_i2c_read(regs, msg->addr, msg->buf, msg->len); + ret = bus_i2c_read(bus, msg->addr, msg->buf, msg->len); else { - ret = bus_i2c_write(regs, msg->addr, msg->buf, + ret = bus_i2c_write(bus, msg->addr, msg->buf, msg->len); if (ret) break; @@ -392,7 +403,7 @@ static int imx_lpi2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs) if (ret) debug("i2c_write: error sending\n");
- ret_stop = bus_i2c_stop(regs); + ret_stop = bus_i2c_stop(bus); if (ret_stop) debug("i2c_xfer: stop bus error\n");

Hello Peng,
Am 08.07.2018 um 05:46 schrieb Peng Fan:
From: Ye Li ye.li@nxp.com
When doing "i2c dev 4; i2c probe" with ENET daughter card connected on iMX8QXP MEK board, we met a i2c bus busy issue, that the BBF of lpi2c always show busy, but the master is idle, and stop is detected (SDF set).
This patch addes a handling to re-init the lpi2c master for this case. Then the issue can be worked around.
Signed-off-by: Ye Li ye.li@nxp.com Acked-by: Peng Fan peng.fan@nxp.com Reviewed-by: Heiko Schocher hs@denx.de
V2: Add review tag. Heiko, I have no idea why the busy show, from the bus, we measured it is idle, but the controller bit shows busy. We followed Linux kernel's method to reinit the bus and all works.
drivers/i2c/imx_lpi2c.c | 53 +++++++++++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 21 deletions(-)
Applied to u-boot-i2c.git master
Thanks!
bye, Heiko

Hello Peng,
Am 08.07.2018 um 05:46 schrieb Peng Fan:
From: Ye Li ye.li@nxp.com
Add compatible string for i.MX8 and move imx_lpi2c.h from mx7ulp directory to u-boot include directory as a common header file.
Signed-off-by: Ye Li ye.li@nxp.com Signed-off-by: Peng Fan peng.fan@nxp.com Reviewed-by: Heiko Schocher hs@denx.de
V2: Add review tag
drivers/i2c/imx_lpi2c.c | 3 ++- {arch/arm/include/asm/arch-mx7ulp => include}/imx_lpi2c.h | 0 2 files changed, 2 insertions(+), 1 deletion(-) rename {arch/arm/include/asm/arch-mx7ulp => include}/imx_lpi2c.h (100%)
Applied to u-boot-i2c.git master
Thanks!
bye, Heiko
participants (2)
-
Heiko Schocher
-
Peng Fan