From 9b2ebcc06048cf49c2f8a8d152177ed1a8363878 Mon Sep 17 00:00:00 2001 From: Ye Li Date: Sun, 8 Jul 2018 11:46:40 +0800 Subject: [PATCH 1/4] imx_lpi2c: Update lpi2c driver to support imx8 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 Signed-off-by: Peng Fan Reviewed-by: Heiko Schocher --- 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 #include #include -#include +#include #include #include #include @@ -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 a32effd2838a62a5d1dcc01f613508f1d90b5667 Mon Sep 17 00:00:00 2001 From: Gao Pan Date: Sun, 8 Jul 2018 11:46:41 +0800 Subject: [PATCH 2/4] imx: lpi2c: fix clock issue when NACK detected 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 Signed-off-by: Peng Fan Reviewed-by: Heiko Schocher --- 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 #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; } From d144f61ad3faa2104676791af58c2371729bef94 Mon Sep 17 00:00:00 2001 From: Ye Li Date: Sun, 8 Jul 2018 11:46:42 +0800 Subject: [PATCH 3/4] lpi2c: Fix bus stop problem in xfer 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 Acked-by: Peng Fan Reviewed-by: Heiko Schocher --- 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; } From 971490c89251ae1a4a9e2a85911de5c217a4027c Mon Sep 17 00:00:00 2001 From: Ye Li Date: Sun, 8 Jul 2018 11:46:43 +0800 Subject: [PATCH 4/4] lpi2c: Add bus busy error handling 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 Acked-by: Peng Fan Reviewed-by: Heiko Schocher --- 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");