diff options
Diffstat (limited to 'drivers/i2c')
-rw-r--r-- | drivers/i2c/Kconfig | 5 | ||||
-rw-r--r-- | drivers/i2c/ihs_i2c.c | 124 | ||||
-rw-r--r-- | drivers/i2c/mvtwsi.c | 11 | ||||
-rw-r--r-- | drivers/i2c/mxc_i2c.c | 129 | ||||
-rw-r--r-- | drivers/i2c/rk_i2c.c | 6 | ||||
-rw-r--r-- | drivers/i2c/stm32f7_i2c.c | 6 |
6 files changed, 187 insertions, 94 deletions
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 215624020f6..095a9bc6a4d 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 diff --git a/drivers/i2c/ihs_i2c.c b/drivers/i2c/ihs_i2c.c index 0922fe9bb10..f7b59d36f98 100644 --- a/drivers/i2c/ihs_i2c.c +++ b/drivers/i2c/ihs_i2c.c @@ -8,8 +8,7 @@ #include <i2c.h> #ifdef CONFIG_DM_I2C #include <dm.h> -#include <fpgamap.h> -#include "../misc/gdsys_soc.h" +#include <regmap.h> #else #include <gdsys_fpga.h> #endif @@ -18,18 +17,24 @@ #ifdef CONFIG_DM_I2C struct ihs_i2c_priv { uint speed; - phys_addr_t addr; + struct regmap *map; }; -enum { - REG_INTERRUPT_STATUS = 0x00, - REG_INTERRUPT_ENABLE_CONTROL = 0x02, - REG_WRITE_MAILBOX_EXT = 0x04, - REG_WRITE_MAILBOX = 0x06, - REG_READ_MAILBOX_EXT = 0x08, - REG_READ_MAILBOX = 0x0A, +struct ihs_i2c_regs { + u16 interrupt_status; + u16 interrupt_enable_control; + u16 write_mailbox_ext; + u16 write_mailbox; + u16 read_mailbox_ext; + u16 read_mailbox; }; +#define ihs_i2c_set(map, member, val) \ + regmap_set(map, struct ihs_i2c_regs, member, val) + +#define ihs_i2c_get(map, member, valp) \ + regmap_get(map, struct ihs_i2c_regs, member, valp) + #else /* !CONFIG_DM_I2C */ DECLARE_GLOBAL_DATA_PTR; @@ -92,14 +97,10 @@ static int wait_for_int(bool read) uint ctr = 0; #ifdef CONFIG_DM_I2C struct ihs_i2c_priv *priv = dev_get_priv(dev); - struct udevice *fpga; - - gdsys_soc_get_fpga(dev, &fpga); #endif #ifdef CONFIG_DM_I2C - fpgamap_read(fpga, priv->addr + REG_INTERRUPT_STATUS, &val, - FPGAMAP_SIZE_16); + ihs_i2c_get(priv->map, interrupt_status, &val); #else I2C_GET_REG(interrupt_status, &val); #endif @@ -107,17 +108,18 @@ static int wait_for_int(bool read) while (!(val & (I2CINT_ERROR_EV | (read ? I2CINT_RECEIVE_EV : I2CINT_TRANSMIT_EV)))) { udelay(10); - if (ctr++ > 5000) - return 1; + if (ctr++ > 5000) { + debug("%s: timed out\n", __func__); + return -ETIMEDOUT; + } #ifdef CONFIG_DM_I2C - fpgamap_read(fpga, priv->addr + REG_INTERRUPT_STATUS, &val, - FPGAMAP_SIZE_16); + ihs_i2c_get(priv->map, interrupt_status, &val); #else I2C_GET_REG(interrupt_status, &val); #endif } - return (val & I2CINT_ERROR_EV) ? 1 : 0; + return (val & I2CINT_ERROR_EV) ? -EIO : 0; } #ifdef CONFIG_DM_I2C @@ -130,20 +132,16 @@ static int ihs_i2c_transfer(uchar chip, uchar *buffer, int len, bool read, { u16 val; u16 data; + int res; #ifdef CONFIG_DM_I2C struct ihs_i2c_priv *priv = dev_get_priv(dev); - struct udevice *fpga; - - gdsys_soc_get_fpga(dev, &fpga); #endif /* Clear interrupt status */ data = I2CINT_ERROR_EV | I2CINT_RECEIVE_EV | I2CINT_TRANSMIT_EV; #ifdef CONFIG_DM_I2C - fpgamap_write(fpga, priv->addr + REG_INTERRUPT_STATUS, &data, - FPGAMAP_SIZE_16); - fpgamap_read(fpga, priv->addr + REG_INTERRUPT_STATUS, &val, - FPGAMAP_SIZE_16); + ihs_i2c_set(priv->map, interrupt_status, data); + ihs_i2c_get(priv->map, interrupt_status, &val); #else I2C_SET_REG(interrupt_status, data); I2C_GET_REG(interrupt_status, &val); @@ -156,8 +154,7 @@ static int ihs_i2c_transfer(uchar chip, uchar *buffer, int len, bool read, if (len > 1) val |= buffer[1] << 8; #ifdef CONFIG_DM_I2C - fpgamap_write(fpga, priv->addr + REG_WRITE_MAILBOX_EXT, &val, - FPGAMAP_SIZE_16); + ihs_i2c_set(priv->map, write_mailbox_ext, val); #else I2C_SET_REG(write_mailbox_ext, val); #endif @@ -170,24 +167,27 @@ static int ihs_i2c_transfer(uchar chip, uchar *buffer, int len, bool read, | (is_last ? 0 : I2CMB_HOLD_BUS); #ifdef CONFIG_DM_I2C - fpgamap_write(fpga, priv->addr + REG_WRITE_MAILBOX, &data, - FPGAMAP_SIZE_16); + ihs_i2c_set(priv->map, write_mailbox, data); #else I2C_SET_REG(write_mailbox, data); #endif #ifdef CONFIG_DM_I2C - if (wait_for_int(dev, read)) + res = wait_for_int(dev, read); #else - if (wait_for_int(read)) + res = wait_for_int(read); #endif - return 1; + if (res) { + if (res == -ETIMEDOUT) + debug("%s: time out while waiting for event\n", __func__); + + return res; + } /* If we want to read, get the bytes from the mailbox */ if (read) { #ifdef CONFIG_DM_I2C - fpgamap_read(fpga, priv->addr + REG_READ_MAILBOX_EXT, &val, - FPGAMAP_SIZE_16); + ihs_i2c_get(priv->map, read_mailbox_ext, &val); #else I2C_GET_REG(read_mailbox_ext, &val); #endif @@ -206,19 +206,21 @@ static int ihs_i2c_send_buffer(uchar chip, u8 *data, int len, bool hold_bus, int read) #endif { + int res; + while (len) { int transfer = min(len, 2); bool is_last = len <= transfer; #ifdef CONFIG_DM_I2C - if (ihs_i2c_transfer(dev, chip, data, transfer, read, - hold_bus ? false : is_last)) - return 1; + res = ihs_i2c_transfer(dev, chip, data, transfer, read, + hold_bus ? false : is_last); #else - if (ihs_i2c_transfer(chip, data, transfer, read, - hold_bus ? false : is_last)) - return 1; + res = ihs_i2c_transfer(chip, data, transfer, read, + hold_bus ? false : is_last); #endif + if (res) + return res; data += transfer; len -= transfer; @@ -249,14 +251,19 @@ static int ihs_i2c_access(struct i2c_adapter *adap, uchar chip, u8 *addr, int alen, uchar *buffer, int len, int read) #endif { + int res; + /* Don't hold the bus if length of data to send/receive is zero */ + if (len <= 0) + return -EINVAL; + #ifdef CONFIG_DM_I2C - if (len <= 0 || ihs_i2c_address(dev, chip, addr, alen, len)) - return 1; + res = ihs_i2c_address(dev, chip, addr, alen, len); #else - if (len <= 0 || ihs_i2c_address(chip, addr, alen, len)) - return 1; + res = ihs_i2c_address(chip, addr, alen, len); #endif + if (res) + return res; #ifdef CONFIG_DM_I2C return ihs_i2c_send_buffer(dev, chip, buffer, len, false, read); @@ -270,11 +277,8 @@ static int ihs_i2c_access(struct i2c_adapter *adap, uchar chip, u8 *addr, int ihs_i2c_probe(struct udevice *bus) { struct ihs_i2c_priv *priv = dev_get_priv(bus); - int addr; - - addr = dev_read_u32_default(bus, "reg", -1); - priv->addr = addr; + regmap_init_mem(dev_ofnode(bus), &priv->map); return 0; } @@ -284,7 +288,7 @@ static int ihs_i2c_set_bus_speed(struct udevice *bus, uint speed) struct ihs_i2c_priv *priv = dev_get_priv(bus); if (speed != priv->speed && priv->speed != 0) - return 1; + return -EINVAL; priv->speed = speed; @@ -301,8 +305,8 @@ static int ihs_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs) * actucal data) or one message (just data) */ if (nmsgs > 2 || nmsgs == 0) { - debug("%s: Only one or two messages are supported.", __func__); - return -1; + debug("%s: Only one or two messages are supported\n", __func__); + return -ENOTSUPP; } omsg = nmsgs == 1 ? &dummy : msg; @@ -322,9 +326,11 @@ static int ihs_i2c_probe_chip(struct udevice *bus, u32 chip_addr, u32 chip_flags) { uchar buffer[2]; + int res; - if (ihs_i2c_transfer(bus, chip_addr, buffer, 0, I2COP_READ, true)) - return 1; + res = ihs_i2c_transfer(bus, chip_addr, buffer, 0, I2COP_READ, true); + if (res) + return res; return 0; } @@ -366,9 +372,11 @@ static void ihs_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr) static int ihs_i2c_probe(struct i2c_adapter *adap, uchar chip) { uchar buffer[2]; + int res; - if (ihs_i2c_transfer(chip, buffer, 0, I2COP_READ, true)) - return 1; + res = ihs_i2c_transfer(chip, buffer, 0, I2COP_READ, true); + if (res) + return res; return 0; } @@ -399,7 +407,7 @@ static unsigned int ihs_i2c_set_bus_speed(struct i2c_adapter *adap, unsigned int speed) { if (speed != adap->speed) - return 1; + return -EINVAL; return speed; } diff --git a/drivers/i2c/mvtwsi.c b/drivers/i2c/mvtwsi.c index 74ac0a4aa78..0a2dafcec6c 100644 --- a/drivers/i2c/mvtwsi.c +++ b/drivers/i2c/mvtwsi.c @@ -271,6 +271,17 @@ static int twsi_wait(struct mvtwsi_registers *twsi, int expected_status, do { control = readl(&twsi->control); if (control & MVTWSI_CONTROL_IFLG) { + /* + * On Armada 38x it seems that the controller works as + * if it first set the MVTWSI_CONTROL_IFLAG in the + * control register and only after that it changed the + * status register. + * This sometimes caused weird bugs which only appeared + * on selected I2C speeds and even then only sometimes. + * We therefore add here a simple ndealy(100), which + * seems to fix this weird bug. + */ + ndelay(100); status = readl(&twsi->status); if (status == expected_status) return 0; diff --git a/drivers/i2c/mxc_i2c.c b/drivers/i2c/mxc_i2c.c index 5420afbc8e0..23119cce65d 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,13 +552,34 @@ 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; } #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) @@ -566,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; @@ -574,6 +614,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 +935,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) { @@ -905,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) diff --git a/drivers/i2c/rk_i2c.c b/drivers/i2c/rk_i2c.c index f9a5796b96b..cdd94bb05a9 100644 --- a/drivers/i2c/rk_i2c.c +++ b/drivers/i2c/rk_i2c.c @@ -12,9 +12,9 @@ #include <errno.h> #include <i2c.h> #include <asm/io.h> -#include <asm/arch/clock.h> -#include <asm/arch/i2c.h> -#include <asm/arch/periph.h> +#include <asm/arch-rockchip/clock.h> +#include <asm/arch-rockchip/i2c.h> +#include <asm/arch-rockchip/periph.h> #include <dm/pinctrl.h> #include <linux/sizes.h> diff --git a/drivers/i2c/stm32f7_i2c.c b/drivers/i2c/stm32f7_i2c.c index 3872364d6bf..50c4fd0de23 100644 --- a/drivers/i2c/stm32f7_i2c.c +++ b/drivers/i2c/stm32f7_i2c.c @@ -500,7 +500,7 @@ static int stm32_i2c_compute_solutions(struct stm32_i2c_setup *setup, af_delay_max = setup->analog_filter ? STM32_I2C_ANALOG_FILTER_DELAY_MAX : 0; - sdadel_min = setup->fall_time - i2c_specs[setup->speed].hddat_min - + sdadel_min = i2c_specs[setup->speed].hddat_min + setup->fall_time - af_delay_min - (setup->dnf + 3) * i2cclk; sdadel_max = i2c_specs[setup->speed].vddat_max - setup->rise_time - @@ -540,8 +540,12 @@ static int stm32_i2c_compute_solutions(struct stm32_i2c_setup *setup, p_prev = p; list_add_tail(&v->node, solutions); + break; } } + + if (p_prev == p) + break; } } |