From 6314b3c7c3acbb81471e2f525c82660ebab5b2fb Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Tue, 30 Apr 2019 16:08:18 +0000 Subject: [PATCH 1/4] i2c: mxc_i2c: Document how non-DM functions work It is not very clear how these work in relation to the exact I2C xfers they produce. In paticular, the address length is somewhat overloaded in the read method. Clearly document the existing behavior. Maybe this will help the next person who needs to work on this driver and not break non-DM boards. Cc: Nandor Han Cc: Heiko Schocher Cc: Stefano Babic Cc: Fabio Estevam Cc: Breno Matheus Lima Signed-off-by: Trent Piepho --- drivers/i2c/mxc_i2c.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/drivers/i2c/mxc_i2c.c b/drivers/i2c/mxc_i2c.c index 5420afbc8e..f17a47f600 100644 --- a/drivers/i2c/mxc_i2c.c +++ b/drivers/i2c/mxc_i2c.c @@ -540,6 +540,25 @@ static int i2c_read_data(struct mxc_i2c_bus *i2c_bus, uchar chip, uchar *buf, #ifndef CONFIG_DM_I2C /* * Read data from I2C device + * + * The transactions use the syntax defined in the Linux kernel I2C docs. + * + * If alen is > 0, then this function will send a transaction of the form: + * S Chip Wr [A] Addr [A] S Chip Rd [A] [data] A ... NA P + * This is a normal I2C register read: writing the register address, then doing + * a repeated start and reading the data. + * + * If alen == 0, then we get this transaction: + * S Chip Wr [A] S Chip Rd [A] [data] A ... NA P + * This is somewhat unusual, though valid, transaction. It addresses the chip + * in write mode, but doesn't actually write any register address or data, then + * does a repeated start and reads data. + * + * If alen < 0, then we get this transaction: + * S Chip Rd [A] [data] A ... NA P + * The chip is addressed in read mode and then data is read. No register + * address is written first. This is perfectly valid on most devices and + * required on some (usually those that don't act like an array of registers). */ static int bus_i2c_read(struct mxc_i2c_bus *i2c_bus, u8 chip, u32 addr, int alen, u8 *buf, int len) @@ -574,6 +593,20 @@ static int bus_i2c_read(struct mxc_i2c_bus *i2c_bus, u8 chip, u32 addr, /* * Write data to I2C device + * + * If alen > 0, we get this transaction: + * S Chip Wr [A] addr [A] data [A] ... [A] P + * An ordinary write register command. + * + * If alen == 0, then we get this: + * S Chip Wr [A] data [A] ... [A] P + * This is a simple I2C write. + * + * If alen < 0, then we get this: + * S data [A] ... [A] P + * This is most likely NOT something that should be used. It doesn't send the + * chip address first, so in effect, the first byte of data will be used as the + * address. */ static int bus_i2c_write(struct mxc_i2c_bus *i2c_bus, u8 chip, u32 addr, int alen, const u8 *buf, int len) @@ -881,6 +914,7 @@ static int mxc_i2c_probe(struct udevice *bus) return 0; } +/* Sends: S Addr Wr [A|NA] P */ static int mxc_i2c_probe_chip(struct udevice *bus, u32 chip_addr, u32 chip_flags) { From c854933f50307f7abcb0280f93f8b2bba70b1ddb Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Tue, 30 Apr 2019 16:08:19 +0000 Subject: [PATCH 2/4] i2c: mxc_i2c: Fix read and read->write xfers in DM mode This is an old driver that supports both device mapped and non-mapped mode, and covers a wide range of hardware. It's hard to change without risking breaking something. I have to tried to be exceedingly detailed in this patch, so please excuse the length of the commit essay that follows. In device mapped mode the I2C xfer function does not handle plain read, and some other, transfers correctly. What it can't handle are transactions that: Start with a read, or, Have a write followed by a read, or, Have more than one read in a row. The common I2C/SMBUS read register and write register transactions always start with a write, followed by a write or a read, and then end. These work, so the bug is not apparent for most I2C slaves that only use these common xfer forms. The existing xfer loop initializes by sending the chip address in write mode after it deals with bus arbitration and master setup. When processing each message, if the next message will be a read, it sends a repeated start followed by the chip address in read mode after the current message. Obviously, this does not work if the first message is a read, as the chip is always addressed in write mode initially by i2c_init_transfer(). A write following a read does not work because the repeated start is only sent when the next message is a read. There is no logic to send it when the current message is a read and next is write. It should be sent every time the bus changes direction. The ability to use a plain read was added to this driver in commit 2feec4eafd40 ("imx: mxc_i2c: tweak the i2c transfer method"), but this applied only the non-DM code path. This patch fixes the DM code path. The xfer function will call i2c_init_transfer() with an alen of -1 to avoid sending the chip address. The same way the non-DM code achieves this. The xfer function's message loop will send the address and mode before each message if the bus changes direction, and on the first message. When reading data, the master hardware is one byte ahead of what we receive. I.e., reading a byte from the data register returns a byte *already received* by the master, and causes the master to start the RX of the *next* byte. Therefor, before we read the final byte of a message, we must tell the master what to do next. I add a "last" flag to i2c_read_data() to tell it if the message is to be followed by a stop or a repeated start. When last == true it acts exactly as before. The non-DM code can only create an xfer where the read, if any, is the final message of the xfer. And so the only callsite of i2c_read_data() in the non-DM code has the "last" parameter as true. Therefore, this change has no effect on the non-DM code. As all other changes are in the DM xfer function, which is not even compiled in non-DM code, I am confident that this patch has no effect on boards not using I2C_DM. This greatly reduces the range of hardware that could be affected. For DM boards, I have verified every transaction the "i2c" command can create on a scope and they are all exactly as they are supposed to be. I also tested write->read->write, which isn't possible with the i2c command, and it works as well. I didn't fix multiple reads in a row, as it's a lot more invasive and obviously no one has every wanted them since they've never worked. It didn't seem like the extra complexity was justified to support something no one uses. Cc: Nandor Han Cc: Heiko Schocher Cc: Stefano Babic Cc: Fabio Estevam Cc: Breno Matheus Lima Signed-off-by: Trent Piepho --- drivers/i2c/mxc_i2c.c | 95 +++++++++++++++++++++++++++++-------------- 1 file changed, 64 insertions(+), 31 deletions(-) diff --git a/drivers/i2c/mxc_i2c.c b/drivers/i2c/mxc_i2c.c index f17a47f600..23119cce65 100644 --- a/drivers/i2c/mxc_i2c.c +++ b/drivers/i2c/mxc_i2c.c @@ -482,8 +482,13 @@ static int i2c_write_data(struct mxc_i2c_bus *i2c_bus, u8 chip, const u8 *buf, return ret; } +/* Will generate a STOP after the last byte if "last" is true, i.e. this is the + * final message of a transaction. If not, it switches the bus back to TX mode + * and does not send a STOP, leaving the bus in a state where a repeated start + * and address can be sent for another message. + */ static int i2c_read_data(struct mxc_i2c_bus *i2c_bus, uchar chip, uchar *buf, - int len) + int len, bool last) { int ret; unsigned int temp; @@ -513,17 +518,31 @@ static int i2c_read_data(struct mxc_i2c_bus *i2c_bus, uchar chip, uchar *buf, return ret; } - /* - * It must generate STOP before read I2DR to prevent - * controller from generating another clock cycle - */ if (i == (len - 1)) { - i2c_imx_stop(i2c_bus); + /* Final byte has already been received by master! When + * we read it from I2DR, the master will start another + * cycle. We must program it first to send a STOP or + * switch to TX to avoid this. + */ + if (last) { + i2c_imx_stop(i2c_bus); + } else { + /* Final read, no stop, switch back to tx */ + temp = readb(base + (I2CR << reg_shift)); + temp |= I2CR_MTX | I2CR_TX_NO_AK; + writeb(temp, base + (I2CR << reg_shift)); + } } else if (i == (len - 2)) { + /* Master has already recevied penultimate byte. When + * we read it from I2DR, master will start RX of final + * byte. We must set TX_NO_AK now so it does not ACK + * that final byte. + */ temp = readb(base + (I2CR << reg_shift)); temp |= I2CR_TX_NO_AK; writeb(temp, base + (I2CR << reg_shift)); } + writeb(I2SR_IIF_CLEAR, base + (I2SR << reg_shift)); buf[i] = readb(base + (I2DR << reg_shift)); } @@ -533,7 +552,9 @@ static int i2c_read_data(struct mxc_i2c_bus *i2c_bus, uchar chip, uchar *buf, debug(" 0x%02x", buf[ret]); debug("\n"); - i2c_imx_stop(i2c_bus); + /* It is not clear to me that this is necessary */ + if (last) + i2c_imx_stop(i2c_bus); return 0; } @@ -585,7 +606,7 @@ static int bus_i2c_read(struct mxc_i2c_bus *i2c_bus, u8 chip, u32 addr, return ret; } - ret = i2c_read_data(i2c_bus, chip, buf, len); + ret = i2c_read_data(i2c_bus, chip, buf, len, true); i2c_imx_stop(i2c_bus); return ret; @@ -939,42 +960,54 @@ static int mxc_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs) ulong base = i2c_bus->base; int reg_shift = i2c_bus->driver_data & I2C_QUIRK_FLAG ? VF610_I2C_REGSHIFT : IMX_I2C_REGSHIFT; + int read_mode; - /* - * Here the 3rd parameter addr and the 4th one alen are set to 0, - * because here we only want to send out chip address. The register - * address is wrapped in msg. + /* Here address len is set to -1 to not send any address at first. + * Otherwise i2c_init_transfer will send the chip address with write + * mode set. This is wrong if the 1st message is read. */ - ret = i2c_init_transfer(i2c_bus, msg->addr, 0, 0); + ret = i2c_init_transfer(i2c_bus, msg->addr, 0, -1); if (ret < 0) { debug("i2c_init_transfer error: %d\n", ret); return ret; } + read_mode = -1; /* So it's always different on the first message */ for (; nmsgs > 0; nmsgs--, msg++) { - bool next_is_read = nmsgs > 1 && (msg[1].flags & I2C_M_RD); - debug("i2c_xfer: chip=0x%x, len=0x%x\n", msg->addr, msg->len); - if (msg->flags & I2C_M_RD) - ret = i2c_read_data(i2c_bus, msg->addr, msg->buf, - msg->len); - else { - ret = i2c_write_data(i2c_bus, msg->addr, msg->buf, - msg->len); - if (ret) - break; - if (next_is_read) { - /* Reuse ret */ + const int msg_is_read = !!(msg->flags & I2C_M_RD); + + debug("i2c_xfer: chip=0x%x, len=0x%x, dir=%c\n", msg->addr, + msg->len, msg_is_read ? 'R' : 'W'); + + if (msg_is_read != read_mode) { + /* Send repeated start if not 1st message */ + if (read_mode != -1) { + debug("i2c_xfer: [RSTART]\n"); ret = readb(base + (I2CR << reg_shift)); ret |= I2CR_RSTA; writeb(ret, base + (I2CR << reg_shift)); - - ret = tx_byte(i2c_bus, (msg->addr << 1) | 1); - if (ret < 0) { - i2c_imx_stop(i2c_bus); - break; - } } + debug("i2c_xfer: [ADDR %02x | %c]\n", msg->addr, + msg_is_read ? 'R' : 'W'); + ret = tx_byte(i2c_bus, (msg->addr << 1) | msg_is_read); + if (ret < 0) { + debug("i2c_xfer: [STOP]\n"); + i2c_imx_stop(i2c_bus); + break; + } + read_mode = msg_is_read; } + + if (msg->flags & I2C_M_RD) + ret = i2c_read_data(i2c_bus, msg->addr, msg->buf, + msg->len, nmsgs == 1 || + (msg->flags & I2C_M_STOP)); + else + ret = i2c_write_data(i2c_bus, msg->addr, msg->buf, + msg->len); + + if (ret < 0) + break; } if (ret) From d1337210d16a64eb7cf6e8a4393f08ab84656220 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Wed, 8 May 2019 23:30:01 +0000 Subject: [PATCH 3/4] wandboard: Don't use I2C speed Kconfig settings with DM_I2C When using DM_I2C the speed value supplied to setup_i2c() is not used, so this code required CONFIG_SYS_MXC_I2C[12]_SPEED to be defined to compile, but did not actually use them. Change this so we no longer need to define an unused macro to compile in DM_I2C mode. Also make it more clear that they do not control the bus speed. Otherwise it is quite easy to mistakenly believe they are used to set the bus speed. Cc: Heiko Schocher Cc: Anatolij Gustschin Signed-off-by: Trent Piepho --- board/wandboard/wandboard.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/board/wandboard/wandboard.c b/board/wandboard/wandboard.c index 69fbc8b690..9d7a94ff9d 100644 --- a/board/wandboard/wandboard.c +++ b/board/wandboard/wandboard.c @@ -46,6 +46,15 @@ DECLARE_GLOBAL_DATA_PTR; #define ETH_PHY_AR8035_POWER IMX_GPIO_NR(7, 13) #define REV_DETECTION IMX_GPIO_NR(2, 28) +/* Speed defined in Kconfig is only applicable when not using DM_I2C. */ +#ifdef CONFIG_DM_I2C +#define I2C1_SPEED_NON_DM 0 +#define I2C2_SPEED_NON_DM 0 +#else +#define I2C1_SPEED_NON_DM CONFIG_SYS_MXC_I2C1_SPEED +#define I2C2_SPEED_NON_DM CONFIG_SYS_MXC_I2C2_SPEED +#endif + static bool with_pmic; int dram_init(void) @@ -463,13 +472,13 @@ int board_init(void) gd->bd->bi_boot_params = PHYS_SDRAM + 0x100; #if defined(CONFIG_VIDEO_IPUV3) - setup_i2c(1, CONFIG_SYS_MXC_I2C1_SPEED, 0x7f, &mx6dl_i2c2_pad_info); + setup_i2c(1, I2C1_SPEED_NON_DM, 0x7f, &mx6dl_i2c2_pad_info); if (is_mx6dq() || is_mx6dqp()) { - setup_i2c(1, CONFIG_SYS_MXC_I2C1_SPEED, 0x7f, &mx6q_i2c2_pad_info); - setup_i2c(2, CONFIG_SYS_MXC_I2C2_SPEED, 0x7f, &mx6q_i2c3_pad_info); + setup_i2c(1, I2C1_SPEED_NON_DM, 0x7f, &mx6q_i2c2_pad_info); + setup_i2c(2, I2C2_SPEED_NON_DM, 0x7f, &mx6q_i2c3_pad_info); } else { - setup_i2c(1, CONFIG_SYS_MXC_I2C1_SPEED, 0x7f, &mx6dl_i2c2_pad_info); - setup_i2c(2, CONFIG_SYS_MXC_I2C2_SPEED, 0x7f, &mx6dl_i2c3_pad_info); + setup_i2c(1, I2C1_SPEED_NON_DM, 0x7f, &mx6dl_i2c2_pad_info); + setup_i2c(2, I2C2_SPEED_NON_DM, 0x7f, &mx6dl_i2c3_pad_info); } setup_display(); From ca0a8f3e8c5920485e9ab2ebba30a01b901f6bb7 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Wed, 8 May 2019 23:30:06 +0000 Subject: [PATCH 4/4] i2c: mxc: Hide kconfig based control in DM_I2C mode These options only apply when not using DM_I2C. When using device trees, the dt will enable and control the speeds of the I2C controller(s) and these configuration options have no effect. So disable them in DM_I2C mode. Otherwise they show up as decoys, and make it look like one is enabling I2C controllers and setting the speed when really it's doing nothing. However, a system using a SPL build will not use DM_I2C in the SPL, even if DM_I2C is enabled for the main u-boot. And so the SPL might use the kconfig based I2C speed controls while the main u-boot does not. Cc: Sriram Dash Cc: Priyanka Jain Cc: Heiko Schocher Signed-off-by: Trent Piepho --- drivers/i2c/Kconfig | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 215624020f..095a9bc6a4 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -161,7 +161,10 @@ config SYS_I2C_MXC channels and operating on standard mode up to 100 kbits/s and fast mode up to 400 kbits/s. -if SYS_I2C_MXC +# These settings are not used with DM_I2C, however SPL doesn't use +# DM_I2C even if DM_I2C is enabled, and so might use these settings even +# when main u-boot does not! +if SYS_I2C_MXC && (!DM_I2C || SPL) config SYS_I2C_MXC_I2C1 bool "NXP MXC I2C1" help