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: Rayagonda Kokatanur <rayagonda.kokatanur@broadcom.com> Signed-off-by: Ray Jui <ray.jui@broadcom.com> Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
This commit is contained in:
parent
682587080d
commit
3f98ad45e5
|
@ -371,13 +371,98 @@ static void bcm_iproc_i2c_read_valid_bytes(struct bcm_iproc_i2c_dev *iproc_i2c)
|
|||
}
|
||||
}
|
||||
|
||||
static void bcm_iproc_i2c_send(struct bcm_iproc_i2c_dev *iproc_i2c)
|
||||
{
|
||||
struct i2c_msg *msg = iproc_i2c->msg;
|
||||
unsigned int tx_bytes = msg->len - iproc_i2c->tx_bytes;
|
||||
unsigned int i;
|
||||
u32 val;
|
||||
|
||||
/* can only fill up to the FIFO size */
|
||||
tx_bytes = min_t(unsigned int, tx_bytes, M_TX_RX_FIFO_SIZE);
|
||||
for (i = 0; i < tx_bytes; i++) {
|
||||
/* start from where we left over */
|
||||
unsigned int idx = iproc_i2c->tx_bytes + i;
|
||||
|
||||
val = msg->buf[idx];
|
||||
|
||||
/* mark the last byte */
|
||||
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
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
static void bcm_iproc_i2c_read(struct bcm_iproc_i2c_dev *iproc_i2c)
|
||||
{
|
||||
struct i2c_msg *msg = iproc_i2c->msg;
|
||||
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 */
|
||||
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 */
|
||||
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;
|
||||
}
|
||||
/*
|
||||
* bytes_left >= iproc_i2c->thld_bytes,
|
||||
* hence no need to change the THRESHOLD SET.
|
||||
* 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)) {
|
||||
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);
|
||||
u32 tmp;
|
||||
|
||||
|
||||
bool ret;
|
||||
u32 sl_status = status & ISR_MASK_SLAVE;
|
||||
|
||||
|
@ -390,76 +475,11 @@ static irqreturn_t bcm_iproc_i2c_isr(int irq, void *data)
|
|||
}
|
||||
|
||||
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;
|
||||
u32 val;
|
||||
|
||||
/* can only fill up to the FIFO size */
|
||||
tx_bytes = min_t(unsigned int, tx_bytes, M_TX_RX_FIFO_SIZE);
|
||||
for (i = 0; i < tx_bytes; i++) {
|
||||
/* start from where we left over */
|
||||
unsigned int idx = iproc_i2c->tx_bytes + i;
|
||||
|
||||
val = msg->buf[idx];
|
||||
|
||||
/* mark the last byte */
|
||||
if (idx == msg->len - 1) {
|
||||
val |= BIT(M_TX_WR_STATUS_SHIFT);
|
||||
|
||||
/*
|
||||
* 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)) {
|
||||
struct i2c_msg *msg = iproc_i2c->msg;
|
||||
u32 bytes_left;
|
||||
|
||||
bcm_iproc_i2c_read_valid_bytes(iproc_i2c);
|
||||
bytes_left = msg->len - iproc_i2c->rx_bytes;
|
||||
if (bytes_left == 0) {
|
||||
/* 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);
|
||||
} 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);
|
||||
iproc_i2c->thld_bytes = bytes_left;
|
||||
}
|
||||
/*
|
||||
* bytes_left >= iproc_i2c->thld_bytes,
|
||||
* hence no need to change the THRESHOLD SET.
|
||||
* It will remain as iproc_i2c->thld_bytes itself
|
||||
*/
|
||||
}
|
||||
|
||||
if (status & BIT(IS_M_START_BUSY_SHIFT)) {
|
||||
iproc_i2c->xfer_is_done = 1;
|
||||
complete(&iproc_i2c->done);
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
reinit_completion(&iproc_i2c->done);
|
||||
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);
|
||||
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,
|
||||
|
@ -779,17 +830,20 @@ 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;
|
||||
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);
|
||||
return ret;
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
|
@ -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 */
|
||||
writel(0, iproc_i2c->base + IE_OFFSET);
|
||||
readl(iproc_i2c->base + IE_OFFSET);
|
||||
synchronize_irq(iproc_i2c->irq);
|
||||
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 */
|
||||
writel(0, iproc_i2c->base + IE_OFFSET);
|
||||
readl(iproc_i2c->base + IE_OFFSET);
|
||||
synchronize_irq(iproc_i2c->irq);
|
||||
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);
|
||||
|
|
Loading…
Reference in New Issue