Commit 3f98ad45 authored by Rayagonda Kokatanur's avatar Rayagonda Kokatanur Committed by Wolfram Sang
Browse files

i2c: iproc: add polling support



Add polling support to the iProc I2C driver. Polling mode is
activated when the driver fails to obtain an interrupt ID from device
tree

Signed-off-by: default avatarRayagonda Kokatanur <rayagonda.kokatanur@broadcom.com>
Signed-off-by: default avatarRay Jui <ray.jui@broadcom.com>
Signed-off-by: default avatarWolfram Sang <wsa@the-dreams.de>
parent 68258708
Loading
Loading
Loading
Loading
+181 −117
Original line number Diff line number Diff line
@@ -371,31 +371,8 @@ static void bcm_iproc_i2c_read_valid_bytes(struct bcm_iproc_i2c_dev *iproc_i2c)
	}
}

static irqreturn_t bcm_iproc_i2c_isr(int irq, void *data)
static void bcm_iproc_i2c_send(struct bcm_iproc_i2c_dev *iproc_i2c)
{
	struct bcm_iproc_i2c_dev *iproc_i2c = data;
	u32 status = readl(iproc_i2c->base + IS_OFFSET);
	u32 tmp;


	bool ret;
	u32 sl_status = status & ISR_MASK_SLAVE;

	if (sl_status) {
		ret = bcm_iproc_i2c_slave_isr(iproc_i2c, sl_status);
		if (ret)
			return IRQ_HANDLED;
		else
			return IRQ_NONE;
	}

	status &= ISR_MASK;

	if (!status)
		return IRQ_NONE;

	/* TX FIFO is empty and we have more data to send */
	if (status & BIT(IS_M_TX_UNDERRUN_SHIFT)) {
	struct i2c_msg *msg = iproc_i2c->msg;
	unsigned int tx_bytes = msg->len - iproc_i2c->tx_bytes;
	unsigned int i;
@@ -413,39 +390,47 @@ static irqreturn_t bcm_iproc_i2c_isr(int irq, void *data)
		if (idx == msg->len - 1) {
			val |= BIT(M_TX_WR_STATUS_SHIFT);

			if (iproc_i2c->irq) {
				u32 tmp;

				/*
				 * Since this is the last byte, we should
				 * now disable TX FIFO underrun interrupt
				 * Since this is the last byte, we should now
				 * disable TX FIFO underrun interrupt
				 */
				tmp = readl(iproc_i2c->base + IE_OFFSET);
				tmp &= ~BIT(IE_M_TX_UNDERRUN_SHIFT);
				writel(tmp, iproc_i2c->base + IE_OFFSET);
			}
		}

		/* load data into TX FIFO */
		writel(val, iproc_i2c->base + M_TX_OFFSET);
	}

	/* update number of transferred bytes */
	iproc_i2c->tx_bytes += tx_bytes;
}

	if (status & BIT(IS_M_RX_THLD_SHIFT)) {
static void bcm_iproc_i2c_read(struct bcm_iproc_i2c_dev *iproc_i2c)
{
	struct i2c_msg *msg = iproc_i2c->msg;
		u32 bytes_left;
	u32 bytes_left, val;

	bcm_iproc_i2c_read_valid_bytes(iproc_i2c);
	bytes_left = msg->len - iproc_i2c->rx_bytes;
	if (bytes_left == 0) {
		if (iproc_i2c->irq) {
			/* finished reading all data, disable rx thld event */
			tmp = readl(iproc_i2c->base + IE_OFFSET);
			tmp &= ~BIT(IS_M_RX_THLD_SHIFT);
			writel(tmp, iproc_i2c->base + IE_OFFSET);
			val = readl(iproc_i2c->base + IE_OFFSET);
			val &= ~BIT(IS_M_RX_THLD_SHIFT);
			writel(val, iproc_i2c->base + IE_OFFSET);
		}
	} else if (bytes_left < iproc_i2c->thld_bytes) {
		/* set bytes left as threshold */
			tmp = readl(iproc_i2c->base + M_FIFO_CTRL_OFFSET);
			tmp &= ~(M_FIFO_RX_THLD_MASK << M_FIFO_RX_THLD_SHIFT);
			tmp |= (bytes_left << M_FIFO_RX_THLD_SHIFT);
			writel(tmp, iproc_i2c->base + M_FIFO_CTRL_OFFSET);
		val = readl(iproc_i2c->base + M_FIFO_CTRL_OFFSET);
		val &= ~(M_FIFO_RX_THLD_MASK << M_FIFO_RX_THLD_SHIFT);
		val |= (bytes_left << M_FIFO_RX_THLD_SHIFT);
		writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET);
		iproc_i2c->thld_bytes = bytes_left;
	}
	/*
@@ -455,11 +440,46 @@ static irqreturn_t bcm_iproc_i2c_isr(int irq, void *data)
	 */
}

static void bcm_iproc_i2c_process_m_event(struct bcm_iproc_i2c_dev *iproc_i2c,
					  u32 status)
{
	/* TX FIFO is empty and we have more data to send */
	if (status & BIT(IS_M_TX_UNDERRUN_SHIFT))
		bcm_iproc_i2c_send(iproc_i2c);

	/* RX FIFO threshold is reached and data needs to be read out */
	if (status & BIT(IS_M_RX_THLD_SHIFT))
		bcm_iproc_i2c_read(iproc_i2c);

	/* transfer is done */
	if (status & BIT(IS_M_START_BUSY_SHIFT)) {
		iproc_i2c->xfer_is_done = 1;
		if (iproc_i2c->irq)
			complete(&iproc_i2c->done);
	}
}

static irqreturn_t bcm_iproc_i2c_isr(int irq, void *data)
{
	struct bcm_iproc_i2c_dev *iproc_i2c = data;
	u32 status = readl(iproc_i2c->base + IS_OFFSET);
	bool ret;
	u32 sl_status = status & ISR_MASK_SLAVE;

	if (sl_status) {
		ret = bcm_iproc_i2c_slave_isr(iproc_i2c, sl_status);
		if (ret)
			return IRQ_HANDLED;
		else
			return IRQ_NONE;
	}

	status &= ISR_MASK;
	if (!status)
		return IRQ_NONE;

	/* process all master based events */
	bcm_iproc_i2c_process_m_event(iproc_i2c, status);
	writel(status, iproc_i2c->base + IS_OFFSET);

	return IRQ_HANDLED;
@@ -558,14 +578,71 @@ static int bcm_iproc_i2c_check_status(struct bcm_iproc_i2c_dev *iproc_i2c,
	}
}

static int bcm_iproc_i2c_xfer_wait(struct bcm_iproc_i2c_dev *iproc_i2c,
				   struct i2c_msg *msg,
				   u32 cmd)
{
	unsigned long time_left = msecs_to_jiffies(I2C_TIMEOUT_MSEC);
	u32 val, status;
	int ret;

	writel(cmd, iproc_i2c->base + M_CMD_OFFSET);

	if (iproc_i2c->irq) {
		time_left = wait_for_completion_timeout(&iproc_i2c->done,
							time_left);
		/* disable all interrupts */
		writel(0, iproc_i2c->base + IE_OFFSET);
		/* read it back to flush the write */
		readl(iproc_i2c->base + IE_OFFSET);
		/* make sure the interrupt handler isn't running */
		synchronize_irq(iproc_i2c->irq);

	} else { /* polling mode */
		unsigned long timeout = jiffies + time_left;

		do {
			status = readl(iproc_i2c->base + IS_OFFSET) & ISR_MASK;
			bcm_iproc_i2c_process_m_event(iproc_i2c, status);
			writel(status, iproc_i2c->base + IS_OFFSET);

			if (time_after(jiffies, timeout)) {
				time_left = 0;
				break;
			}

			cpu_relax();
			cond_resched();
		} while (!iproc_i2c->xfer_is_done);
	}

	if (!time_left && !iproc_i2c->xfer_is_done) {
		dev_err(iproc_i2c->device, "transaction timed out\n");

		/* flush both TX/RX FIFOs */
		val = BIT(M_FIFO_RX_FLUSH_SHIFT) | BIT(M_FIFO_TX_FLUSH_SHIFT);
		writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET);
		return -ETIMEDOUT;
	}

	ret = bcm_iproc_i2c_check_status(iproc_i2c, msg);
	if (ret) {
		/* flush both TX/RX FIFOs */
		val = BIT(M_FIFO_RX_FLUSH_SHIFT) | BIT(M_FIFO_TX_FLUSH_SHIFT);
		writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET);
		return ret;
	}

	return 0;
}

static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
					 struct i2c_msg *msg)
{
	int ret, i;
	int i;
	u8 addr;
	u32 val, tmp, val_intr_en;
	unsigned int tx_bytes;
	unsigned long time_left = msecs_to_jiffies(I2C_TIMEOUT_MSEC);

	/* check if bus is busy */
	if (!!(readl(iproc_i2c->base + M_CMD_OFFSET) &
@@ -600,7 +677,9 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
	}

	/* mark as incomplete before starting the transaction */
	if (iproc_i2c->irq)
		reinit_completion(&iproc_i2c->done);

	iproc_i2c->xfer_is_done = 0;

	/*
@@ -645,39 +724,11 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
	} else {
		val |= (M_CMD_PROTOCOL_BLK_WR << M_CMD_PROTOCOL_SHIFT);
	}
	writel(val_intr_en, iproc_i2c->base + IE_OFFSET);
	writel(val, iproc_i2c->base + M_CMD_OFFSET);

	time_left = wait_for_completion_timeout(&iproc_i2c->done, time_left);

	/* disable all interrupts */
	writel(0, iproc_i2c->base + IE_OFFSET);
	/* read it back to flush the write */
	readl(iproc_i2c->base + IE_OFFSET);

	/* make sure the interrupt handler isn't running */
	synchronize_irq(iproc_i2c->irq);

	if (!time_left && !iproc_i2c->xfer_is_done) {
		dev_err(iproc_i2c->device, "transaction timed out\n");

		/* flush FIFOs */
		val = (1 << M_FIFO_RX_FLUSH_SHIFT) |
		      (1 << M_FIFO_TX_FLUSH_SHIFT);
		writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET);
		return -ETIMEDOUT;
	}

	ret = bcm_iproc_i2c_check_status(iproc_i2c, msg);
	if (ret) {
		/* flush both TX/RX FIFOs */
		val = (1 << M_FIFO_RX_FLUSH_SHIFT) |
		      (1 << M_FIFO_TX_FLUSH_SHIFT);
		writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET);
		return ret;
	}
	if (iproc_i2c->irq)
		writel(val_intr_en, iproc_i2c->base + IE_OFFSET);

	return 0;
	return bcm_iproc_i2c_xfer_wait(iproc_i2c, msg, val);
}

static int bcm_iproc_i2c_xfer(struct i2c_adapter *adapter,
@@ -779,19 +830,22 @@ static int bcm_iproc_i2c_probe(struct platform_device *pdev)
		return ret;

	irq = platform_get_irq(pdev, 0);
	if (irq <= 0) {
		dev_err(iproc_i2c->device, "no irq resource\n");
		return irq;
	}
	iproc_i2c->irq = irq;

	ret = devm_request_irq(iproc_i2c->device, irq, bcm_iproc_i2c_isr, 0,
			       pdev->name, iproc_i2c);
	if (irq > 0) {
		ret = devm_request_irq(iproc_i2c->device, irq,
				       bcm_iproc_i2c_isr, 0, pdev->name,
				       iproc_i2c);
		if (ret < 0) {
		dev_err(iproc_i2c->device, "unable to request irq %i\n", irq);
			dev_err(iproc_i2c->device,
				"unable to request irq %i\n", irq);
			return ret;
		}

		iproc_i2c->irq = irq;
	} else {
		dev_warn(iproc_i2c->device,
			 "no irq resource, falling back to poll mode\n");
	}

	bcm_iproc_i2c_enable_disable(iproc_i2c, true);

	adap = &iproc_i2c->adapter;
@@ -809,10 +863,15 @@ static int bcm_iproc_i2c_remove(struct platform_device *pdev)
{
	struct bcm_iproc_i2c_dev *iproc_i2c = platform_get_drvdata(pdev);

	/* make sure there's no pending interrupt when we remove the adapter */
	if (iproc_i2c->irq) {
		/*
		 * Make sure there's no pending interrupt when we remove the
		 * adapter
		 */
		writel(0, iproc_i2c->base + IE_OFFSET);
		readl(iproc_i2c->base + IE_OFFSET);
		synchronize_irq(iproc_i2c->irq);
	}

	i2c_del_adapter(&iproc_i2c->adapter);
	bcm_iproc_i2c_enable_disable(iproc_i2c, false);
@@ -826,10 +885,15 @@ static int bcm_iproc_i2c_suspend(struct device *dev)
{
	struct bcm_iproc_i2c_dev *iproc_i2c = dev_get_drvdata(dev);

	/* make sure there's no pending interrupt when we go into suspend */
	if (iproc_i2c->irq) {
		/*
		 * Make sure there's no pending interrupt when we go into
		 * suspend
		 */
		writel(0, iproc_i2c->base + IE_OFFSET);
		readl(iproc_i2c->base + IE_OFFSET);
		synchronize_irq(iproc_i2c->irq);
	}

	/* now disable the controller */
	bcm_iproc_i2c_enable_disable(iproc_i2c, false);