Commit e21d7977 authored by Rayagonda Kokatanur's avatar Rayagonda Kokatanur Committed by Wolfram Sang
Browse files

i2c: iproc: handle master read request



Handle single or multi byte master read request with or without
repeated start.

Fixes: c245d94e ("i2c: iproc: Add multi byte read-write support for slave mode")
Signed-off-by: default avatarRayagonda Kokatanur <rayagonda.kokatanur@broadcom.com>
Acked-by: default avatarRay Jui <ray.jui@broadcom.com>
Signed-off-by: default avatarWolfram Sang <wsa@kernel.org>
parent 514bfc64
Loading
Loading
Loading
Loading
+170 −45
Original line number Diff line number Diff line
@@ -160,6 +160,11 @@

#define IE_S_ALL_INTERRUPT_SHIFT     21
#define IE_S_ALL_INTERRUPT_MASK      0x3f
/*
 * It takes ~18us to reading 10bytes of data, hence to keep tasklet
 * running for less time, max slave read per tasklet is set to 10 bytes.
 */
#define MAX_SLAVE_RX_PER_INT         10

enum i2c_slave_read_status {
	I2C_SLAVE_RX_FIFO_EMPTY = 0,
@@ -206,8 +211,18 @@ struct bcm_iproc_i2c_dev {
	/* bytes that have been read */
	unsigned int rx_bytes;
	unsigned int thld_bytes;

	bool slave_rx_only;
	bool rx_start_rcvd;
	bool slave_read_complete;
	u32 tx_underrun;
	u32 slave_int_mask;
	struct tasklet_struct slave_rx_tasklet;
};

/* tasklet to process slave rx data */
static void slave_rx_tasklet_fn(unsigned long);

/*
 * Can be expanded in the future if more interrupt status bits are utilized
 */
@@ -261,6 +276,7 @@ static void bcm_iproc_i2c_slave_init(
{
	u32 val;

	iproc_i2c->tx_underrun = 0;
	if (need_reset) {
		/* put controller in reset */
		val = iproc_i2c_rd_reg(iproc_i2c, CFG_OFFSET);
@@ -297,8 +313,11 @@ static void bcm_iproc_i2c_slave_init(

	/* Enable interrupt register to indicate a valid byte in receive fifo */
	val = BIT(IE_S_RX_EVENT_SHIFT);
	/* Enable interrupt register to indicate a Master read transaction */
	val |= BIT(IE_S_RD_EVENT_SHIFT);
	/* Enable interrupt register for the Slave BUSY command */
	val |= BIT(IE_S_START_BUSY_SHIFT);
	iproc_i2c->slave_int_mask = val;
	iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, val);
}

@@ -324,76 +343,176 @@ static void bcm_iproc_i2c_check_slave_status(
	}
}

static bool bcm_iproc_i2c_slave_isr(struct bcm_iproc_i2c_dev *iproc_i2c,
				    u32 status)
static void bcm_iproc_i2c_slave_read(struct bcm_iproc_i2c_dev *iproc_i2c)
{
	u8 rx_data, rx_status;
	u32 rx_bytes = 0;
	u32 val;
	u8 value, rx_status;

	/* Slave RX byte receive */
	if (status & BIT(IS_S_RX_EVENT_SHIFT)) {
	while (rx_bytes < MAX_SLAVE_RX_PER_INT) {
		val = iproc_i2c_rd_reg(iproc_i2c, S_RX_OFFSET);
		rx_status = (val >> S_RX_STATUS_SHIFT) & S_RX_STATUS_MASK;
		rx_data = ((val >> S_RX_DATA_SHIFT) & S_RX_DATA_MASK);

		if (rx_status == I2C_SLAVE_RX_START) {
			/* Start of SMBUS for Master write */
			/* Start of SMBUS Master write */
			i2c_slave_event(iproc_i2c->slave,
					I2C_SLAVE_WRITE_REQUESTED, &value);

			val = iproc_i2c_rd_reg(iproc_i2c, S_RX_OFFSET);
			value = (u8)((val >> S_RX_DATA_SHIFT) & S_RX_DATA_MASK);
					I2C_SLAVE_WRITE_REQUESTED, &rx_data);
			iproc_i2c->rx_start_rcvd = true;
			iproc_i2c->slave_read_complete = false;
		} else if (rx_status == I2C_SLAVE_RX_DATA &&
			   iproc_i2c->rx_start_rcvd) {
			/* Middle of SMBUS Master write */
			i2c_slave_event(iproc_i2c->slave,
					I2C_SLAVE_WRITE_RECEIVED, &value);
		} else if (status & BIT(IS_S_RD_EVENT_SHIFT)) {
			/* Start of SMBUS for Master Read */
					I2C_SLAVE_WRITE_RECEIVED, &rx_data);
		} else if (rx_status == I2C_SLAVE_RX_END &&
			   iproc_i2c->rx_start_rcvd) {
			/* End of SMBUS Master write */
			if (iproc_i2c->slave_rx_only)
				i2c_slave_event(iproc_i2c->slave,
					I2C_SLAVE_READ_REQUESTED, &value);
			iproc_i2c_wr_reg(iproc_i2c, S_TX_OFFSET, value);
						I2C_SLAVE_WRITE_RECEIVED,
						&rx_data);

			i2c_slave_event(iproc_i2c->slave, I2C_SLAVE_STOP,
					&rx_data);
		} else if (rx_status == I2C_SLAVE_RX_FIFO_EMPTY) {
			iproc_i2c->rx_start_rcvd = false;
			iproc_i2c->slave_read_complete = true;
			break;
		}

			val = BIT(S_CMD_START_BUSY_SHIFT);
			iproc_i2c_wr_reg(iproc_i2c, S_CMD_OFFSET, val);
		rx_bytes++;
	}
}

static void slave_rx_tasklet_fn(unsigned long data)
{
	struct bcm_iproc_i2c_dev *iproc_i2c = (struct bcm_iproc_i2c_dev *)data;
	u32 int_clr;

	bcm_iproc_i2c_slave_read(iproc_i2c);

	/* clear pending IS_S_RX_EVENT_SHIFT interrupt */
	int_clr = BIT(IS_S_RX_EVENT_SHIFT);

	if (!iproc_i2c->slave_rx_only && iproc_i2c->slave_read_complete) {
		/*
			 * Enable interrupt for TX FIFO becomes empty and
			 * less than PKT_LENGTH bytes were output on the SMBUS
		 * In case of single byte master-read request,
		 * IS_S_TX_UNDERRUN_SHIFT event is generated before
		 * IS_S_START_BUSY_SHIFT event. Hence start slave data send
		 * from first IS_S_TX_UNDERRUN_SHIFT event.
		 *
		 * This means don't send any data from slave when
		 * IS_S_RD_EVENT_SHIFT event is generated else it will increment
		 * eeprom or other backend slave driver read pointer twice.
		 */
		iproc_i2c->tx_underrun = 0;
		iproc_i2c->slave_int_mask |= BIT(IE_S_TX_UNDERRUN_SHIFT);

		/* clear IS_S_RD_EVENT_SHIFT interrupt */
		int_clr |= BIT(IS_S_RD_EVENT_SHIFT);
	}

	/* clear slave interrupt */
	iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, int_clr);
	/* enable slave interrupts */
	iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, iproc_i2c->slave_int_mask);
}

static bool bcm_iproc_i2c_slave_isr(struct bcm_iproc_i2c_dev *iproc_i2c,
				    u32 status)
{
	u32 val;
	u8 value;

	/*
	 * Slave events in case of master-write, master-write-read and,
	 * master-read
	 *
	 * Master-write     : only IS_S_RX_EVENT_SHIFT event
	 * Master-write-read: both IS_S_RX_EVENT_SHIFT and IS_S_RD_EVENT_SHIFT
	 *                    events
	 * Master-read      : both IS_S_RX_EVENT_SHIFT and IS_S_RD_EVENT_SHIFT
	 *                    events or only IS_S_RD_EVENT_SHIFT
	 */
	if (status & BIT(IS_S_RX_EVENT_SHIFT) ||
	    status & BIT(IS_S_RD_EVENT_SHIFT)) {
		/* disable slave interrupts */
		val = iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET);
			val |= BIT(IE_S_TX_UNDERRUN_SHIFT);
		val &= ~iproc_i2c->slave_int_mask;
		iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, val);
		} else {
			/* Master write other than start */
			value = (u8)((val >> S_RX_DATA_SHIFT) & S_RX_DATA_MASK);
			i2c_slave_event(iproc_i2c->slave,
					I2C_SLAVE_WRITE_RECEIVED, &value);
			if (rx_status == I2C_SLAVE_RX_END)
				i2c_slave_event(iproc_i2c->slave,
						I2C_SLAVE_STOP, &value);

		if (status & BIT(IS_S_RD_EVENT_SHIFT))
			/* Master-write-read request */
			iproc_i2c->slave_rx_only = false;
		else
			/* Master-write request only */
			iproc_i2c->slave_rx_only = true;

		/* schedule tasklet to read data later */
		tasklet_schedule(&iproc_i2c->slave_rx_tasklet);

		/* clear only IS_S_RX_EVENT_SHIFT interrupt */
		iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET,
				 BIT(IS_S_RX_EVENT_SHIFT));
	}
	} else if (status & BIT(IS_S_TX_UNDERRUN_SHIFT)) {

	if (status & BIT(IS_S_TX_UNDERRUN_SHIFT)) {
		iproc_i2c->tx_underrun++;
		if (iproc_i2c->tx_underrun == 1)
			/* Start of SMBUS for Master Read */
			i2c_slave_event(iproc_i2c->slave,
					I2C_SLAVE_READ_REQUESTED,
					&value);
		else
			/* Master read other than start */
			i2c_slave_event(iproc_i2c->slave,
				I2C_SLAVE_READ_PROCESSED, &value);
					I2C_SLAVE_READ_PROCESSED,
					&value);

		iproc_i2c_wr_reg(iproc_i2c, S_TX_OFFSET, value);
		/* start transfer */
		val = BIT(S_CMD_START_BUSY_SHIFT);
		iproc_i2c_wr_reg(iproc_i2c, S_CMD_OFFSET, val);

		/* clear interrupt */
		iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET,
				 BIT(IS_S_TX_UNDERRUN_SHIFT));
	}

	/* Stop */
	/* Stop received from master in case of master read transaction */
	if (status & BIT(IS_S_START_BUSY_SHIFT)) {
		i2c_slave_event(iproc_i2c->slave, I2C_SLAVE_STOP, &value);
		/*
		 * Disable interrupt for TX FIFO becomes empty and
		 * less than PKT_LENGTH bytes were output on the SMBUS
		 */
		val = iproc_i2c_rd_reg(iproc_i2c, IE_OFFSET);
		val &= ~BIT(IE_S_TX_UNDERRUN_SHIFT);
		iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, val);
	}
		iproc_i2c->slave_int_mask &= ~BIT(IE_S_TX_UNDERRUN_SHIFT);
		iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET,
				 iproc_i2c->slave_int_mask);

	/* clear interrupt status */
	iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET, status);
		/* End of SMBUS for Master Read */
		val = BIT(S_TX_WR_STATUS_SHIFT);
		iproc_i2c_wr_reg(iproc_i2c, S_TX_OFFSET, val);

		val = BIT(S_CMD_START_BUSY_SHIFT);
		iproc_i2c_wr_reg(iproc_i2c, S_CMD_OFFSET, val);

		/* flush TX FIFOs */
		val = iproc_i2c_rd_reg(iproc_i2c, S_FIFO_CTRL_OFFSET);
		val |= (BIT(S_FIFO_TX_FLUSH_SHIFT));
		iproc_i2c_wr_reg(iproc_i2c, S_FIFO_CTRL_OFFSET, val);

		i2c_slave_event(iproc_i2c->slave, I2C_SLAVE_STOP, &value);

		/* clear interrupt */
		iproc_i2c_wr_reg(iproc_i2c, IS_OFFSET,
				 BIT(IS_S_START_BUSY_SHIFT));
	}

	/* check slave transmit status only if slave is transmitting */
	if (!iproc_i2c->slave_rx_only)
		bcm_iproc_i2c_check_slave_status(iproc_i2c);

	return true;
}

@@ -1074,6 +1193,10 @@ static int bcm_iproc_i2c_reg_slave(struct i2c_client *slave)
		return -EAFNOSUPPORT;

	iproc_i2c->slave = slave;

	tasklet_init(&iproc_i2c->slave_rx_tasklet, slave_rx_tasklet_fn,
		     (unsigned long)iproc_i2c);

	bcm_iproc_i2c_slave_init(iproc_i2c, false);
	return 0;
}
@@ -1094,6 +1217,8 @@ static int bcm_iproc_i2c_unreg_slave(struct i2c_client *slave)
			IE_S_ALL_INTERRUPT_SHIFT);
	iproc_i2c_wr_reg(iproc_i2c, IE_OFFSET, tmp);

	tasklet_kill(&iproc_i2c->slave_rx_tasklet);

	/* Erase the slave address programmed */
	tmp = iproc_i2c_rd_reg(iproc_i2c, S_CFG_SMBUS_ADDR_OFFSET);
	tmp &= ~BIT(S_CFG_EN_NIC_SMB_ADDR3_SHIFT);