Commit ace92fd9 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull more i2c updates from Wolfram Sang:
 "This contains one patch which slipped through the cracks (iproc), a
  core sanitizing improvement as the new memdup_array_user() helper went
  upstream (i2c-dev), and two driver bugfixes (designware, cp2615)"

* tag 'for-6.7-rc1-part2' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux:
  i2c: cp2615: Fix 'assignment to __be16' warning
  i2c: dev: copy userspace array safely
  i2c: designware: Disable TX_EMPTY irq while waiting for block length byte
  i2c: iproc: handle invalid slave state
parents 12418ece bdba49cb
Loading
Loading
Loading
Loading
+75 −58
Original line number Diff line number Diff line
@@ -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;

	/* 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 after it was set */
	if (val & BIT(S_CMD_START_BUSY_SHIFT))
		return;

		/* 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_err(iproc_i2c->device, (val == S_CMD_STATUS_TIMEOUT) ?
			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;
			}
		}
	}

	/* 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,48 +438,6 @@ static bool bcm_iproc_i2c_slave_isr(struct bcm_iproc_i2c_dev *iproc_i2c,
	u32 val;
	u8 value;

	/*
	 * Slave events in case of master-write, master-write-read and,
	 * master-read
	 *
	 * Master-write     : only IS_S_RX_EVENT_SHIFT event
	 * Master-write-read: both IS_S_RX_EVENT_SHIFT and IS_S_RD_EVENT_SHIFT
	 *                    events
	 * Master-read      : both IS_S_RX_EVENT_SHIFT and IS_S_RD_EVENT_SHIFT
	 *                    events or only IS_S_RD_EVENT_SHIFT
	 *
	 * iproc has a slave rx fifo size of 64 bytes. Rx fifo full interrupt
	 * (IS_S_RX_FIFO_FULL_SHIFT) will be generated when RX fifo becomes
	 * full. This can happen if Master issues write requests of more than
	 * 64 bytes.
	 */
	if (status & BIT(IS_S_RX_EVENT_SHIFT) ||
	    status & BIT(IS_S_RD_EVENT_SHIFT) ||
	    status & BIT(IS_S_RX_FIFO_FULL_SHIFT)) {
		/* disable slave interrupts */
		val = iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET);
		val &= ~iproc_i2c->slave_int_mask;
		iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, val);

		if (status & BIT(IS_S_RD_EVENT_SHIFT))
			/* Master-write-read request */
			iproc_i2c->slave_rx_only = false;
		else
			/* Master-write request only */
			iproc_i2c->slave_rx_only = true;

		/* 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);
	}

	if (status & BIT(IS_S_TX_UNDERRUN_SHIFT)) {
		iproc_i2c->tx_underrun++;
@@ -493,8 +469,9 @@ static bool bcm_iproc_i2c_slave_isr(struct bcm_iproc_i2c_dev *iproc_i2c,
		 * 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);
		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);
@@ -515,9 +492,49 @@ static bool bcm_iproc_i2c_slave_isr(struct bcm_iproc_i2c_dev *iproc_i2c,
				 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);
	/* 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
	 *
	 * Master-write     : only IS_S_RX_EVENT_SHIFT event
	 * Master-write-read: both IS_S_RX_EVENT_SHIFT and IS_S_RD_EVENT_SHIFT
	 *                    events
	 * Master-read      : both IS_S_RX_EVENT_SHIFT and IS_S_RD_EVENT_SHIFT
	 *                    events or only IS_S_RD_EVENT_SHIFT
	 *
	 * iproc has a slave rx fifo size of 64 bytes. Rx fifo full interrupt
	 * (IS_S_RX_FIFO_FULL_SHIFT) will be generated when RX fifo becomes
	 * full. This can happen if Master issues write requests of more than
	 * 64 bytes.
	 */
	if (status & BIT(IS_S_RX_EVENT_SHIFT) ||
	    status & BIT(IS_S_RD_EVENT_SHIFT) ||
	    status & BIT(IS_S_RX_FIFO_FULL_SHIFT)) {
		/* disable slave interrupts */
		val = iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET);
		val &= ~iproc_i2c->slave_int_mask;
		iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, val);

		if (status & BIT(IS_S_RD_EVENT_SHIFT))
			/* Master-write-read request */
			iproc_i2c->slave_rx_only = false;
		else
			/* Master-write request only */
			iproc_i2c->slave_rx_only = true;

		/* schedule tasklet to read data later */
		tasklet_schedule(&iproc_i2c->slave_rx_tasklet);

		/* 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);
		}
	}

	return true;
}
+1 −1
Original line number Diff line number Diff line
@@ -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)
+16 −3
Original line number Diff line number Diff line
@@ -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;
}

+2 −2
Original line number Diff line number Diff line
@@ -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);