本文基于mini2440开发板,Linux版本号是:linux-2.6.32.2
一.IIC总线device 硬件信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32#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, };
其中,IIC寄存器的基地址为0x54000000。
s3c3440芯片的寄存器的地址如下:
二.IIC总线device注册
IIC总线device包含在mini2440_devices中,如下图所示:
将包含usb,lcd,i2c,nand等设备的mini2440_devices数组当成platform设备注册到内核
1
2platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices))
三.IIC总线driver的注册
IIC总线driver的注册调用的是platform_driver_register函数,IIC总线driver注册是platform总线会去自动匹配相应的device,匹配的规则是:
- 先根据id_table来匹配
- 再根据device的名称来匹配
这个IICdriver可以匹配两种IIC device,如下所示:
四. IIC总线driver和device的匹配
IIC总线的driver和device匹配上后,会执行driver的probe函数,probe函数中执行了如下动作:
- 获取i2c时钟,并使能时钟
1
2
3
4
5
6
7
8i2c->clk = clk_get(&pdev->dev, "i2c"); if (IS_ERR(i2c->clk)) { dev_err(&pdev->dev, "cannot get clockn"); ret = -ENOENT; goto err_noclk; } clk_enable(i2c->clk);
- 获取IIC寄存器的虚拟地址
1
2i2c->regs = ioremap(res->start, resource_size(res));
- 获取IIC中断,并申请IIC中断
1
2
3
4
5
6
7i2c->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初始化,设置时钟,使能中断,使能ask, 设置寄存器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34static 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 :获取platform 设备数据 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... */ //设置时钟 if (s3c24xx_i2c_clockrate(i2c, &freq) != 0) { writel(0, i2c->regs + S3C2410_IICCON); dev_err(i2c->dev, "cannot meet bus frequency requiredn"); return -EINVAL; } /* 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; }
- 注册 IIC adapter,在注册adapter之前要设置adap的名字,算法,父节点等信息
1
2
3
4
5
6
7
8
9
10
11strlcpy(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);
五.使能IIC总线应答
需要把 IICCON寄存器的第7位设置为1。
1
2
3
4
5
6
7
8#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); }
六. 使能IIC总线中断
需要把 IICCON寄存器的第5位设置为1。
1
2
3
4
5
6
7
8#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); }
七.获取IIC总线的忙状态
读 IICSTAT寄存器的第5位,1表示忙,0表示空闲
1
2
3
4
5#define S3C2410_IICSTAT_BUSBUSY (1<<5) iicstat = readl(i2c->regs + S3C2410_IICSTAT); return (iicstat & S3C2410_IICSTAT_BUSBUSY);
八. IIC硬件中断
- IIC中断产生的条件
①一个字节的数据发送或者接收完成产生硬件中断。
②发出从设备地址,成功匹配到从设备后产生硬件中断。
③总线仲裁失败产生中断。 - 正常情况下IIC中断产生的时机
发送数据时,IICDS移位寄存器向SDA一位一位发出电平信号,先发高位,循环8次后,数据发送完成, 接收到ASK信号后产生中断。
读数据时,IICDS移位寄存器从SDA线一位一位读入电平信号,先读高位,循环8次后,数据接收完成,产生中断。
3.中断处理过程中又来了新的IIC数据
产生硬件中断后,会设置中断挂起flag,同时将SCL拉低,IIC传输暂停,以防在中断处理过程中又来了新的IIC信号。
等待中断函数处理完成后,清除中断挂起flag,释放SCL,IIC继续发送和接收数据。
清除中断挂起flag,设置 IICCON寄存器的第4位为0。
1
2
3
4
5#define S3C2410_IICCON_IRQPEND (1<<4) tmp = readl(i2c->regs + S3C2410_IICCON); tmp &= ~S3C2410_IICCON_IRQPEND; writel(tmp, i2c->regs + S3C2410_IICCON);
九. IIC总线启动
- 使能IIC应答
1
2s3c24xx_i2c_enable_ack(i2c);
2.使能IIC数据输入输出
1
2
3#define S3C2410_IICSTAT_TXRXEN (1<<4) stat |= S3C2410_IICSTAT_TXRXEN;
3.配置主机读写模式,若是读,设备地址最低位或上1
1
2
3
4
5
6
7
8
9
10
11
12#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.写从设备地址到IICDS寄存器
1
2writeb(addr, i2c->regs + S3C2410_IICDS);
5.开始IIC传输
1
2
3stat |= S3C2410_IICSTAT_START; writel(stat, i2c->regs + S3C2410_IICSTAT);
十. IIC总线停止传输
1.需要向IICSTART寄存器的第5位写0
1
2
3
4
5unsigned long iicstat = readl(i2c->regs + S3C2410_IICSTAT); /* stop the transfer */ iicstat &= ~S3C2410_IICSTAT_START; writel(iicstat, i2c->regs + S3C2410_IICSTAT);
2.禁止IIC中断
1
2s3c24xx_i2c_disable_irq(i2c);
十一. IIC总线的algo
1
2
3
4
5static const struct i2c_algorithm s3c24xx_i2c_algorithm = { .master_xfer = s3c24xx_i2c_xfer, .functionality = s3c24xx_i2c_func, };
s3c24xx_i2c_algorithm赋值给adap.algo
1
2i2c->adap.algo = &s3c24xx_i2c_algorithm;
该adap添加进内核,后面内核通过该adap控制这个IIC总线的数据传输。
1
2i2c_add_numbered_adapter(&i2c->adap);
十二. IIC的数据传输函数s3c24xx_i2c_xfer
- 配置IIC引脚
1
2
3if (pdata->cfg_gpio) pdata->cfg_gpio(to_platform_device(i2c->dev));
2.调用s3c24xx_i2c_doxfer函数传输数据,最多重复操作2次,成功了就退出,失败了就继续操作。
adap->retries = 2。
1
2
3
4
5
6
7
8for (retry = 0; retry < adap->retries; retry++) { ret = s3c24xx_i2c_doxfer(i2c, msgs, num); if (ret != -EAGAIN) return ret; udelay(100); }
十三. IIC 总线发数据
IIC总线调用s3c24xx_i2c_xfer发送数据,s3c24xx_i2c_xfer函数又调用s3c24xx_i2c_doxfer。
1.等待IIC不忙。
1
2ret = s3c24xx_i2c_set_master(i2c);
2.设置IIC状态标志位
1
2i2c->state = STATE_START;
3.使能IIC中断
1
2s3c24xx_i2c_enable_irq(i2c);
- IIC总线启动传输
1
2s3c24xx_i2c_message_start(i2c, msgs);
5.进入睡眠,等待IIC的等待队列唤醒。
1
2timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);
6.等待中断发生,中断函数在s3c24xx_i2c_probe里面注册。
1
2ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED, dev_name(&pdev->dev), i2c);
7.中断发生,进入中断
8.进入case STATE_START,判断最后接收到1bit数据是不是ASK, 没有收到ask,退出IIC传输(前提是没有设置I2C_M_IGNORE_NAK模式)
1
2
3
4
5
6
7
8if (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.判断是读IIC消息还是写IIC消息
1
2
3
4
5if (i2c->msg->flags & I2C_M_RD) i2c->state = STATE_READ; else i2c->state = STATE_WRITE;
在这里是写IIC,i2c->state = STATE_WRITE;
10.判断该消息是不是最后一个消息,若是,停止IIC传输,否则,进入case STATE_WRITE。这里通常是i2c匹配设备的时候使用。
1
2
3
4
5
6if (is_lastmsg(i2c) && i2c->msg->len == 0) { s3c24xx_i2c_stop(i2c, 0); goto out_ack; }
11.若这个msg的数据没有发完,继续发,每次发一个byte,就是给寄存器S3C2410_IICDS赋值。
1
2
3
4
5
6
7if (!is_msgend(i2c)) { byte = i2c->msg->buf[i2c->msg_ptr++]; writeb(byte, i2c->regs + S3C2410_IICDS); ndelay(i2c->tx_setup); }
- 若这个msg发完了,看看是不是最后一个msg。如果不是,指向下一个msg。判断下一个msg是不是变成读了,就是msg改变方向了。如果是读,要设置I2C_M_NOSTART标志,表示IIC重新发起一个起始信号,否则IIC停止传输。如果还是写,继续写下一个消息。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26else if (!is_lastmsg(i2c)) { /* we need to go to the next i2c message */ dev_dbg(i2c->dev, "WRITE: Next Messagen"); i2c->msg_ptr = 0; i2c->msg_idx++; i2c->msg++; /* check to see if we need to do another message */ 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 */ s3c24xx_i2c_stop(i2c, -EINVAL); } goto retry_write; } else { /* send the new start */ s3c24xx_i2c_message_start(i2c, i2c->msg); i2c->state = STATE_START; }
- 若是所有的msg都发送完了,停止IIC传输
1
2
3
4
5
6else { /* send stop */ s3c24xx_i2c_stop(i2c, 0); }
- 写寄存器停止IIC传输
1
2
3iicstat &= ~S3C2410_IICSTAT_START; writel(iicstat, i2c->regs + S3C2410_IICSTAT);
- IIC msg 清零
1
2
3
4
5i2c->msg_ptr = 0; i2c->msg = NULL; i2c->msg_idx++; i2c->msg_num = 0;
- 唤醒等待队列
1
2wake_up(&i2c->wait);
17.禁止中断产生。
1
2s3c24xx_i2c_disable_irq(i2c);
18.清除中断挂起标志位。
1
2
3
4
5#define S3C2410_IICCON_IRQPEND (1<<4) tmp = readl(i2c->regs + S3C2410_IICCON); tmp &= ~S3C2410_IICCON_IRQPEND; writel(tmp, i2c->regs + S3C2410_IICCON);
十四. IIC总线收数据
IIC总线调用s3c24xx_i2c_xfer收数据,s3c24xx_i2c_xfer函数又调用s3c24xx_i2c_doxfer。
1.等待IIC不忙。
1
2ret = s3c24xx_i2c_set_master(i2c);
2.设置IIC状态标志位
1
2i2c->state = STATE_START;
3.使能IIC中断
1
2s3c24xx_i2c_enable_irq(i2c);
- IIC总线启动传输
1
2s3c24xx_i2c_message_start(i2c, msgs);
5.进入睡眠,等待IIC的等待队列唤醒。
1
2timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);
6.等待中断发生,中断函数在s3c24xx_i2c_probe里面注册。
1
2ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED, dev_name(&pdev->dev), i2c);
7.中断发生,进入中断
8.进入case STATE_START,判断最后接收到1bit数据是不是ASK, 没有收到ask,退出IIC传输(前提是没有设置I2C_M_IGNORE_NAK模式)
1
2
3
4
5
6
7
8if (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.判断是读IIC消失还是写IIC消息
1
2
3
4
5if (i2c->msg->flags & I2C_M_RD) i2c->state = STATE_READ; else i2c->state = STATE_WRITE;
在这里是读IIC,i2c->state = STATE_READ,跳到prepare_read执行。这里为什么跳过byte = readb(i2c->regs + S3C2410_IICDS);i2c->msg->buf[i2c->msg_ptr++] = byte 直接到prepare_read执行始终没有想明白。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38case 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 */ byte = readb(i2c->regs + S3C2410_IICDS); i2c->msg->buf[i2c->msg_ptr++] = byte; prepare_read: if (is_msglast(i2c)) { /* last byte of buffer */ 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 */ if (is_lastmsg(i2c)) { /* last message, send stop and complete */ dev_dbg(i2c->dev, "READ: Send Stopn"); s3c24xx_i2c_stop(i2c, 0); } else { /* go to the next transfer */ dev_dbg(i2c->dev, "READ: Next Transfern"); i2c->msg_ptr = 0; i2c->msg_idx++; i2c->msg++; } }
10.下面的分析同写数据一样。
十五. 从设备device注册
1.设置从设备地址
1
2info.addr = setup->i2c_address;
2.设置从设备名称
1
2strlcpy(info.type, "wm8510", I2C_NAME_SIZE);
3.根据IIC index获取IIC adapter
1
2adapter = i2c_get_adapter(setup->i2c_bus);
4.注册IIC device
1
2client = i2c_new_device(adapter, &info);
十六. IIC从设备driver注册
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20static const struct i2c_device_id ds1682_id[] = { { "ds1682", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, ds1682_id); static struct i2c_driver ds1682_driver = { .driver = { .name = "ds1682", }, .probe = ds1682_probe, .remove = ds1682_remove, .id_table = ds1682_id, }; static int __init ds1682_init(void) { return i2c_add_driver(&ds1682_driver); }
十七. IIC从设备device和driver匹配
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39static int ds1682_probe(struct i2c_client *client, const struct i2c_device_id *id) { int rc; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) { dev_err(&client->dev, "i2c bus does not support the ds1682n"); rc = -ENODEV; goto exit; } rc = sysfs_create_group(&client->dev.kobj, &ds1682_group); if (rc) goto exit; 在sys目录下创建bin节点文件,用户可以同此节点文件来操作eeprom,并提供操作方法(read,write) rc = sysfs_create_bin_file(&client->dev.kobj, &ds1682_eeprom_attr); if (rc) goto exit_bin_attr; return 0; exit_bin_attr: sysfs_remove_group(&client->dev.kobj, &ds1682_group); exit: return rc; } static struct bin_attribute ds1682_eeprom_attr = { .attr = { .name = "eeprom", .mode = S_IRUGO | S_IWUSR, }, .size = DS1682_EEPROM_SIZE, .read = ds1682_eeprom_read, .write = ds1682_eeprom_write, };
十八. 对IIC从设备的读写,最终调用的是smbus_xfer函数
获取i2c_client对象的函数,获取了i2c_client就可以在driver中对IIC从设备进行读写。
1
2
3struct i2c_client *client = to_i2c_client(dev); struct i2c_client *client = kobj_to_i2c_client(kobj);
1
2
3
4
5
6
7
8
9
10ds1682_eeprom_write i2c_smbus_write_i2c_block_data i2c_smbus_xfer adapter->algo->smbus_xfer ds1682_eeprom_read i2c_smbus_read_i2c_block_data i2c_smbus_xfer adapter->algo->smbus_xfer
最后
以上就是背后大象最近收集整理的关于Linux I2C驱动详解的全部内容,更多相关Linux内容请搜索靠谱客的其他文章。
发表评论 取消回复