From: Anders Berg <anders.b...@lsi.com> Add support for the I2C_M_RECV_LEN flag to enable SMBus block data transfers.
This patch also fixes an issue where the master failed to perform a proper repeated start, but instead issued a stop followed by a start. This behaviour confused some SMBus slave devices. The STOP-START was caused by the use of the automatic mode of the controller. By using manual mode the driver gets control over when a STOP condition is signalled. Signed-off-by: Anders Berg <anders.b...@lsi.com> --- drivers/i2c/busses/i2c-axxia.c | 220 +++++++++++++++++++++++++++------------- 1 file changed, 151 insertions(+), 69 deletions(-) diff --git a/drivers/i2c/busses/i2c-axxia.c b/drivers/i2c/busses/i2c-axxia.c index 4f86418..e0a4b24 100644 --- a/drivers/i2c/busses/i2c-axxia.c +++ b/drivers/i2c/busses/i2c-axxia.c @@ -27,7 +27,8 @@ #include <linux/module.h> #define SCL_WAIT_TIMEOUT_NS 25000000 -#define I2C_TIMEOUT (msecs_to_jiffies(1000)) +#define I2C_XFER_TIMEOUT (msecs_to_jiffies(500)) +#define I2C_STOP_TIMEOUT (msecs_to_jiffies(100)) #define TX_FIFO_SIZE 8 #define RX_FIFO_SIZE 8 @@ -123,12 +124,10 @@ struct axxia_i2c_dev { int irq; /* xfer completion object */ struct completion msg_complete; - /* pointer to current message data */ - u8 *msg_buf; - /* size of unsent data in the message buffer */ - size_t msg_buf_remaining; - /* identifies read transfers */ - int msg_read; + /* pointer to current message */ + struct i2c_msg *msg; + /* number of bytes transferred in msg */ + size_t msg_xfrd; /* error code for completed message */ int msg_err; /* current i2c bus clock rate */ @@ -168,10 +167,21 @@ axxia_i2c_init(struct axxia_i2c_dev *idev) u32 t_setup; u32 tmo_clk; u32 prescale; + unsigned long timeout; dev_dbg(idev->dev, "rate=%uHz per_clk=%uMHz -> ratio=1:%u\n", idev->bus_clk_rate, clk_mhz, divisor); + /* Reset controller */ + writel(0x01, &idev->regs->soft_reset); + timeout = jiffies + msecs_to_jiffies(100); + while (readl(&idev->regs->soft_reset) & 1) { + if (time_after(jiffies, timeout)) { + dev_warn(idev->dev, "Soft reset failed\n"); + break; + } + } + /* Enable Master Mode */ writel(0x1, &idev->regs->global_control); @@ -186,8 +196,8 @@ axxia_i2c_init(struct axxia_i2c_dev *idev) /* SDA Setup Time */ writel(t_setup, &idev->regs->sda_setup_time); - /* SDA Hold Time, 5ns */ - writel(ns_to_clk(5, clk_mhz), &idev->regs->sda_hold_time); + /* SDA Hold Time, 300ns */ + writel(ns_to_clk(300, clk_mhz), &idev->regs->sda_hold_time); /* Filter <50ns spikes */ writel(ns_to_clk(50, clk_mhz), &idev->regs->spike_fltr_len); @@ -195,15 +205,16 @@ axxia_i2c_init(struct axxia_i2c_dev *idev) tmo_clk = ns_to_clk(SCL_WAIT_TIMEOUT_NS, clk_mhz); /* - * Find the prescaler value that makes tmo_clk fit in 15-bits counter. + Find the prescaler value that makes tmo_clk fit in 15-bits counter. */ - for (prescale = 0; prescale < 15; ++prescale) { + for (prescale=0; prescale < 15; ++prescale) { if (tmo_clk <= 0x7fff) break; tmo_clk >>= 1; } - if (tmo_clk > 0x7fff) + if (tmo_clk > 0x7fff) { tmo_clk = 0x7fff; + } /* Prescale divider (log2) */ writel(prescale, &idev->regs->timer_clock_div); @@ -228,15 +239,40 @@ axxia_i2c_init(struct axxia_i2c_dev *idev) } static int -axxia_i2c_empty_rx_fifo(struct axxia_i2c_dev *idev) +i2c_m_rd(const struct i2c_msg *msg) { - size_t rx_fifo_avail = readl(&idev->regs->mst_rx_fifo); - int bytes_to_transfer = min(rx_fifo_avail, idev->msg_buf_remaining); + return (msg->flags & I2C_M_RD) != 0; +} - idev->msg_buf_remaining -= bytes_to_transfer; +static int +i2c_m_recv_len(const struct i2c_msg *msg) +{ + return (msg->flags & I2C_M_RECV_LEN) != 0; +} - while (0 < bytes_to_transfer--) - *idev->msg_buf++ = readl(&idev->regs->mst_data); +static int +axxia_i2c_empty_rx_fifo(struct axxia_i2c_dev *idev) +{ + struct i2c_msg *msg = idev->msg; + size_t rx_fifo_avail = readl(&idev->regs->mst_rx_fifo); + int bytes_to_transfer = min(rx_fifo_avail, msg->len - idev->msg_xfrd); + + while (0 < bytes_to_transfer--) { + int c = readl(&idev->regs->mst_data); + if (idev->msg_xfrd == 0 && i2c_m_recv_len(msg)) { + if (c == 0 || c > I2C_SMBUS_BLOCK_MAX) { + idev->msg_err = -EPROTO; + i2c_int_disable(idev, ~0); + dev_err(idev->dev, + "invalid SMBus block size (%d)\n", c); + complete(&idev->msg_complete); + break; + } + msg->len += c; + writel(msg->len, &idev->regs->mst_rx_xfer); + } + msg->buf[idev->msg_xfrd++] = c; + } return 0; } @@ -244,17 +280,44 @@ axxia_i2c_empty_rx_fifo(struct axxia_i2c_dev *idev) static int axxia_i2c_fill_tx_fifo(struct axxia_i2c_dev *idev) { + struct i2c_msg *msg = idev->msg; size_t tx_fifo_avail = TX_FIFO_SIZE - readl(&idev->regs->mst_tx_fifo); - int bytes_to_transfer = min(tx_fifo_avail, idev->msg_buf_remaining); - - idev->msg_buf_remaining -= bytes_to_transfer; + int bytes_to_transfer = min(tx_fifo_avail, msg->len - idev->msg_xfrd); while (0 < bytes_to_transfer--) - writel(*idev->msg_buf++, &idev->regs->mst_data); + writel(msg->buf[idev->msg_xfrd++], &idev->regs->mst_data); return 0; } +static char * +status_str(u32 status) +{ + static char buf[128]; + + buf[0] = '\0'; + + if (status & MST_STATUS_RFL) + strcat(buf, "RFL "); + if (status & MST_STATUS_TFL) + strcat(buf, "TFL "); + if (status & MST_STATUS_SNS) + strcat(buf, "SNS "); + if (status & MST_STATUS_SS) + strcat(buf, "SS "); + if (status & MST_STATUS_SCC) + strcat(buf, "SCC "); + if (status & MST_STATUS_TSS) + strcat(buf, "TSS "); + if (status & MST_STATUS_AL) + strcat(buf, "AL "); + if (status & MST_STATUS_ND) + strcat(buf, "ND "); + if (status & MST_STATUS_NA) + strcat(buf, "NA "); + return buf; +} + static irqreturn_t axxia_i2c_isr(int irq, void *_dev) { @@ -264,11 +327,11 @@ axxia_i2c_isr(int irq, void *_dev) /* Clear interrupt */ writel(0x01, &idev->regs->interrupt_status); - if (status & MST_STATUS_ERR) { + if (unlikely(status & MST_STATUS_ERR)) { idev->msg_err = status & MST_STATUS_ERR; i2c_int_disable(idev, ~0); - dev_err(idev->dev, "error %#x, rx=%u/%u tx=%u/%u\n", - idev->msg_err, + dev_err(idev->dev, "error %s, rx=%u/%u tx=%u/%u\n", + status_str(idev->msg_err), readl(&idev->regs->mst_rx_bytes_xfrd), readl(&idev->regs->mst_rx_xfer), readl(&idev->regs->mst_tx_bytes_xfrd), @@ -277,24 +340,28 @@ axxia_i2c_isr(int irq, void *_dev) return IRQ_HANDLED; } + /* Stop completed? */ + if (status & MST_STATUS_SCC) { + i2c_int_disable(idev, ~0); + complete(&idev->msg_complete); + } + /* Transfer done? */ if (status & (MST_STATUS_SNS | MST_STATUS_SS)) { - if (idev->msg_read && idev->msg_buf_remaining > 0) + if (i2c_m_rd(idev->msg) && idev->msg_xfrd < idev->msg->len) axxia_i2c_empty_rx_fifo(idev); - WARN_ON(idev->msg_buf_remaining > 0); i2c_int_disable(idev, ~0); complete(&idev->msg_complete); } /* RX FIFO needs service? */ - if (idev->msg_read && (status & MST_STATUS_RFL)) { - WARN_ON(idev->msg_buf_remaining == 0); + if (i2c_m_rd(idev->msg) && (status & MST_STATUS_RFL)) { axxia_i2c_empty_rx_fifo(idev); } /* TX FIFO needs service? */ - if (!idev->msg_read && (status & MST_STATUS_TFL)) { - if (idev->msg_buf_remaining) + if (!i2c_m_rd(idev->msg) && (status & MST_STATUS_TFL)) { + if (idev->msg_xfrd < idev->msg->len) axxia_i2c_fill_tx_fifo(idev); else i2c_int_disable(idev, MST_STATUS_TFL); @@ -303,80 +370,91 @@ axxia_i2c_isr(int irq, void *_dev) return IRQ_HANDLED; } + static int -axxia_i2c_xfer_msg(struct axxia_i2c_dev *idev, struct i2c_msg *msg, int stop) +axxia_i2c_xfer_msg(struct axxia_i2c_dev *idev, struct i2c_msg *msg) { - u32 int_mask; + u32 int_mask = MST_STATUS_ERR | MST_STATUS_SNS; int ret; - dev_dbg(idev->dev, "xfer_msg: chip=%#x, buffer=[%02x %02x %02x %02x], len=%d, stop=%d\n", - msg->addr, msg->buf[0], msg->buf[1], msg->buf[2], msg->buf[3], - msg->len, stop); - if (msg->len == 0 || msg->len > 255) return -EINVAL; - idev->msg_buf = msg->buf; - idev->msg_buf_remaining = msg->len; - idev->msg_err = 0; - idev->msg_read = (msg->flags & I2C_M_RD); + idev->msg = msg; + idev->msg_xfrd = 0; + idev->msg_err = 0; INIT_COMPLETION(idev->msg_complete); - if (msg->flags & I2C_M_RD) { + if (i2c_m_rd(msg)) { /* TX 0 bytes */ writel(0, &idev->regs->mst_tx_xfer); /* RX # bytes */ writel(msg->len, &idev->regs->mst_rx_xfer); /* Chip address for write */ - writel(CHIP_READ(msg->addr & 0xfe), &idev->regs->mst_addr_1); + writel(CHIP_READ(msg->addr), &idev->regs->mst_addr_1); } else { /* TX # bytes */ writel(msg->len, &idev->regs->mst_tx_xfer); /* RX 0 bytes */ writel(0, &idev->regs->mst_rx_xfer); /* Chip address for write */ - writel(CHIP_WRITE(msg->addr & 0xfe), &idev->regs->mst_addr_1); + writel(CHIP_WRITE(msg->addr), &idev->regs->mst_addr_1); } writel(msg->addr >> 8, &idev->regs->mst_addr_2); - if (!(msg->flags & I2C_M_RD)) - axxia_i2c_fill_tx_fifo(idev); - - int_mask = MST_STATUS_ERR; - int_mask |= stop ? MST_STATUS_SS : MST_STATUS_SNS; - if (msg->flags & I2C_M_RD) + if (i2c_m_rd(msg)) { int_mask |= MST_STATUS_RFL; - else if (idev->msg_buf_remaining) - int_mask |= MST_STATUS_TFL; + } else { + axxia_i2c_fill_tx_fifo(idev); + if (idev->msg_xfrd < msg->len) + int_mask |= MST_STATUS_TFL; + } /* Start manual mode */ - writel(stop ? 0x9 : 0x8, &idev->regs->mst_command); + writel(0x8, &idev->regs->mst_command); i2c_int_enable(idev, int_mask); - ret = wait_for_completion_timeout(&idev->msg_complete, I2C_TIMEOUT); + ret = wait_for_completion_timeout(&idev->msg_complete, I2C_XFER_TIMEOUT); i2c_int_disable(idev, int_mask); + WARN_ON(readl(&idev->regs->mst_command) & 0x8); + if (WARN_ON(ret == 0)) { - dev_warn(idev->dev, "i2c transfer timed out\n"); - /* Reset i2c controller and re-initialize */ - writel(0x01, &idev->regs->soft_reset); - while (readl(&idev->regs->soft_reset) & 1) - cpu_relax(); + dev_warn(idev->dev, "xfer timeout (%#x)\n", msg->addr); axxia_i2c_init(idev); return -ETIMEDOUT; } - WARN_ON(readl(&idev->regs->mst_command) & 0x8); + if (unlikely(idev->msg_err != 0)) { + axxia_i2c_init(idev); + return -EIO; + } + + return 0; +} - dev_dbg(idev->dev, "transfer complete: %d %d %#x\n", - ret, completion_done(&idev->msg_complete), idev->msg_err); +static int +axxia_i2c_stop(struct axxia_i2c_dev *idev) +{ + u32 int_mask = MST_STATUS_ERR | MST_STATUS_SCC; + int ret; - if (likely(idev->msg_err == 0)) - return 0; + INIT_COMPLETION(idev->msg_complete); - return -EIO; + /* Issue stop */ + writel(0xb, &idev->regs->mst_command); + i2c_int_enable(idev, int_mask); + ret = wait_for_completion_timeout(&idev->msg_complete, I2C_STOP_TIMEOUT); + i2c_int_disable(idev, int_mask); + if (ret == 0) { + return -ETIMEDOUT; + } + + WARN_ON(readl(&idev->regs->mst_command) & 0x8); + + return 0; } static int @@ -387,17 +465,21 @@ axxia_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) int ret = 0; for (i = 0; ret == 0 && i < num; i++) { - int stop = (i == num-1); - ret = axxia_i2c_xfer_msg(idev, &msgs[i], stop); + ret = axxia_i2c_xfer_msg(idev, &msgs[i]); } + axxia_i2c_stop(idev); + return ret ?: i; } static u32 axxia_i2c_func(struct i2c_adapter *adap) { - return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | I2C_FUNC_SMBUS_EMUL; + return (I2C_FUNC_I2C | + I2C_FUNC_10BIT_ADDR | + I2C_FUNC_SMBUS_EMUL | + I2C_FUNC_SMBUS_BLOCK_DATA); } @@ -445,7 +527,7 @@ axxia_i2c_probe(struct platform_device *pdev) } idev->base = base; - idev->regs = (struct __iomem i2c_regs*)base; + idev->regs = (struct __iomem i2c_regs *) base; idev->i2c_clk = i2c_clk; idev->dev = &pdev->dev; init_completion(&idev->msg_complete); -- 1.7.9.5 _______________________________________________ linux-yocto mailing list linux-yocto@yoctoproject.org https://lists.yoctoproject.org/listinfo/linux-yocto