Linux I2C driver detailed explanation

Publisher:数字行者Latest update time:2021-11-22 Source: eefocusKeywords:Linux  I2C  driver Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere

This article is based on the mini2440 development board, and the Linux version number is: linux-2.6.32.2


1.IIC bus device hardware information

#define S3C2410_PA_IIC    (0x54000000)

static struct resource s3c_i2c_resource[] = {

[0] = {

.start = S3C_PA_IIC,

.end = S3C_PA_IIC + SZ_4K - 1,

.flags = IORESOURCE_MEM,

},

[1] = {

.start = IRQ_IIC,

.end   = IRQ_IIC,

.flags = IORESOURCE_IRQ,

},

};


struct platform_device s3c_device_i2c0 = {

.name   = "s3c2410-i2c",

#ifdef CONFIG_S3C_DEV_I2C1

.id   = 0,

#else

.id   = -1,

#endif

.num_resources   = ARRAY_SIZE(s3c_i2c_resource),

.resource   = s3c_i2c_resource,

};


static struct s3c2410_platform_i2c default_i2c_data0 __initdata = {

.flags = 0,

.slave_addr = 0x10,

.frequency = 100*1000,

.sda_delay = 100,

};


Among them, the base address of the IIC register is 0x54000000.

The addresses of the registers of the s3c3440 chip are as follows:

insert image description here
insert image description here
insert image description here
insert image description here
insert image description here

2.IIC bus device registration

The IIC bus device is included in mini2440_devices, as shown in the following figure:

insert image description here

Register the mini2440_devices array containing USB, LCD, I2C, NAND and other devices as platform devices to the kernel


platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices))


3. Registration of IIC bus driver

The registration of the IIC bus driver calls the platform_driver_register function. The IIC bus driver registration means that the platform bus will automatically match the corresponding device. The matching rules are:


First match according to id_table

Then match according to the name of the device

insert image description here

This IICdriver can match two IIC devices, as shown below:

insert image description here

4. Matching of IIC bus driver and device

After the driver and device of the IIC bus are matched, the driver's probe function will be executed. The following actions are performed in the probe function:


Get the i2c clock and enable the clock

i2c->clk = clk_get(&pdev->dev, "i2c");

if (IS_ERR(i2c->clk)) {

    dev_err(&pdev->dev, "cannot get clockn");

    right = -ENOENT;

    goto err_noclk;

}

clk_enable(i2c->clk);


Get the virtual address of the IIC register

i2c->regs = ioremap(res->start, resource_size(res));


Get IIC interrupt and apply IIC interrupt

i2c->irq = ret = platform_get_irq(pdev, 0);

if (ret <= 0) {

    dev_err(&pdev->dev, "cannot find IRQn");

    goto err_iomap;

}

ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,dev_name(&pdev->dev), i2c);


IIC initialization, set clock, enable interrupt, enable ask, set register

static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c)

{

unsigned long iicon = S3C2410_IICCON_IRQEN | S3C2410_IICCON_ACKEN;

struct s3c2410_platform_i2c *pdata;

unsigned int freq;


/* get the plafrom data: Get platform device data

pdata = i2c->dev->platform_data;

/* inititalise the gpio */

if (pdata->cfg_gpio)

pdata->cfg_gpio(to_platform_device(i2c->dev));


/* write slave address */

writeb(pdata->slave_addr, i2c->regs + S3C2410_IICADD);


        // IIC-bus acknowledge enable

        //IC-Bus Tx/Rx interrupt enable

writel(iicon, i2c->regs + S3C2410_IICCON);


/* we need to work out the divisors for the clock... */

        //Set the clock

if (s3c24xx_i2c_clockrate(i2c, &freq) != 0) {

writel(0, i2c->regs + S3C2410_IICCON);

dev_err(i2c->dev, "cannot meet bus frequency requiredn");

return -SINGLE SELECT;

}


/* todo - check that the i2c lines aren't being dragged anywhere */

dev_info(i2c->dev, "bus frequency set to %d KHzn", freq);

dev_dbg(i2c->dev, "S3C2410_IICCON=0x%02lxn", iicon);


return 0;

}


Register IIC adapter. Before registering the adapter, you need to set the name, algorithm, parent node and other information of the adapter.

strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));

i2c->adap.owner   = THIS_MODULE;

i2c->adap.algo    = &s3c24xx_i2c_algorithm;

i2c->adap.retries = 2;

i2c->adap.class   = I2C_CLASS_HWMON | I2C_CLASS_SPD;

i2c->tx_setup     = 50;

i2c->adap.algo_data = i2c;

i2c->adap.dev.parent = &pdev->dev;

i2c->adap.nr = pdata->bus_num;

ret = i2c_add_numbered_adapter(&i2c->adap);


5. Enable IIC bus response

insert image description here

Need to set bit 7 of the IICCON register to 1.


#define S3C2410_IICCON_ACKEN (1<<7)

static inline void s3c24xx_i2c_enable_ack(struct s3c24xx_i2c *i2c)

{

unsigned long tmp;

tmp = readl(i2c->regs + S3C2410_IICCON);

writel(tmp | S3C2410_IICCON_ACKEN, i2c->regs + S3C2410_IICCON);

}


6. Enable IIC bus interrupt

insert image description here

Need to set bit 5 of the IICCON register to 1.


#define S3C2410_IICCON_IRQEN (1<<5)

static inline void s3c24xx_i2c_enable_irq(struct s3c24xx_i2c *i2c)

{

unsigned long tmp;

tmp = readl(i2c->regs + S3C2410_IICCON);

writel(tmp | S3C2410_IICCON_IRQEN, i2c->regs + S3C2410_IICCON);

}


7. Get the busy status of the IIC bus

insert image description here

Read bit 5 of the IICSTAT register, 1 indicates busy, 0 indicates idle


#define S3C2410_IICSTAT_BUSBUSY (1<<5)


iicstat = readl(i2c->regs + S3C2410_IICSTAT);

return (iicstat & S3C2410_IICSTAT_BUSBUSY);


8. IIC hardware interrupt

Conditions for IIC interrupt generation

① A hardware interrupt is generated when one byte of data is sent or received.

②Send the slave device address and generate a hardware interrupt after successfully matching the slave device.

③ Bus arbitration failure generates an interrupt.

Under normal circumstances, the timing of IIC interruption

When sending data, the IICDS shift register sends a level signal to SDA one bit at a time, sending the high bit first. After 8 cycles, the data transmission is completed and an interrupt is generated after receiving the ASK signal.

When reading data, the IICDS shift register reads the level signal from the SDA line one bit at a time, reading the high bit first. After 8 cycles, data reception is completed and an interrupt is generated.


3. New IIC data comes during interrupt processing

After a hardware interrupt is generated, the interrupt pending flag will be set, SCL will be pulled low, and IIC transmission will be suspended to prevent a new IIC signal from coming during the interrupt processing.

After waiting for the interrupt function to complete, clear the interrupt pending flag, release SCL, and IIC continues to send and receive data.

insert image description here

Clear the interrupt pending flag and set bit 4 of the IICCON register to 0.


#define S3C2410_IICCON_IRQPEND (1<<4)

tmp = readl(i2c->regs + S3C2410_IICCON);

tmp &= ~S3C2410_IICCON_IRQPEND;

writel(tmp, i2c->regs + S3C2410_IICCON);


9. IIC bus start

Enable IIC response

s3c24xx_i2c_enable_ack(i2c);


2. Enable IIC data input and output


#define S3C2410_IICSTAT_TXRXEN (1<<4)

stat |=  S3C2410_IICSTAT_TXRXEN;


3. Configure the host read/write mode. If reading, the lowest bit or upper bit of the device address is 1.

insert image description here

#define S3C2410_IICSTAT_MASTER_RX (2<<6)

#define S3C2410_IICSTAT_MASTER_TX (3<<6)

if (msg->flags & I2C_M_RD)

{

stat |= S3C2410_IICSTAT_MASTER_RX;

addr |= 1;

else

stat |= S3C2410_IICSTAT_MASTER_TX;


writel(stat, i2c->regs + S3C2410_IICSTAT);


4. Write the slave device address to the IICDS register


writeb(addr, i2c->regs + S3C2410_IICDS);


5. Start IIC transmission


stat |= S3C2410_IICSTAT_START;

writel(stat, i2c->regs + S3C2410_IICSTAT);

insert image description here

10. IIC bus stops transmitting

1. Need to write 0 to bit 5 of the IICSTART register

insert image description here

unsigned long iicstat = readl(i2c->regs + S3C2410_IICSTAT);

/* stop the transfer */

iicstat &= ~S3C2410_IICSTAT_START;

writel(iicstat, i2c->regs + S3C2410_IICSTAT);


2. Disable IIC interrupt


s3c24xx_i2c_disable_irq(i2c);


11. IIC bus algo

static const struct i2c_algorithm s3c24xx_i2c_algorithm = {

.master_xfer = s3c24xx_i2c_xfer,

.functionality = s3c24xx_i2c_func,

};


s3c24xx_i2c_algorithm is assigned to adap.algo


i2c->adap.algo    = &s3c24xx_i2c_algorithm;


This adap is added into the kernel, and the kernel then controls the data transmission of this IIC bus through this adap.


i2c_add_numbered_adapter(&i2c->adap);


12. IIC data transmission function s3c24xx_i2c_xfer

Configuring IIC pins

if (pdata->cfg_gpio)

pdata->cfg_gpio(to_platform_device(i2c->dev));


2. Call the s3c24xx_i2c_doxfer function to transfer data. Repeat the operation up to 2 times. Exit if successful and continue the operation if failed.

adap->retries = 2。


for (retry = 0; retry < adap->retries; retry++) 

{

ret = s3c24xx_i2c_doxfer(i2c, msgs, num);

if (ret != -EAGAIN)

return right;

udelay(100);

}


13. IIC bus sends data

The IIC bus calls s3c24xx_i2c_xfer to send data, and the s3c24xx_i2c_xfer function calls s3c24xx_i2c_doxfer.

1. Wait until IIC is not busy.


ret = s3c24xx_i2c_set_master(i2c);


2. Set the IIC status flag


i2c->state   = STATE_START;


3. Enable IIC interrupt


s3c24xx_i2c_enable_irq(i2c);


IIC bus starts transmission

s3c24xx_i2c_message_start(i2c, msgs);


5. Enter sleep mode and wait for IIC's waiting queue to wake up.


timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);


6. Wait for the interrupt to occur. The interrupt function is registered in s3c24xx_i2c_probe.


ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED, dev_name(&pdev->dev), i2c);


7. Interrupt occurs, enter interrupt

insert image description here

8. Enter case STATE_START and determine whether the last received 1-bit data is ASK. If no ASK is received, exit IIC transmission (assuming that I2C_M_IGNORE_NAK mode is not set)


if (iicstat & S3C2410_IICSTAT_LASTBIT && !(i2c->msg->flags & I2C_M_IGNORE_NAK)) 

{

        /* ack was not received... */

        dev_dbg(i2c->dev, "ack was not receivedn");

        s3c24xx_i2c_stop(i2c, -ENXIO);

        goto out_ack;

}


9. Determine whether to read or write IIC messages


if (i2c->msg->flags & I2C_M_RD)

        i2c->state = STATE_READ;

else

        i2c->state = STATE_WRITE;


Here is writing IIC, i2c->state = STATE_WRITE;


10. Determine whether this message is the last one. If so, stop IIC transmission. Otherwise, enter case STATE_WRITE. This is usually used when matching devices with i2c.


if (is_lastmsg(i2c) && i2c->msg->len == 0) 

{

        s3c24xx_i2c_stop(i2c, 0);

        goto out_ack;

}


11. If the data of this msg has not been sent out, continue to send it, one byte at a time, which is to assign a value to the register S3C2410_IICDS.

[1] [2]
Keywords:Linux  I2C  driver Reference address:Linux I2C driver detailed explanation

Previous article:mini2440 linuxi2c driver
Next article:Analysis and study of mini2440 I2C driver (Part 2)

Latest Microcontroller Articles
Change More Related Popular Components

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号