static irqreturn_t jz4780_i2c_irq()

in busses/i2c-jz4780.c [434:555]


static irqreturn_t jz4780_i2c_irq(int irqno, void *dev_id)
{
	unsigned short tmp;
	unsigned short intst;
	unsigned short intmsk;
	struct jz4780_i2c *i2c = dev_id;

	spin_lock(&i2c->lock);
	intmsk = jz4780_i2c_readw(i2c, JZ4780_I2C_INTM);
	intst = jz4780_i2c_readw(i2c, JZ4780_I2C_INTST);

	intst &= intmsk;

	if (intst & JZ4780_I2C_INTST_TXABT) {
		jz4780_i2c_trans_done(i2c);
		goto done;
	}

	if (intst & JZ4780_I2C_INTST_RXOF) {
		dev_dbg(&i2c->adap.dev, "received fifo overflow!\n");
		jz4780_i2c_trans_done(i2c);
		goto done;
	}

	/*
	 * When reading, always drain RX FIFO before we send more Read
	 * Commands to avoid fifo overrun
	 */
	if (i2c->is_write == 0) {
		int rd_left;

		while ((jz4780_i2c_readw(i2c, JZ4780_I2C_STA)
				  & JZ4780_I2C_STA_RFNE)) {
			*(i2c->rbuf++) = jz4780_i2c_readw(i2c, JZ4780_I2C_DC)
					 & 0xff;
			i2c->rd_data_xfered++;
			if (i2c->rd_data_xfered == i2c->rd_total_len) {
				jz4780_i2c_trans_done(i2c);
				goto done;
			}
		}

		rd_left = i2c->rd_total_len - i2c->rd_data_xfered;

		if (rd_left <= i2c->cdata->fifosize)
			jz4780_i2c_writew(i2c, JZ4780_I2C_RXTL, rd_left - 1);
	}

	if (intst & JZ4780_I2C_INTST_TXEMP) {
		if (i2c->is_write == 0) {
			int cmd_left = i2c->rd_total_len - i2c->rd_cmd_xfered;
			int max_send = (i2c->cdata->fifosize - 1)
					 - (i2c->rd_cmd_xfered
					 - i2c->rd_data_xfered);
			int cmd_to_send = min(cmd_left, max_send);

			if (i2c->rd_cmd_xfered != 0)
				cmd_to_send = min(cmd_to_send,
						  i2c->cdata->fifosize
						  - i2c->cdata->tx_level - 1);

			if (cmd_to_send) {
				i2c->rd_cmd_xfered += cmd_to_send;
				cmd_left = i2c->rd_total_len -
						i2c->rd_cmd_xfered;
				jz4780_i2c_send_rcmd(i2c,
						cmd_to_send, cmd_left);

			}

			if (cmd_left == 0) {
				intmsk = jz4780_i2c_readw(i2c, JZ4780_I2C_INTM);
				intmsk &= ~JZ4780_I2C_INTM_MTXEMP;
				jz4780_i2c_writew(i2c, JZ4780_I2C_INTM, intmsk);

				if (i2c->cdata->version < ID_X1000) {
					tmp = jz4780_i2c_readw(i2c,
							JZ4780_I2C_CTRL);
					tmp &= ~JZ4780_I2C_CTRL_STPHLD;
					jz4780_i2c_writew(i2c,
							JZ4780_I2C_CTRL, tmp);
				}
			}
		} else {
			unsigned short data;
			unsigned short i2c_sta;

			i2c_sta = jz4780_i2c_readw(i2c, JZ4780_I2C_STA);

			while ((i2c_sta & JZ4780_I2C_STA_TFNF) &&
					(i2c->wt_len > 0)) {
				i2c_sta = jz4780_i2c_readw(i2c, JZ4780_I2C_STA);
				data = *i2c->wbuf;
				data &= ~JZ4780_I2C_DC_READ;
				if ((i2c->wt_len == 1) && (!i2c->stop_hold) &&
						(i2c->cdata->version >= ID_X1000))
					data |= X1000_I2C_DC_STOP;
				jz4780_i2c_writew(i2c, JZ4780_I2C_DC, data);
				i2c->wbuf++;
				i2c->wt_len--;
			}

			if (i2c->wt_len == 0) {
				if ((!i2c->stop_hold) && (i2c->cdata->version <
						ID_X1000)) {
					tmp = jz4780_i2c_readw(i2c,
							JZ4780_I2C_CTRL);
					tmp &= ~JZ4780_I2C_CTRL_STPHLD;
					jz4780_i2c_writew(i2c,
							JZ4780_I2C_CTRL, tmp);
				}

				jz4780_i2c_trans_done(i2c);
				goto done;
			}
		}
	}

done:
	spin_unlock(&i2c->lock);
	return IRQ_HANDLED;
}