static int omap_i2c_xfer_data()

in busses/i2c-omap.c [1069:1199]


static int omap_i2c_xfer_data(struct omap_i2c_dev *omap)
{
	u16 bits;
	u16 stat;
	int err = 0, count = 0;

	do {
		bits = omap_i2c_read_reg(omap, OMAP_I2C_IE_REG);
		stat = omap_i2c_read_reg(omap, OMAP_I2C_STAT_REG);
		stat &= bits;

		/* If we're in receiver mode, ignore XDR/XRDY */
		if (omap->receiver)
			stat &= ~(OMAP_I2C_STAT_XDR | OMAP_I2C_STAT_XRDY);
		else
			stat &= ~(OMAP_I2C_STAT_RDR | OMAP_I2C_STAT_RRDY);

		if (!stat) {
			/* my work here is done */
			err = -EAGAIN;
			break;
		}

		dev_dbg(omap->dev, "IRQ (ISR = 0x%04x)\n", stat);
		if (count++ == 100) {
			dev_warn(omap->dev, "Too much work in one IRQ\n");
			break;
		}

		if (stat & OMAP_I2C_STAT_NACK) {
			err |= OMAP_I2C_STAT_NACK;
			omap_i2c_ack_stat(omap, OMAP_I2C_STAT_NACK);
		}

		if (stat & OMAP_I2C_STAT_AL) {
			dev_err(omap->dev, "Arbitration lost\n");
			err |= OMAP_I2C_STAT_AL;
			omap_i2c_ack_stat(omap, OMAP_I2C_STAT_AL);
		}

		/*
		 * ProDB0017052: Clear ARDY bit twice
		 */
		if (stat & OMAP_I2C_STAT_ARDY)
			omap_i2c_ack_stat(omap, OMAP_I2C_STAT_ARDY);

		if (stat & (OMAP_I2C_STAT_ARDY | OMAP_I2C_STAT_NACK |
					OMAP_I2C_STAT_AL)) {
			omap_i2c_ack_stat(omap, (OMAP_I2C_STAT_RRDY |
						OMAP_I2C_STAT_RDR |
						OMAP_I2C_STAT_XRDY |
						OMAP_I2C_STAT_XDR |
						OMAP_I2C_STAT_ARDY));
			break;
		}

		if (stat & OMAP_I2C_STAT_RDR) {
			u8 num_bytes = 1;

			if (omap->fifo_size)
				num_bytes = omap->buf_len;

			if (omap->errata & I2C_OMAP_ERRATA_I207) {
				i2c_omap_errata_i207(omap, stat);
				num_bytes = (omap_i2c_read_reg(omap,
					OMAP_I2C_BUFSTAT_REG) >> 8) & 0x3F;
			}

			omap_i2c_receive_data(omap, num_bytes, true);
			omap_i2c_ack_stat(omap, OMAP_I2C_STAT_RDR);
			continue;
		}

		if (stat & OMAP_I2C_STAT_RRDY) {
			u8 num_bytes = 1;

			if (omap->threshold)
				num_bytes = omap->threshold;

			omap_i2c_receive_data(omap, num_bytes, false);
			omap_i2c_ack_stat(omap, OMAP_I2C_STAT_RRDY);
			continue;
		}

		if (stat & OMAP_I2C_STAT_XDR) {
			u8 num_bytes = 1;
			int ret;

			if (omap->fifo_size)
				num_bytes = omap->buf_len;

			ret = omap_i2c_transmit_data(omap, num_bytes, true);
			if (ret < 0)
				break;

			omap_i2c_ack_stat(omap, OMAP_I2C_STAT_XDR);
			continue;
		}

		if (stat & OMAP_I2C_STAT_XRDY) {
			u8 num_bytes = 1;
			int ret;

			if (omap->threshold)
				num_bytes = omap->threshold;

			ret = omap_i2c_transmit_data(omap, num_bytes, false);
			if (ret < 0)
				break;

			omap_i2c_ack_stat(omap, OMAP_I2C_STAT_XRDY);
			continue;
		}

		if (stat & OMAP_I2C_STAT_ROVR) {
			dev_err(omap->dev, "Receive overrun\n");
			err |= OMAP_I2C_STAT_ROVR;
			omap_i2c_ack_stat(omap, OMAP_I2C_STAT_ROVR);
			break;
		}

		if (stat & OMAP_I2C_STAT_XUDF) {
			dev_err(omap->dev, "Transmit underflow\n");
			err |= OMAP_I2C_STAT_XUDF;
			omap_i2c_ack_stat(omap, OMAP_I2C_STAT_XUDF);
			break;
		}
	} while (stat);

	return err;
}