in busses/i2c-nomadik.c [724:874]
static irqreturn_t i2c_irq_handler(int irq, void *arg)
{
struct nmk_i2c_dev *dev = arg;
u32 tft, rft;
u32 count;
u32 misr, src;
/* load Tx FIFO and Rx FIFO threshold values */
tft = readl(dev->virtbase + I2C_TFTR);
rft = readl(dev->virtbase + I2C_RFTR);
/* read interrupt status register */
misr = readl(dev->virtbase + I2C_MISR);
src = __ffs(misr);
switch ((1 << src)) {
/* Transmit FIFO nearly empty interrupt */
case I2C_IT_TXFNE:
{
if (dev->cli.operation == I2C_READ) {
/*
* in read operation why do we care for writing?
* so disable the Transmit FIFO interrupt
*/
disable_interrupts(dev, I2C_IT_TXFNE);
} else {
fill_tx_fifo(dev, (MAX_I2C_FIFO_THRESHOLD - tft));
/*
* if done, close the transfer by disabling the
* corresponding TXFNE interrupt
*/
if (dev->cli.count == 0)
disable_interrupts(dev, I2C_IT_TXFNE);
}
}
break;
/*
* Rx FIFO nearly full interrupt.
* This is set when the numer of entries in Rx FIFO is
* greater or equal than the threshold value programmed
* in RFT
*/
case I2C_IT_RXFNF:
for (count = rft; count > 0; count--) {
/* Read the Rx FIFO */
*dev->cli.buffer = readb(dev->virtbase + I2C_RFR);
dev->cli.buffer++;
}
dev->cli.count -= rft;
dev->cli.xfer_bytes += rft;
break;
/* Rx FIFO full */
case I2C_IT_RXFF:
for (count = MAX_I2C_FIFO_THRESHOLD; count > 0; count--) {
*dev->cli.buffer = readb(dev->virtbase + I2C_RFR);
dev->cli.buffer++;
}
dev->cli.count -= MAX_I2C_FIFO_THRESHOLD;
dev->cli.xfer_bytes += MAX_I2C_FIFO_THRESHOLD;
break;
/* Master Transaction Done with/without stop */
case I2C_IT_MTD:
case I2C_IT_MTDWS:
if (dev->cli.operation == I2C_READ) {
while (!(readl(dev->virtbase + I2C_RISR)
& I2C_IT_RXFE)) {
if (dev->cli.count == 0)
break;
*dev->cli.buffer =
readb(dev->virtbase + I2C_RFR);
dev->cli.buffer++;
dev->cli.count--;
dev->cli.xfer_bytes++;
}
}
disable_all_interrupts(dev);
clear_all_interrupts(dev);
if (dev->cli.count) {
dev->result = -EIO;
dev_err(&dev->adev->dev,
"%lu bytes still remain to be xfered\n",
dev->cli.count);
(void) init_hw(dev);
}
complete(&dev->xfer_complete);
break;
/* Master Arbitration lost interrupt */
case I2C_IT_MAL:
dev->result = -EIO;
(void) init_hw(dev);
i2c_set_bit(dev->virtbase + I2C_ICR, I2C_IT_MAL);
complete(&dev->xfer_complete);
break;
/*
* Bus Error interrupt.
* This happens when an unexpected start/stop condition occurs
* during the transaction.
*/
case I2C_IT_BERR:
dev->result = -EIO;
/* get the status */
if (((readl(dev->virtbase + I2C_SR) >> 2) & 0x3) == I2C_ABORT)
(void) init_hw(dev);
i2c_set_bit(dev->virtbase + I2C_ICR, I2C_IT_BERR);
complete(&dev->xfer_complete);
break;
/*
* Tx FIFO overrun interrupt.
* This is set when a write operation in Tx FIFO is performed and
* the Tx FIFO is full.
*/
case I2C_IT_TXFOVR:
dev->result = -EIO;
(void) init_hw(dev);
dev_err(&dev->adev->dev, "Tx Fifo Over run\n");
complete(&dev->xfer_complete);
break;
/* unhandled interrupts by this driver - TODO*/
case I2C_IT_TXFE:
case I2C_IT_TXFF:
case I2C_IT_RXFE:
case I2C_IT_RFSR:
case I2C_IT_RFSE:
case I2C_IT_WTSR:
case I2C_IT_STD:
dev_err(&dev->adev->dev, "unhandled Interrupt\n");
break;
default:
dev_err(&dev->adev->dev, "spurious Interrupt..\n");
break;
}
return IRQ_HANDLED;
}