diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c index 51aab662050b..e905734c26a0 100644 --- a/drivers/i2c/busses/i2c-bcm-iproc.c +++ b/drivers/i2c/busses/i2c-bcm-iproc.c @@ -316,26 +316,44 @@ static void bcm_iproc_i2c_slave_init( iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, val); } -static void bcm_iproc_i2c_check_slave_status( - struct bcm_iproc_i2c_dev *iproc_i2c) +static bool bcm_iproc_i2c_check_slave_status + (struct bcm_iproc_i2c_dev *iproc_i2c, u32 status) { u32 val; + bool recover = false; - val = iproc_i2c_rd_reg(iproc_i2c, S_CMD_OFFSET); - /* status is valid only when START_BUSY is cleared after it was set */ - if (val & BIT(S_CMD_START_BUSY_SHIFT)) - return; + /* check slave transmit status only if slave is transmitting */ + if (!iproc_i2c->slave_rx_only) { + val = iproc_i2c_rd_reg(iproc_i2c, S_CMD_OFFSET); + /* status is valid only when START_BUSY is cleared */ + if (!(val & BIT(S_CMD_START_BUSY_SHIFT))) { + val = (val >> S_CMD_STATUS_SHIFT) & S_CMD_STATUS_MASK; + if (val == S_CMD_STATUS_TIMEOUT || + val == S_CMD_STATUS_MASTER_ABORT) { + dev_warn(iproc_i2c->device, + (val == S_CMD_STATUS_TIMEOUT) ? + "slave random stretch time timeout\n" : + "Master aborted read transaction\n"); + recover = true; + } + } + } - val = (val >> S_CMD_STATUS_SHIFT) & S_CMD_STATUS_MASK; - if (val == S_CMD_STATUS_TIMEOUT || val == S_CMD_STATUS_MASTER_ABORT) { - dev_err(iproc_i2c->device, (val == S_CMD_STATUS_TIMEOUT) ? - "slave random stretch time timeout\n" : - "Master aborted read transaction\n"); + /* RX_EVENT is not valid when START_BUSY is set */ + if ((status & BIT(IS_S_RX_EVENT_SHIFT)) && + (status & BIT(IS_S_START_BUSY_SHIFT))) { + dev_warn(iproc_i2c->device, "Slave aborted read transaction\n"); + recover = true; + } + + if (recover) { /* re-initialize i2c for recovery */ bcm_iproc_i2c_enable_disable(iproc_i2c, false); bcm_iproc_i2c_slave_init(iproc_i2c, true); bcm_iproc_i2c_enable_disable(iproc_i2c, true); } + + return recover; } static void bcm_iproc_i2c_slave_read(struct bcm_iproc_i2c_dev *iproc_i2c) @@ -420,6 +438,64 @@ static bool bcm_iproc_i2c_slave_isr(struct bcm_iproc_i2c_dev *iproc_i2c, u32 val; u8 value; + + if (status & BIT(IS_S_TX_UNDERRUN_SHIFT)) { + iproc_i2c->tx_underrun++; + if (iproc_i2c->tx_underrun == 1) + /* Start of SMBUS for Master Read */ + i2c_slave_event(iproc_i2c->slave, + I2C_SLAVE_READ_REQUESTED, + &value); + else + /* Master read other than start */ + i2c_slave_event(iproc_i2c->slave, + I2C_SLAVE_READ_PROCESSED, + &value); + + iproc_i2c_wr_reg(iproc_i2c, S_TX_OFFSET, value); + /* start transfer */ + val = BIT(S_CMD_START_BUSY_SHIFT); + iproc_i2c_wr_reg(iproc_i2c, S_CMD_OFFSET, val); + + /* clear interrupt */ + iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, + BIT(IS_S_TX_UNDERRUN_SHIFT)); + } + + /* Stop received from master in case of master read transaction */ + if (status & BIT(IS_S_START_BUSY_SHIFT)) { + /* + * Disable interrupt for TX FIFO becomes empty and + * less than PKT_LENGTH bytes were output on the SMBUS + */ + iproc_i2c->slave_int_mask &= ~BIT(IE_S_TX_UNDERRUN_SHIFT); + val = iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET); + val &= ~BIT(IE_S_TX_UNDERRUN_SHIFT); + iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, val); + + /* End of SMBUS for Master Read */ + val = BIT(S_TX_WR_STATUS_SHIFT); + iproc_i2c_wr_reg(iproc_i2c, S_TX_OFFSET, val); + + val = BIT(S_CMD_START_BUSY_SHIFT); + iproc_i2c_wr_reg(iproc_i2c, S_CMD_OFFSET, val); + + /* flush TX FIFOs */ + val = iproc_i2c_rd_reg(iproc_i2c, S_FIFO_CTRL_OFFSET); + val |= (BIT(S_FIFO_TX_FLUSH_SHIFT)); + iproc_i2c_wr_reg(iproc_i2c, S_FIFO_CTRL_OFFSET, val); + + i2c_slave_event(iproc_i2c->slave, I2C_SLAVE_STOP, &value); + + /* clear interrupt */ + iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, + BIT(IS_S_START_BUSY_SHIFT)); + } + + /* if the controller has been reset, immediately return from the ISR */ + if (bcm_iproc_i2c_check_slave_status(iproc_i2c, status)) + return true; + /* * Slave events in case of master-write, master-write-read and, * master-read @@ -453,72 +529,13 @@ static bool bcm_iproc_i2c_slave_isr(struct bcm_iproc_i2c_dev *iproc_i2c, /* schedule tasklet to read data later */ tasklet_schedule(&iproc_i2c->slave_rx_tasklet); - /* - * clear only IS_S_RX_EVENT_SHIFT and - * IS_S_RX_FIFO_FULL_SHIFT interrupt. - */ - val = BIT(IS_S_RX_EVENT_SHIFT); - if (status & BIT(IS_S_RX_FIFO_FULL_SHIFT)) - val |= BIT(IS_S_RX_FIFO_FULL_SHIFT); - iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, val); + /* clear IS_S_RX_FIFO_FULL_SHIFT interrupt */ + if (status & BIT(IS_S_RX_FIFO_FULL_SHIFT)) { + val = BIT(IS_S_RX_FIFO_FULL_SHIFT); + iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, val); + } } - if (status & BIT(IS_S_TX_UNDERRUN_SHIFT)) { - iproc_i2c->tx_underrun++; - if (iproc_i2c->tx_underrun == 1) - /* Start of SMBUS for Master Read */ - i2c_slave_event(iproc_i2c->slave, - I2C_SLAVE_READ_REQUESTED, - &value); - else - /* Master read other than start */ - i2c_slave_event(iproc_i2c->slave, - I2C_SLAVE_READ_PROCESSED, - &value); - - iproc_i2c_wr_reg(iproc_i2c, S_TX_OFFSET, value); - /* start transfer */ - val = BIT(S_CMD_START_BUSY_SHIFT); - iproc_i2c_wr_reg(iproc_i2c, S_CMD_OFFSET, val); - - /* clear interrupt */ - iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, - BIT(IS_S_TX_UNDERRUN_SHIFT)); - } - - /* Stop received from master in case of master read transaction */ - if (status & BIT(IS_S_START_BUSY_SHIFT)) { - /* - * Disable interrupt for TX FIFO becomes empty and - * less than PKT_LENGTH bytes were output on the SMBUS - */ - iproc_i2c->slave_int_mask &= ~BIT(IE_S_TX_UNDERRUN_SHIFT); - iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, - iproc_i2c->slave_int_mask); - - /* End of SMBUS for Master Read */ - val = BIT(S_TX_WR_STATUS_SHIFT); - iproc_i2c_wr_reg(iproc_i2c, S_TX_OFFSET, val); - - val = BIT(S_CMD_START_BUSY_SHIFT); - iproc_i2c_wr_reg(iproc_i2c, S_CMD_OFFSET, val); - - /* flush TX FIFOs */ - val = iproc_i2c_rd_reg(iproc_i2c, S_FIFO_CTRL_OFFSET); - val |= (BIT(S_FIFO_TX_FLUSH_SHIFT)); - iproc_i2c_wr_reg(iproc_i2c, S_FIFO_CTRL_OFFSET, val); - - i2c_slave_event(iproc_i2c->slave, I2C_SLAVE_STOP, &value); - - /* clear interrupt */ - iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, - BIT(IS_S_START_BUSY_SHIFT)); - } - - /* check slave transmit status only if slave is transmitting */ - if (!iproc_i2c->slave_rx_only) - bcm_iproc_i2c_check_slave_status(iproc_i2c); - return true; } diff --git a/drivers/i2c/busses/i2c-cp2615.c b/drivers/i2c/busses/i2c-cp2615.c index 20f8f7c9a8cd..cf3747d87034 100644 --- a/drivers/i2c/busses/i2c-cp2615.c +++ b/drivers/i2c/busses/i2c-cp2615.c @@ -85,7 +85,7 @@ static int cp2615_init_iop_msg(struct cp2615_iop_msg *ret, enum cp2615_iop_msg_t if (!ret) return -EINVAL; - ret->preamble = 0x2A2A; + ret->preamble = htons(0x2A2AU); ret->length = htons(data_len + 6); ret->msg = htons(msg); if (data && data_len) diff --git a/drivers/i2c/busses/i2c-designware-master.c b/drivers/i2c/busses/i2c-designware-master.c index ca1035e010c7..85dbd0eb5392 100644 --- a/drivers/i2c/busses/i2c-designware-master.c +++ b/drivers/i2c/busses/i2c-designware-master.c @@ -519,10 +519,16 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev) /* * Because we don't know the buffer length in the - * I2C_FUNC_SMBUS_BLOCK_DATA case, we can't stop - * the transaction here. + * I2C_FUNC_SMBUS_BLOCK_DATA case, we can't stop the + * transaction here. Also disable the TX_EMPTY IRQ + * while waiting for the data length byte to avoid the + * bogus interrupts flood. */ - if (buf_len > 0 || flags & I2C_M_RECV_LEN) { + if (flags & I2C_M_RECV_LEN) { + dev->status |= STATUS_WRITE_IN_PROGRESS; + intr_mask &= ~DW_IC_INTR_TX_EMPTY; + break; + } else if (buf_len > 0) { /* more bytes to be written */ dev->status |= STATUS_WRITE_IN_PROGRESS; break; @@ -558,6 +564,13 @@ i2c_dw_recv_len(struct dw_i2c_dev *dev, u8 len) msgs[dev->msg_read_idx].len = len; msgs[dev->msg_read_idx].flags &= ~I2C_M_RECV_LEN; + /* + * Received buffer length, re-enable TX_EMPTY interrupt + * to resume the SMBUS transaction. + */ + regmap_update_bits(dev->map, DW_IC_INTR_MASK, DW_IC_INTR_TX_EMPTY, + DW_IC_INTR_TX_EMPTY); + return len; } diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c index a91201509bc1..8b7e599f1674 100644 --- a/drivers/i2c/i2c-dev.c +++ b/drivers/i2c/i2c-dev.c @@ -450,8 +450,8 @@ static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (rdwr_arg.nmsgs > I2C_RDWR_IOCTL_MAX_MSGS) return -EINVAL; - rdwr_pa = memdup_user(rdwr_arg.msgs, - rdwr_arg.nmsgs * sizeof(struct i2c_msg)); + rdwr_pa = memdup_array_user(rdwr_arg.msgs, + rdwr_arg.nmsgs, sizeof(struct i2c_msg)); if (IS_ERR(rdwr_pa)) return PTR_ERR(rdwr_pa);