Commit e1d93266 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull i2c fixes from Wolfram Sang:

 - IMX: fix stop condition in single master mode and add compatible
   string for errata adherence

 - Microchip: Add support for proper repeated sends and fix unnecessary
   NAKs on empty messages, which caused false bus detection

* tag 'i2c-for-6.13-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux:
  i2c: microchip-core: fix "ghost" detections
  i2c: microchip-core: actually use repeated sends
  i2c: imx: add imx7d compatible string for applying erratum ERR007805
  i2c: imx: fix missing stop condition in single-master mode
parents 059dd502 f802f11b
Loading
Loading
Loading
Loading
+4 −5
Original line number Diff line number Diff line
@@ -335,6 +335,7 @@ static const struct of_device_id i2c_imx_dt_ids[] = {
	{ .compatible = "fsl,imx6sll-i2c", .data = &imx6_i2c_hwdata, },
	{ .compatible = "fsl,imx6sx-i2c", .data = &imx6_i2c_hwdata, },
	{ .compatible = "fsl,imx6ul-i2c", .data = &imx6_i2c_hwdata, },
	{ .compatible = "fsl,imx7d-i2c", .data = &imx6_i2c_hwdata, },
	{ .compatible = "fsl,imx7s-i2c", .data = &imx6_i2c_hwdata, },
	{ .compatible = "fsl,imx8mm-i2c", .data = &imx6_i2c_hwdata, },
	{ .compatible = "fsl,imx8mn-i2c", .data = &imx6_i2c_hwdata, },
@@ -532,22 +533,20 @@ static void i2c_imx_dma_free(struct imx_i2c_struct *i2c_imx)

static int i2c_imx_bus_busy(struct imx_i2c_struct *i2c_imx, int for_busy, bool atomic)
{
	bool multi_master = i2c_imx->multi_master;
	unsigned long orig_jiffies = jiffies;
	unsigned int temp;

	if (!i2c_imx->multi_master)
		return 0;

	while (1) {
		temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);

		/* check for arbitration lost */
		if (temp & I2SR_IAL) {
		if (multi_master && (temp & I2SR_IAL)) {
			i2c_imx_clear_irq(i2c_imx, I2SR_IAL);
			return -EAGAIN;
		}

		if (for_busy && (temp & I2SR_IBB)) {
		if (for_busy && (!multi_master || (temp & I2SR_IBB))) {
			i2c_imx->stopped = 0;
			break;
		}
+94 −28
Original line number Diff line number Diff line
@@ -93,27 +93,35 @@
 * @base:		pointer to register struct
 * @dev:		device reference
 * @i2c_clk:		clock reference for i2c input clock
 * @msg_queue:		pointer to the messages requiring sending
 * @buf:		pointer to msg buffer for easier use
 * @msg_complete:	xfer completion object
 * @adapter:		core i2c abstraction
 * @msg_err:		error code for completed message
 * @bus_clk_rate:	current i2c bus clock rate
 * @isr_status:		cached copy of local ISR status
 * @total_num:		total number of messages to be sent/received
 * @current_num:	index of the current message being sent/received
 * @msg_len:		number of bytes transferred in msg
 * @addr:		address of the current slave
 * @restart_needed:	whether or not a repeated start is required after current message
 */
struct mchp_corei2c_dev {
	void __iomem *base;
	struct device *dev;
	struct clk *i2c_clk;
	struct i2c_msg *msg_queue;
	u8 *buf;
	struct completion msg_complete;
	struct i2c_adapter adapter;
	int msg_err;
	int total_num;
	int current_num;
	u32 bus_clk_rate;
	u32 isr_status;
	u16 msg_len;
	u8 addr;
	bool restart_needed;
};

static void mchp_corei2c_core_disable(struct mchp_corei2c_dev *idev)
@@ -222,6 +230,47 @@ static int mchp_corei2c_fill_tx(struct mchp_corei2c_dev *idev)
	return 0;
}

static void mchp_corei2c_next_msg(struct mchp_corei2c_dev *idev)
{
	struct i2c_msg *this_msg;
	u8 ctrl;

	if (idev->current_num >= idev->total_num) {
		complete(&idev->msg_complete);
		return;
	}

	/*
	 * If there's been an error, the isr needs to return control
	 * to the "main" part of the driver, so as not to keep sending
	 * messages once it completes and clears the SI bit.
	 */
	if (idev->msg_err) {
		complete(&idev->msg_complete);
		return;
	}

	this_msg = idev->msg_queue++;

	if (idev->current_num < (idev->total_num - 1)) {
		struct i2c_msg *next_msg = idev->msg_queue;

		idev->restart_needed = next_msg->flags & I2C_M_RD;
	} else {
		idev->restart_needed = false;
	}

	idev->addr = i2c_8bit_addr_from_msg(this_msg);
	idev->msg_len = this_msg->len;
	idev->buf = this_msg->buf;

	ctrl = readb(idev->base + CORE_I2C_CTRL);
	ctrl |= CTRL_STA;
	writeb(ctrl, idev->base + CORE_I2C_CTRL);

	idev->current_num++;
}

static irqreturn_t mchp_corei2c_handle_isr(struct mchp_corei2c_dev *idev)
{
	u32 status = idev->isr_status;
@@ -238,8 +287,6 @@ static irqreturn_t mchp_corei2c_handle_isr(struct mchp_corei2c_dev *idev)
		ctrl &= ~CTRL_STA;
		writeb(idev->addr, idev->base + CORE_I2C_DATA);
		writeb(ctrl, idev->base + CORE_I2C_CTRL);
		if (idev->msg_len == 0)
			finished = true;
		break;
	case STATUS_M_ARB_LOST:
		idev->msg_err = -EAGAIN;
@@ -247,10 +294,14 @@ static irqreturn_t mchp_corei2c_handle_isr(struct mchp_corei2c_dev *idev)
		break;
	case STATUS_M_SLAW_ACK:
	case STATUS_M_TX_DATA_ACK:
		if (idev->msg_len > 0)
		if (idev->msg_len > 0) {
			mchp_corei2c_fill_tx(idev);
		} else {
			if (idev->restart_needed)
				finished = true;
			else
				last_byte = true;
		}
		break;
	case STATUS_M_TX_DATA_NACK:
	case STATUS_M_SLAR_NACK:
@@ -287,7 +338,7 @@ static irqreturn_t mchp_corei2c_handle_isr(struct mchp_corei2c_dev *idev)
		mchp_corei2c_stop(idev);

	if (last_byte || finished)
		complete(&idev->msg_complete);
		mchp_corei2c_next_msg(idev);

	return IRQ_HANDLED;
}
@@ -311,21 +362,48 @@ static irqreturn_t mchp_corei2c_isr(int irq, void *_dev)
	return ret;
}

static int mchp_corei2c_xfer_msg(struct mchp_corei2c_dev *idev,
				 struct i2c_msg *msg)
static int mchp_corei2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
			     int num)
{
	u8 ctrl;
	struct mchp_corei2c_dev *idev = i2c_get_adapdata(adap);
	struct i2c_msg *this_msg = msgs;
	unsigned long time_left;
	u8 ctrl;

	mchp_corei2c_core_enable(idev);

	idev->addr = i2c_8bit_addr_from_msg(msg);
	idev->msg_len = msg->len;
	idev->buf = msg->buf;
	/*
	 * The isr controls the flow of a transfer, this info needs to be saved
	 * to a location that it can access the queue information from.
	 */
	idev->restart_needed = false;
	idev->msg_queue = msgs;
	idev->total_num = num;
	idev->current_num = 0;

	/*
	 * But the first entry to the isr is triggered by the start in this
	 * function, so the first message needs to be "dequeued".
	 */
	idev->addr = i2c_8bit_addr_from_msg(this_msg);
	idev->msg_len = this_msg->len;
	idev->buf = this_msg->buf;
	idev->msg_err = 0;

	reinit_completion(&idev->msg_complete);
	if (idev->total_num > 1) {
		struct i2c_msg *next_msg = msgs + 1;

	mchp_corei2c_core_enable(idev);
		idev->restart_needed = next_msg->flags & I2C_M_RD;
	}

	idev->current_num++;
	idev->msg_queue++;

	reinit_completion(&idev->msg_complete);

	/*
	 * Send the first start to pass control to the isr
	 */
	ctrl = readb(idev->base + CORE_I2C_CTRL);
	ctrl |= CTRL_STA;
	writeb(ctrl, idev->base + CORE_I2C_CTRL);
@@ -335,20 +413,8 @@ static int mchp_corei2c_xfer_msg(struct mchp_corei2c_dev *idev,
	if (!time_left)
		return -ETIMEDOUT;

	if (idev->msg_err)
		return idev->msg_err;
}

static int mchp_corei2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
			     int num)
{
	struct mchp_corei2c_dev *idev = i2c_get_adapdata(adap);
	int i, ret;

	for (i = 0; i < num; i++) {
		ret = mchp_corei2c_xfer_msg(idev, msgs++);
		if (ret)
			return ret;
	}

	return num;
}