Driver development from scratch, linux driver (51, the communication method of IIC bus under Linux [5])

Posted by jqa on Sat, 11 May 2019 12:03:05 +0200

We need to implement communication methods for specific I2C adapters, mainly to implement functions () and master_xfer () of i2c_algorithm.
The functionality () function is very simple and is used to return the communication protocol supported by algorithm.

Define a method in linux

/* To determine what functionality is present */

#define I2C_FUNC_I2C			0x00000001
#define I2C_FUNC_10BIT_ADDR		0x00000002
#define I2C_FUNC_PROTOCOL_MANGLING	0x00000004 /* I2C_M_NOSTART etc. */
#define I2C_FUNC_SMBUS_PEC		0x00000008
#define I2C_FUNC_SMBUS_BLOCK_PROC_CALL	0x00008000 /* SMBus 2.0 */
#define I2C_FUNC_SMBUS_QUICK		0x00010000
#define I2C_FUNC_SMBUS_READ_BYTE	0x00020000
#define I2C_FUNC_SMBUS_WRITE_BYTE	0x00040000
#define I2C_FUNC_SMBUS_READ_BYTE_DATA	0x00080000
#define I2C_FUNC_SMBUS_WRITE_BYTE_DATA	0x00100000
#define I2C_FUNC_SMBUS_READ_WORD_DATA	0x00200000
#define I2C_FUNC_SMBUS_WRITE_WORD_DATA	0x00400000
#define I2C_FUNC_SMBUS_PROC_CALL	0x00800000
#define I2C_FUNC_SMBUS_READ_BLOCK_DATA	0x01000000
#define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 0x02000000
#define I2C_FUNC_SMBUS_READ_I2C_BLOCK	0x04000000 /* I2C-like block xfer  */
#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK	0x08000000 /* w/ 1-byte reg. addr. */

#define I2C_FUNC_SMBUS_BYTE		(I2C_FUNC_SMBUS_READ_BYTE | \
					 I2C_FUNC_SMBUS_WRITE_BYTE)
#define I2C_FUNC_SMBUS_BYTE_DATA	(I2C_FUNC_SMBUS_READ_BYTE_DATA | \
					 I2C_FUNC_SMBUS_WRITE_BYTE_DATA)
#define I2C_FUNC_SMBUS_WORD_DATA	(I2C_FUNC_SMBUS_READ_WORD_DATA | \
					 I2C_FUNC_SMBUS_WRITE_WORD_DATA)
#define I2C_FUNC_SMBUS_BLOCK_DATA	(I2C_FUNC_SMBUS_READ_BLOCK_DATA | \
					 I2C_FUNC_SMBUS_WRITE_BLOCK_DATA)
#define I2C_FUNC_SMBUS_I2C_BLOCK	(I2C_FUNC_SMBUS_READ_I2C_BLOCK | \
					 I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)

#define I2C_FUNC_SMBUS_EMUL		(I2C_FUNC_SMBUS_QUICK | \
					 I2C_FUNC_SMBUS_BYTE | \
					 I2C_FUNC_SMBUS_BYTE_DATA | \
					 I2C_FUNC_SMBUS_WORD_DATA | \
					 I2C_FUNC_SMBUS_PROC_CALL | \
					 I2C_FUNC_SMBUS_WRITE_BLOCK_DATA | \
					 I2C_FUNC_SMBUS_I2C_BLOCK | \
					 I2C_FUNC_SMBUS_PEC)

 

The master_xfer() function completes each I2C message passed to its i2c_msg array on the I2C adapter.
 

 

 

/* i2c bus registration info */

static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
	.master_xfer		= s3c24xx_i2c_xfer,
	.functionality		= s3c24xx_i2c_func,
};

 

Take a look at the communication protocols supported by Samsung.

/* declare our i2c functionality */
static u32 s3c24xx_i2c_func(struct i2c_adapter *adap)
{
	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING;
}

 

 

In iic, the sending of messages is only a start boot, and the detailed sending steps are processed in the interrupt function.

 

/* s3c24xx_i2c_xfer
 *
 * first port of call from the i2c bus code when an message needs
 * transferring across the i2c bus.
*/

static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
			struct i2c_msg *msgs, int num)
{
    /* Get the specific i2c controller */
	struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data;
	int retry;
	int ret;

	clk_enable(i2c->clk);    /* Open i2c clock */

    /* retries Represents the number of retransmits that were initialized to 2.
     * Here's where num msg s can occur at most twice */
	for (retry = 0; retry < adap->retries; retry++) {

        /* Send num msgs */
		ret = s3c24xx_i2c_doxfer(i2c, msgs, num);

		if (ret != -EAGAIN)
			goto out;

		dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry);

		udelay(200);
	}
	ret = -EREMOTEIO;
out:
	clk_disable(i2c->clk);    /* Turn off i2c clock */

	return ret;
}

 

/* s3c24xx_i2c_doxfer
 *
 * this starts an i2c transfer
*/

static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c,
			      struct i2c_msg *msgs, int num)
{
	unsigned long timeout;
	int ret;

	if (i2c->suspended)
		return -EIO;

    /* Wait for the iic controller to be idle before it happens */
	ret = s3c24xx_i2c_set_master(i2c);
	if (ret != 0) {
		dev_err(i2c->dev, "cannot get bus (error %d)\n", ret);
		ret = -EAGAIN;
		goto out;
	}

	spin_lock_irq(&i2c->lock);

    /* Set the message to be sent */
	i2c->msg     = msgs;
	i2c->msg_num = num;
	i2c->msg_ptr = 0;
	i2c->msg_idx = 0;
	i2c->state   = STATE_START;    /* Initialization is the starting state */

	s3c24xx_i2c_enable_irq(i2c);    /* When the data is completed, there will be an interruption of sending completion. The interruption must be opened before sending or accepting the data. */
	s3c24xx_i2c_message_start(i2c, msgs);    /* Fill in the data to the i2c data register and start sending */
	spin_unlock_irq(&i2c->lock);

    /* The occurrence process is completely handled by the interrupt function, so the function is now asleep. The function either wakes up by interrupt or wakes up by itself overtime. */
	timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);

	ret = i2c->msg_idx;

	/* having these next two as dev_err() makes life very
	 * noisy when doing an i2cdetect */

	if (timeout == 0)
		dev_dbg(i2c->dev, "timeout\n");
	else if (ret != num)
		dev_dbg(i2c->dev, "incomplete xfer (%d)\n", ret);

	/* ensure the stop has been through the bus */

	udelay(10);

 out:
	return ret;
}

 

Waiting for idleness is simple. Just look at the registers and check the corresponding bits.

static int s3c24xx_i2c_set_master(struct s3c24xx_i2c *i2c)
{
	unsigned long iicstat;
	int timeout = 400;

    /* Look at the status register here to ensure that it is idle before it happens, and if it is busy, it can wait up to 400 ms. */
	while (timeout-- > 0) {
		iicstat = readl(i2c->regs + S3C2410_IICSTAT);

		if (!(iicstat & S3C2410_IICSTAT_BUSBUSY))
			return 0;

		msleep(1);
	}
    
    /* Turn on Occurrence and Receive Functions */
	writel(iicstat & ~S3C2410_IICSTAT_TXRXEN, i2c->regs + S3C2410_IICSTAT);
	if (!(readl(i2c->regs + S3C2410_IICSTAT) & S3C2410_IICSTAT_BUSBUSY))
		return 0;

	return -ETIMEDOUT;
}

 

/* s3c24xx_i2c_message_start
 *
 * put the start of a message onto the bus
*/

static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c,
				      struct i2c_msg *msg)
{
	unsigned int addr = (msg->addr & 0x7f) << 1;    //Setting the address of the device that happened
	unsigned long stat;
	unsigned long iiccon;

	stat = 0;
	stat |=  S3C2410_IICSTAT_TXRXEN;

    /* Set the read and write direction of this message */
	if (msg->flags & I2C_M_RD) {
		stat |= S3C2410_IICSTAT_MASTER_RX;
		addr |= 1;
	} else
		stat |= S3C2410_IICSTAT_MASTER_TX;

	if (msg->flags & I2C_M_REV_DIR_ADDR)
		addr ^= 1;

	/* todo - check for wether ack wanted or not */
	s3c24xx_i2c_enable_ack(i2c);    /* Turn on the clock */

    /* Get the control register value */
	iiccon = readl(i2c->regs + S3C2410_IICCON);

    /* Set the state of the host at this time (occurs or receives) */
	writel(stat, i2c->regs + S3C2410_IICSTAT);

	dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS\n", stat, addr);
    /* Set the slave address and data direction to send */
	writeb(addr, i2c->regs + S3C2410_IICDS);

	/* delay here to ensure the data byte has gotten onto the bus
	 * before the transaction is started */

	ndelay(i2c->tx_setup);    

	dev_dbg(i2c->dev, "iiccon, %08lx\n", iiccon);
    /* Write back the controller register value */
	writel(iiccon, i2c->regs + S3C2410_IICCON);

    /* Start transmission */
	stat |= S3C2410_IICSTAT_START;
	writel(stat, i2c->regs + S3C2410_IICSTAT);
}

Here we can see that the function s3c24xx_i2c_message_start is very simple, setting the slave address and data direction, and starting the transmission.

Of course, when the address occurs, there is an ACK signal from the opportunity. When the i2c host receives the ACK signal, it triggers the interrupt handler, and then reads, writes, stops and so on are executed in the interrupt handler.

 

Before starting the analysis of interrupt processing function, let's take a look at Samsung's i2c, which defines some occurrence states, such as idle state, start sending state, read data state, write data state and stop state of occurrence and completion.

enum s3c24xx_i2c_state {
	STATE_IDLE,
	STATE_START,
	STATE_READ,
	STATE_WRITE,
	STATE_STOP
};

 

Next, look at the real interrupt handler.

/* s3c24xx_i2c_irq
 *
 * top level IRQ servicing routine
*/

static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id)
{
	struct s3c24xx_i2c *i2c = dev_id;
	unsigned long status;
	unsigned long tmp;

    /* Get iic's status register */
	status = readl(i2c->regs + S3C2410_IICSTAT);

    /* Special circumstances of arbitration failure */
	if (status & S3C2410_IICSTAT_ARBITR) {
		/* deal with arbitration loss */
		dev_err(i2c->dev, "deal with arbitration loss\n");
	}

    /* Is it idle at this time? Normal interruption will not be idle? */
	if (i2c->state == STATE_IDLE) {
		dev_dbg(i2c->dev, "IRQ: error i2c->state == IDLE\n");

        /* Clear the iic controller register here */
		tmp = readl(i2c->regs + S3C2410_IICCON);
		tmp &= ~S3C2410_IICCON_IRQPEND;
		writel(tmp, i2c->regs +  S3C2410_IICCON);
		goto out;
	}

	/* pretty much this leaves us with the fact that we've
	 * transmitted or received whatever byte we last sent */

    /* This is the normal situation. Whether it's the starting address or other data sent, it's all in here. */
	i2c_s3c_irq_nextbyte(i2c, status);

 out:
	return IRQ_HANDLED;
}

 

Before we start, let's talk about iic's data sending and receiving.

 

Suppose we write a byte of data at 0x03 address in a device with 0x02 address, and the data to be written is 0x04.

The normal order should be this.

  • The host fills in the device address 0x02, sets the direction to write, and starts sending.
  • Next, the device address received from the slave machine is returned to ACK.
  • The host receives ACK and interrupts.
  • In the interrupt processing function, the host first determines whether or not it receives the response signal. If it receives the response signal, it fills in 0x03 to the data register and clears the interrupt suspension.
  • Next, the slave receives the address information of 0x03 and returns an ACK signal.
  • The host receives ACK signal and generates interruption.
  • In the interrupt processing function, the host first determines whether or not it receives the response signal. If it receives the response signal, it fills in 0x04 to the data register and clears the interrupt suspension.
  • Next, the data information of 0x04 is received from the slave computer and an ACK signal is returned.
  • The host receives ACK signal and generates interruption.
  • Next, the whole writing process has been completed. The host sends a stop signal to indicate the end of the communication.

 

Of course, for reading operations, it will be relatively troublesome.

Because before the read operation, the address of the register (memory) to be read in the device should be set first, and then the read operation should be started.

 

 

 

 

 

Interrupt function processing process is a state machine, step by step.

 

 

 

 

 

/* is_lastmsg()
 *
 * returns TRUE if the current message is the last in the set
*/
/* Judge if it's the last message? */
static inline int is_lastmsg(struct s3c24xx_i2c *i2c)
{
	return i2c->msg_idx >= (i2c->msg_num - 1);
}

/* is_msglast
 *
 * returns TRUE if we this is the last byte in the current message
*/
/* Is it the last byte of the current message? */
static inline int is_msglast(struct s3c24xx_i2c *i2c)
{
	return i2c->msg_ptr == i2c->msg->len-1;
}

/* is_msgend
 *
 * returns TRUE if we reached the end of the current message
*/
/* At the end of the current message */
static inline int is_msgend(struct s3c24xx_i2c *i2c)
{
	return i2c->msg_ptr >= i2c->msg->len;
}

/* i2c_s3c_irq_nextbyte
 *
 * process an interrupt and work out what to do
 */

static int i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat)
{
	unsigned long tmp;
	unsigned char byte;
	int ret = 0;

	switch (i2c->state) {

	case STATE_IDLE:
		dev_err(i2c->dev, "%s: called in STATE_IDLE\n", __func__);
		goto out;
		break;

	case STATE_STOP:
        /* When a stop signal occurs, the iic interrupt is turned off */
		dev_err(i2c->dev, "%s: called in STATE_STOP\n", __func__);
		s3c24xx_i2c_disable_irq(i2c);
		goto out_ack;

	case STATE_START:
		/* last thing we did was send a start condition on the
		 * bus, or started a new i2c message
		 */

        /* The first normal entry is when the xfer function sends the address. Normally, the slave machine has a response signal to return.
         * It is judged here that the response is received before the execution continues, otherwise the stop signal will occur directly.
         */
		if (iicstat & S3C2410_IICSTAT_LASTBIT &&
		    !(i2c->msg->flags & I2C_M_IGNORE_NAK)) {
			/* ack was not received... */

			dev_dbg(i2c->dev, "ack was not received\n");
			s3c24xx_i2c_stop(i2c, -ENXIO);
			goto out_ack;
		}
        
        /* For the read operation, the next state is set to read, and for the write operation, the next state is set to write. */
		if (i2c->msg->flags & I2C_M_RD)
			i2c->state = STATE_READ;
		else
			i2c->state = STATE_WRITE;

		/* terminate the transfer if there is nothing to do
		 * as this is used by the i2c probe to find devices. */

        /* Judge if there is data, stop without data? */
		if (is_lastmsg(i2c) && i2c->msg->len == 0) {
			s3c24xx_i2c_stop(i2c, 0);
			goto out_ack;
		}
        /* For read operations, jump to read */
		if (i2c->state == STATE_READ)
			goto prepare_read;

		/* fall through to the write state, as we will need to
		 * send a byte as well */
        /* For writing, just continue executing */

	case STATE_WRITE:
		/* we are writing data to the device... check for the
		 * end of the message, and if so, work out what to do
		 */
        /* Anomaly check */
		if (!(i2c->msg->flags & I2C_M_IGNORE_NAK)) {
			if (iicstat & S3C2410_IICSTAT_LASTBIT) {
				dev_dbg(i2c->dev, "WRITE: No Ack\n");

				s3c24xx_i2c_stop(i2c, -ECONNREFUSED);
				goto out_ack;
			}
		}

 retry_write:

		if (!is_msgend(i2c)) {
            /* If there is data in the current msg, the data is written into the data register */
			byte = i2c->msg->buf[i2c->msg_ptr++];
			writeb(byte, i2c->regs + S3C2410_IICDS);

			/* delay after writing the byte to allow the
			 * data setup time on the bus, as writing the
			 * data to the register causes the first bit
			 * to appear on SDA, and SCL will change as
			 * soon as the interrupt is acknowledged */

			ndelay(i2c->tx_setup);

		} else if (!is_lastmsg(i2c)) {     /* Is there another msg to send? */
			/* we need to go to the next i2c message */
            /* The last msg is sent out and the new msg is taken out */
			dev_dbg(i2c->dev, "WRITE: Next Message\n");

			i2c->msg_ptr = 0;
			i2c->msg_idx++;    //Number of msg s occurring
			i2c->msg++;

			/* check to see if we need to do another message */
            /* Is the I2C_M_NOSTART flag set for the same device?
             * To send different msg s, do you want to reproduce the start signal and the device address
             */
			if (i2c->msg->flags & I2C_M_NOSTART) {

				if (i2c->msg->flags & I2C_M_RD) {
					/* cannot do this, the controller
					 * forces us to send a new START
					 * when we change direction */
                    /* Before the read operation, it is a write operation, and there must be no re-occurrence of start and so on. If the read operation has this item, it must be problematic, indicating that the parameters are invalid. */
					s3c24xx_i2c_stop(i2c, -EINVAL);
				} else {
                    /* Here we're going to send a stop signal. It's in the else that's implied here. */
                }

				goto retry_write;
			} else {
				/* send the new start */
                /* Some msg s are set up with different registers of the device, so they need to restart a transmission */
				s3c24xx_i2c_message_start(i2c, i2c->msg);
				i2c->state = STATE_START;
			}

		} else {
			/* send stop */
            /* For write operations, when all data is sent out, a stubborn signal is sent. */
			s3c24xx_i2c_stop(i2c, 0);
		}
		break;

	case STATE_READ:
		/* we have a byte of data in the data register, do
		 * something with it, and then work out wether we are
		 * going to do any more read/write
		 */
        /* For reading data, it is usually set to two msg s, one is to write a packet, which tells the slave the address to read.
         * Use the second package for real reads, and the second msg, which comes in for the first time, has no data
         */
		byte = readb(i2c->regs + S3C2410_IICDS);
		i2c->msg->buf[i2c->msg_ptr++] = byte;

 prepare_read:

        /* For the normal reading process, the following if s or else IFS are not accessible */

		if (is_msglast(i2c)) {
			/* last byte of buffer */
                /* For the last read data, the host can send ack signal to slave without sending ack signal. */
			if (is_lastmsg(i2c))
				s3c24xx_i2c_disable_ack(i2c);

		} else if (is_msgend(i2c)) {
			/* ok, we've read the entire buffer, see if there
			 * is anything else we need to do */
            /* After reading, a stop signal occurs. */
    

			if (is_lastmsg(i2c)) {
				/* last message, send stop and complete */
				dev_dbg(i2c->dev, "READ: Send Stop\n");

				s3c24xx_i2c_stop(i2c, 0);
			} else {
				/* go to the next transfer */
				dev_dbg(i2c->dev, "READ: Next Transfer\n");

				i2c->msg_ptr = 0;
				i2c->msg_idx++;
				i2c->msg++;
			}
		}

		break;
	}

	/* acknowlegde the IRQ and get back on with the work */

 out_ack:
    /* Clear interrupt pending */
	tmp = readl(i2c->regs + S3C2410_IICCON);
	tmp &= ~S3C2410_IICCON_IRQPEND;
	writel(tmp, i2c->regs + S3C2410_IICCON);
 out:
	return ret;
}

 

 

static inline void s3c24xx_i2c_stop(struct s3c24xx_i2c *i2c, int ret)
{
	unsigned long iicstat = readl(i2c->regs + S3C2410_IICSTAT);

	dev_dbg(i2c->dev, "STOP\n");

	/* stop the transfer */
    /* Send Stop Signal */
	iicstat &= ~S3C2410_IICSTAT_START;
	writel(iicstat, i2c->regs + S3C2410_IICSTAT);

    /* Setup state */
	i2c->state = STATE_STOP;

    /* Close interruption, set completion flag */
	s3c24xx_i2c_master_complete(i2c, ret);
	s3c24xx_i2c_disable_irq(i2c);
}

 

Topics: Linux