static irqreturn_t exynos5_i2c_irq()

in busses/i2c-exynos5.c [445:560]


static irqreturn_t exynos5_i2c_irq(int irqno, void *dev_id)
{
	struct exynos5_i2c *i2c = dev_id;
	u32 fifo_level, int_status, fifo_status, trans_status;
	unsigned char byte;
	int len = 0;

	i2c->state = -EINVAL;

	spin_lock(&i2c->lock);

	int_status = readl(i2c->regs + HSI2C_INT_STATUS);
	writel(int_status, i2c->regs + HSI2C_INT_STATUS);

	/* handle interrupt related to the transfer status */
	switch (i2c->variant->hw) {
	case I2C_TYPE_EXYNOSAUTOV9:
		fallthrough;
	case I2C_TYPE_EXYNOS7:
		if (int_status & HSI2C_INT_TRANS_DONE) {
			i2c->trans_done = 1;
			i2c->state = 0;
		} else if (int_status & HSI2C_INT_TRANS_ABORT) {
			dev_dbg(i2c->dev, "Deal with arbitration lose\n");
			i2c->state = -EAGAIN;
			goto stop;
		} else if (int_status & HSI2C_INT_NO_DEV_ACK) {
			dev_dbg(i2c->dev, "No ACK from device\n");
			i2c->state = -ENXIO;
			goto stop;
		} else if (int_status & HSI2C_INT_NO_DEV) {
			dev_dbg(i2c->dev, "No device\n");
			i2c->state = -ENXIO;
			goto stop;
		} else if (int_status & HSI2C_INT_TIMEOUT) {
			dev_dbg(i2c->dev, "Accessing device timed out\n");
			i2c->state = -ETIMEDOUT;
			goto stop;
		}

		break;
	case I2C_TYPE_EXYNOS5:
		if (!(int_status & HSI2C_INT_I2C))
			break;

		trans_status = readl(i2c->regs + HSI2C_TRANS_STATUS);
		if (trans_status & HSI2C_NO_DEV_ACK) {
			dev_dbg(i2c->dev, "No ACK from device\n");
			i2c->state = -ENXIO;
			goto stop;
		} else if (trans_status & HSI2C_NO_DEV) {
			dev_dbg(i2c->dev, "No device\n");
			i2c->state = -ENXIO;
			goto stop;
		} else if (trans_status & HSI2C_TRANS_ABORT) {
			dev_dbg(i2c->dev, "Deal with arbitration lose\n");
			i2c->state = -EAGAIN;
			goto stop;
		} else if (trans_status & HSI2C_TIMEOUT_AUTO) {
			dev_dbg(i2c->dev, "Accessing device timed out\n");
			i2c->state = -ETIMEDOUT;
			goto stop;
		} else if (trans_status & HSI2C_TRANS_DONE) {
			i2c->trans_done = 1;
			i2c->state = 0;
		}

		break;
	}

	if ((i2c->msg->flags & I2C_M_RD) && (int_status &
			(HSI2C_INT_TRAILING | HSI2C_INT_RX_ALMOSTFULL))) {
		fifo_status = readl(i2c->regs + HSI2C_FIFO_STATUS);
		fifo_level = HSI2C_RX_FIFO_LVL(fifo_status);
		len = min(fifo_level, i2c->msg->len - i2c->msg_ptr);

		while (len > 0) {
			byte = (unsigned char)
				readl(i2c->regs + HSI2C_RX_DATA);
			i2c->msg->buf[i2c->msg_ptr++] = byte;
			len--;
		}
		i2c->state = 0;
	} else if (int_status & HSI2C_INT_TX_ALMOSTEMPTY) {
		fifo_status = readl(i2c->regs + HSI2C_FIFO_STATUS);
		fifo_level = HSI2C_TX_FIFO_LVL(fifo_status);

		len = i2c->variant->fifo_depth - fifo_level;
		if (len > (i2c->msg->len - i2c->msg_ptr)) {
			u32 int_en = readl(i2c->regs + HSI2C_INT_ENABLE);

			int_en &= ~HSI2C_INT_TX_ALMOSTEMPTY_EN;
			writel(int_en, i2c->regs + HSI2C_INT_ENABLE);
			len = i2c->msg->len - i2c->msg_ptr;
		}

		while (len > 0) {
			byte = i2c->msg->buf[i2c->msg_ptr++];
			writel(byte, i2c->regs + HSI2C_TX_DATA);
			len--;
		}
		i2c->state = 0;
	}

 stop:
	if ((i2c->trans_done && (i2c->msg->len == i2c->msg_ptr)) ||
	    (i2c->state < 0)) {
		writel(0, i2c->regs + HSI2C_INT_ENABLE);
		exynos5_i2c_clr_pend_irq(i2c);
		complete(&i2c->msg_complete);
	}

	spin_unlock(&i2c->lock);

	return IRQ_HANDLED;
}