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

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
...@@ -371,31 +371,8 @@ static void bcm_iproc_i2c_read_valid_bytes(struct bcm_iproc_i2c_dev *iproc_i2c) ...@@ -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; struct i2c_msg *msg = iproc_i2c->msg;
unsigned int tx_bytes = msg->len - iproc_i2c->tx_bytes; unsigned int tx_bytes = msg->len - iproc_i2c->tx_bytes;
unsigned int i; unsigned int i;
...@@ -413,39 +390,47 @@ static irqreturn_t bcm_iproc_i2c_isr(int irq, void *data) ...@@ -413,39 +390,47 @@ static irqreturn_t bcm_iproc_i2c_isr(int irq, void *data)
if (idx == msg->len - 1) { if (idx == msg->len - 1) {
val |= BIT(M_TX_WR_STATUS_SHIFT); val |= BIT(M_TX_WR_STATUS_SHIFT);
if (iproc_i2c->irq) {
u32 tmp;
/* /*
* Since this is the last byte, we should * Since this is the last byte, we should now
* now disable TX FIFO underrun interrupt * disable TX FIFO underrun interrupt
*/ */
tmp = readl(iproc_i2c->base + IE_OFFSET); tmp = readl(iproc_i2c->base + IE_OFFSET);
tmp &= ~BIT(IE_M_TX_UNDERRUN_SHIFT); tmp &= ~BIT(IE_M_TX_UNDERRUN_SHIFT);
writel(tmp, iproc_i2c->base + IE_OFFSET); writel(tmp, iproc_i2c->base + IE_OFFSET);
} }
}
/* load data into TX FIFO */ /* load data into TX FIFO */
writel(val, iproc_i2c->base + M_TX_OFFSET); writel(val, iproc_i2c->base + M_TX_OFFSET);
} }
/* update number of transferred bytes */ /* update number of transferred bytes */
iproc_i2c->tx_bytes += tx_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; struct i2c_msg *msg = iproc_i2c->msg;
u32 bytes_left; u32 bytes_left, val;
bcm_iproc_i2c_read_valid_bytes(iproc_i2c); bcm_iproc_i2c_read_valid_bytes(iproc_i2c);
bytes_left = msg->len - iproc_i2c->rx_bytes; bytes_left = msg->len - iproc_i2c->rx_bytes;
if (bytes_left == 0) { if (bytes_left == 0) {
if (iproc_i2c->irq) {
/* finished reading all data, disable rx thld event */ /* finished reading all data, disable rx thld event */
tmp = readl(iproc_i2c->base + IE_OFFSET); val = readl(iproc_i2c->base + IE_OFFSET);
tmp &= ~BIT(IS_M_RX_THLD_SHIFT); val &= ~BIT(IS_M_RX_THLD_SHIFT);
writel(tmp, iproc_i2c->base + IE_OFFSET); writel(val, iproc_i2c->base + IE_OFFSET);
}
} else if (bytes_left < iproc_i2c->thld_bytes) { } else if (bytes_left < iproc_i2c->thld_bytes) {
/* set bytes left as threshold */ /* set bytes left as threshold */
tmp = readl(iproc_i2c->base + M_FIFO_CTRL_OFFSET); val = readl(iproc_i2c->base + M_FIFO_CTRL_OFFSET);
tmp &= ~(M_FIFO_RX_THLD_MASK << M_FIFO_RX_THLD_SHIFT); val &= ~(M_FIFO_RX_THLD_MASK << M_FIFO_RX_THLD_SHIFT);
tmp |= (bytes_left << M_FIFO_RX_THLD_SHIFT); val |= (bytes_left << M_FIFO_RX_THLD_SHIFT);
writel(tmp, iproc_i2c->base + M_FIFO_CTRL_OFFSET); writel(val, iproc_i2c->base + M_FIFO_CTRL_OFFSET);
iproc_i2c->thld_bytes = bytes_left; iproc_i2c->thld_bytes = bytes_left;
} }
/* /*
...@@ -453,13 +438,48 @@ static irqreturn_t bcm_iproc_i2c_isr(int irq, void *data) ...@@ -453,13 +438,48 @@ static irqreturn_t bcm_iproc_i2c_isr(int irq, void *data)
* hence no need to change the THRESHOLD SET. * hence no need to change the THRESHOLD SET.
* It will remain as iproc_i2c->thld_bytes itself * It will remain as iproc_i2c->thld_bytes itself
*/ */
} }
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)) { if (status & BIT(IS_M_START_BUSY_SHIFT)) {
iproc_i2c->xfer_is_done = 1; iproc_i2c->xfer_is_done = 1;
if (iproc_i2c->irq)
complete(&iproc_i2c->done); 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); writel(status, iproc_i2c->base + IS_OFFSET);
return IRQ_HANDLED; return IRQ_HANDLED;
...@@ -558,14 +578,71 @@ static int bcm_iproc_i2c_check_status(struct bcm_iproc_i2c_dev *iproc_i2c, ...@@ -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, static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
struct i2c_msg *msg) struct i2c_msg *msg)
{ {
int ret, i; int i;
u8 addr; u8 addr;
u32 val, tmp, val_intr_en; u32 val, tmp, val_intr_en;
unsigned int tx_bytes; unsigned int tx_bytes;
unsigned long time_left = msecs_to_jiffies(I2C_TIMEOUT_MSEC);
/* check if bus is busy */ /* check if bus is busy */
if (!!(readl(iproc_i2c->base + M_CMD_OFFSET) & 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, ...@@ -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 */ /* mark as incomplete before starting the transaction */
if (iproc_i2c->irq)
reinit_completion(&iproc_i2c->done); reinit_completion(&iproc_i2c->done);
iproc_i2c->xfer_is_done = 0; 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, ...@@ -645,39 +724,11 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
} else { } else {
val |= (M_CMD_PROTOCOL_BLK_WR << M_CMD_PROTOCOL_SHIFT); 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); if (iproc_i2c->irq)
writel(val_intr_en, iproc_i2c->base + IE_OFFSET);
/* 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;
}
return 0; return bcm_iproc_i2c_xfer_wait(iproc_i2c, msg, val);
} }
static int bcm_iproc_i2c_xfer(struct i2c_adapter *adapter, static int bcm_iproc_i2c_xfer(struct i2c_adapter *adapter,
...@@ -779,19 +830,22 @@ static int bcm_iproc_i2c_probe(struct platform_device *pdev) ...@@ -779,19 +830,22 @@ static int bcm_iproc_i2c_probe(struct platform_device *pdev)
return ret; return ret;
irq = platform_get_irq(pdev, 0); irq = platform_get_irq(pdev, 0);
if (irq <= 0) { if (irq > 0) {
dev_err(iproc_i2c->device, "no irq resource\n"); ret = devm_request_irq(iproc_i2c->device, irq,
return irq; bcm_iproc_i2c_isr, 0, pdev->name,
} iproc_i2c);
iproc_i2c->irq = irq;
ret = devm_request_irq(iproc_i2c->device, irq, bcm_iproc_i2c_isr, 0,
pdev->name, iproc_i2c);
if (ret < 0) { 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; 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); bcm_iproc_i2c_enable_disable(iproc_i2c, true);
adap = &iproc_i2c->adapter; adap = &iproc_i2c->adapter;
...@@ -809,10 +863,15 @@ static int bcm_iproc_i2c_remove(struct platform_device *pdev) ...@@ -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); 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); writel(0, iproc_i2c->base + IE_OFFSET);
readl(iproc_i2c->base + IE_OFFSET); readl(iproc_i2c->base + IE_OFFSET);
synchronize_irq(iproc_i2c->irq); synchronize_irq(iproc_i2c->irq);
}
i2c_del_adapter(&iproc_i2c->adapter); i2c_del_adapter(&iproc_i2c->adapter);
bcm_iproc_i2c_enable_disable(iproc_i2c, false); bcm_iproc_i2c_enable_disable(iproc_i2c, false);
...@@ -826,10 +885,15 @@ static int bcm_iproc_i2c_suspend(struct device *dev) ...@@ -826,10 +885,15 @@ static int bcm_iproc_i2c_suspend(struct device *dev)
{ {
struct bcm_iproc_i2c_dev *iproc_i2c = dev_get_drvdata(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); writel(0, iproc_i2c->base + IE_OFFSET);
readl(iproc_i2c->base + IE_OFFSET); readl(iproc_i2c->base + IE_OFFSET);
synchronize_irq(iproc_i2c->irq); synchronize_irq(iproc_i2c->irq);
}
/* now disable the controller */ /* now disable the controller */
bcm_iproc_i2c_enable_disable(iproc_i2c, false); bcm_iproc_i2c_enable_disable(iproc_i2c, false);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment