Commit f66062c7 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull i2c fixes from Wolfram Sang:
 "I2C has another set of driver bugfixes, mostly for the stm32f7 driver"

* 'i2c/for-current' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux:
  i2c: rk3x: Handle a spurious start completion interrupt flag
  i2c: stm32f7: use proper DMAENGINE API for termination
  i2c: stm32f7: stop dma transfer in case of NACK
  i2c: stm32f7: recover the bus on access timeout
  i2c: stm32f7: flush TX FIFO upon transfer errors
  i2c: cbus-gpio: set atomic transfer callback
parents a44f27e4 02fe0fbd
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -196,6 +196,7 @@ static u32 cbus_i2c_func(struct i2c_adapter *adapter)

static const struct i2c_algorithm cbus_i2c_algo = {
	.smbus_xfer		= cbus_i2c_smbus_xfer,
	.smbus_xfer_atomic	= cbus_i2c_smbus_xfer,
	.functionality		= cbus_i2c_func,
};

+2 −2
Original line number Diff line number Diff line
@@ -423,8 +423,8 @@ static void rk3x_i2c_handle_read(struct rk3x_i2c *i2c, unsigned int ipd)
	if (!(ipd & REG_INT_MBRF))
		return;

	/* ack interrupt */
	i2c_writel(i2c, REG_INT_MBRF, REG_IPD);
	/* ack interrupt (read also produces a spurious START flag, clear it too) */
	i2c_writel(i2c, REG_INT_MBRF | REG_INT_START, REG_IPD);

	/* Can only handle a maximum of 32 bytes at a time */
	if (len > 32)
+38 −7
Original line number Diff line number Diff line
@@ -1493,6 +1493,7 @@ static irqreturn_t stm32f7_i2c_isr_event(int irq, void *data)
{
	struct stm32f7_i2c_dev *i2c_dev = data;
	struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg;
	struct stm32_i2c_dma *dma = i2c_dev->dma;
	void __iomem *base = i2c_dev->base;
	u32 status, mask;
	int ret = IRQ_HANDLED;
@@ -1518,6 +1519,10 @@ static irqreturn_t stm32f7_i2c_isr_event(int irq, void *data)
		dev_dbg(i2c_dev->dev, "<%s>: Receive NACK (addr %x)\n",
			__func__, f7_msg->addr);
		writel_relaxed(STM32F7_I2C_ICR_NACKCF, base + STM32F7_I2C_ICR);
		if (i2c_dev->use_dma) {
			stm32f7_i2c_disable_dma_req(i2c_dev);
			dmaengine_terminate_async(dma->chan_using);
		}
		f7_msg->result = -ENXIO;
	}

@@ -1533,7 +1538,7 @@ static irqreturn_t stm32f7_i2c_isr_event(int irq, void *data)
		/* Clear STOP flag */
		writel_relaxed(STM32F7_I2C_ICR_STOPCF, base + STM32F7_I2C_ICR);

		if (i2c_dev->use_dma) {
		if (i2c_dev->use_dma && !f7_msg->result) {
			ret = IRQ_WAKE_THREAD;
		} else {
			i2c_dev->master_mode = false;
@@ -1546,7 +1551,7 @@ static irqreturn_t stm32f7_i2c_isr_event(int irq, void *data)
		if (f7_msg->stop) {
			mask = STM32F7_I2C_CR2_STOP;
			stm32f7_i2c_set_bits(base + STM32F7_I2C_CR2, mask);
		} else if (i2c_dev->use_dma) {
		} else if (i2c_dev->use_dma && !f7_msg->result) {
			ret = IRQ_WAKE_THREAD;
		} else if (f7_msg->smbus) {
			stm32f7_i2c_smbus_rep_start(i2c_dev);
@@ -1583,7 +1588,7 @@ static irqreturn_t stm32f7_i2c_isr_event_thread(int irq, void *data)
	if (!ret) {
		dev_dbg(i2c_dev->dev, "<%s>: Timed out\n", __func__);
		stm32f7_i2c_disable_dma_req(i2c_dev);
		dmaengine_terminate_all(dma->chan_using);
		dmaengine_terminate_async(dma->chan_using);
		f7_msg->result = -ETIMEDOUT;
	}

@@ -1660,7 +1665,7 @@ static irqreturn_t stm32f7_i2c_isr_error(int irq, void *data)
	/* Disable dma */
	if (i2c_dev->use_dma) {
		stm32f7_i2c_disable_dma_req(i2c_dev);
		dmaengine_terminate_all(dma->chan_using);
		dmaengine_terminate_async(dma->chan_using);
	}

	i2c_dev->master_mode = false;
@@ -1696,12 +1701,26 @@ static int stm32f7_i2c_xfer(struct i2c_adapter *i2c_adap,
	time_left = wait_for_completion_timeout(&i2c_dev->complete,
						i2c_dev->adap.timeout);
	ret = f7_msg->result;
	if (ret) {
		if (i2c_dev->use_dma)
			dmaengine_synchronize(dma->chan_using);

		/*
		 * It is possible that some unsent data have already been
		 * written into TXDR. To avoid sending old data in a
		 * further transfer, flush TXDR in case of any error
		 */
		writel_relaxed(STM32F7_I2C_ISR_TXE,
			       i2c_dev->base + STM32F7_I2C_ISR);
		goto pm_free;
	}

	if (!time_left) {
		dev_dbg(i2c_dev->dev, "Access to slave 0x%x timed out\n",
			i2c_dev->msg->addr);
		if (i2c_dev->use_dma)
			dmaengine_terminate_all(dma->chan_using);
			dmaengine_terminate_sync(dma->chan_using);
		stm32f7_i2c_wait_free_bus(i2c_dev);
		ret = -ETIMEDOUT;
	}

@@ -1744,13 +1763,25 @@ static int stm32f7_i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
	timeout = wait_for_completion_timeout(&i2c_dev->complete,
					      i2c_dev->adap.timeout);
	ret = f7_msg->result;
	if (ret)
	if (ret) {
		if (i2c_dev->use_dma)
			dmaengine_synchronize(dma->chan_using);

		/*
		 * It is possible that some unsent data have already been
		 * written into TXDR. To avoid sending old data in a
		 * further transfer, flush TXDR in case of any error
		 */
		writel_relaxed(STM32F7_I2C_ISR_TXE,
			       i2c_dev->base + STM32F7_I2C_ISR);
		goto pm_free;
	}

	if (!timeout) {
		dev_dbg(dev, "Access to slave 0x%x timed out\n", f7_msg->addr);
		if (i2c_dev->use_dma)
			dmaengine_terminate_all(dma->chan_using);
			dmaengine_terminate_sync(dma->chan_using);
		stm32f7_i2c_wait_free_bus(i2c_dev);
		ret = -ETIMEDOUT;
		goto pm_free;
	}