此文件是I2C适配器的驱动加载文件,设备加载文件为bsp文件,如果使用的是mini2440,则为mach-mini2440.c
程序运行流程:
1、module_init(i2c_adap_s3c_init); 模块初始化函数登记。
2、驱动启动后自动执行:
static int __init i2c_adap_s3c_init(void)
{
int ret;
//平台驱动注册
ret = platform_driver_register(&s3c2410_i2c_driver);
if (ret == 0) {
ret = platform_driver_register(&s3c2440_i2c_driver);
if (ret)
platform_driver_unregister(&s3c2410_i2c_driver);
}
return ret;
}
这里使用的是平台驱动,也就是使用platform_driver_register注册一个平台driver,这个driver的结构体注册为s3c2410_i2c_driver。
3、平台驱动结构体s3c2410_i2c_driver
static struct platform_device_id s3c24xx_driver_ids[] = {
{
.name
= "s3c2410-i2c",
.driver_data
= TYPE_S3C2410,
}, {
.name
= "s3c2440-i2c",
.driver_data
= TYPE_S3C2440,
}, { },
};
MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids);
static struct platform_driver s3c24xx_i2c_driver = {
.probe
= s3c24xx_i2c_probe,
.remove
= s3c24xx_i2c_remove,
.id_table
= s3c24xx_driver_ids,
.driver
= {
.owner
= THIS_MODULE,
.name
= "s3c-i2c",
.pm
= S3C24XX_DEV_PM_OPS,
},
};
其中包含了平台驱动用到的几个函数,probe、remove、resume,其中probe是加载driver后调用以初始化设备的,结构体中的id_table中的name是用来和device也就是适配器设备配对的,两者名字一样即配对成功,在有的版本的内核中,driver结构体中没有.id_table的赋值,与device的配对是通过driver中的name来进行配对的。
4、i2c总线初始化函数s3c24xx_i2c_probe(就是之前平台驱动结构体中定义的probe函数)
这个函数的主要目的是对I2C总线进行初始化,方法是:1、先声明一个s3c24xx_i2c结构体 然后对其初始化一些基本信息,这些信息在&s3c24xx_i2c里面,第一条语句就是干这个的,我们可以看到&s3c24xx_i2c里面就包含信息 .name = "s3c2410-i2c", 正好和之前驱动结构体里面的name吻合(这个是i2c_adper结构中的成员,也就是说一个适配器与一个平台设备相匹配)。2、将传入的平台结构体(其他程序调用驱动的时候应该会付给一个平台结构体)中的一些不用修改的信息付给i2c(也就是s3c24xx_i2c结构体),这样就构造出一个我们想要的i2c结构体,其中包含的是完整信息,然后再将i2c付给平台结构体,使用platform_set_drvdata(pdev, i2c);这条语句,这样平台结构就关联上了I2C数据。
static int s3c24xx_i2c_probe(struct platform_device *pdev)
{
//获取设备数据,该数据在本文件的一开始定义
struct s3c24xx_i2c *i2c = &s3c24xx_i2c;
struct resource *res;
int ret;
//I2C设备关联到平台设备
i2c->dev = &pdev->dev;
i2c->clk = clk_get(&pdev->dev, "i2c"); //获取设备的时钟
if (IS_ERR(i2c->clk)) {
dev_err(&pdev->dev, "cannot get clock\n");
ret = -ENOENT;
goto err_noclk;
}
dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk);
clk_enable(i2c->clk); //时钟有效
//获取平台的IO内存资源
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) { //资源为空,出错
dev_err(&pdev->dev, "cannot find IO resource\n");
ret = -ENOENT;
goto err_clk;
}
//申请IO内存
i2c->ioarea = request_mem_region(res->start, (res->end-res->start)+1,
pdev->name);
if (i2c->ioarea == NULL) { //申请失败
dev_err(&pdev->dev, "cannot request IO\n");
ret = -ENXIO;
goto err_clk;
}
//映射IO内存
i2c->regs = ioremap(res->start, (res->end-res->start)+1);
if (i2c->regs == NULL) { //映射失败
dev_err(&pdev->dev, "cannot map IO\n");
ret = -ENXIO;
goto err_ioarea;
}
dev_dbg(&pdev->dev, "registers %p (%p, %p)\n", i2c->regs, i2c->ioarea, res);
//关联数据
i2c->adap.algo_data = i2c;
i2c->adap.dev.parent = &pdev->dev; //适配器的父设备为平台设备
//初始化I2C(IICON寄存器)
ret = s3c24xx_i2c_init(i2c);
if (ret != 0)
goto err_iomap;
//获取平台的IRQ资源
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (res == NULL) {
dev_err(&pdev->dev, "cannot find IRQ\n");
ret = -ENOENT;
goto err_iomap;
}
//申请IRQ中断
ret = request_irq(res->start, s3c24xx_i2c_irq, IRQF_DISABLED,
pdev->name, i2c);
if (ret != 0) {
dev_err(&pdev->dev, "cannot claim IRQ\n");
goto err_iomap;
}
//关联上设备的IRQ数据
i2c->irq = res;
dev_dbg(&pdev->dev, "irq resource %p (%lu)\n", res,
(unsigned long)res->start);
//添加适配器
//难怪之前分析的代码里都没有看到适配器的添加,原来是在这里添加的,由此可见,一根I2C总线上应该只有一个适配器
ret = i2c_add_adapter(&i2c->adap);
if (ret < 0) {
dev_err(&pdev->dev, "failed to add bus to i2c core\n");
goto err_irq;
}
//pdev->dev->driver_data = i2c
//即平台设备关联的驱动数据为I2C数据
platform_set_drvdata(pdev, i2c);
dev_info(&pdev->dev, "%s: S3C I2C adapter\n", i2c->adap.dev.bus_id);
return 0;
err_irq:
free_irq(i2c->irq->start, i2c); //释放中断请求
//初始化I2C失败
err_iomap:
iounmap(i2c->regs); //取消内存映射
err_ioarea:
release_resource(i2c->ioarea); //释放资源
kfree(i2c->ioarea);
err_clk:
clk_disable(i2c->clk); //禁止时钟
clk_put(i2c->clk); //本时钟的使用者减1
err_noclk:
return ret;
}
5、中断
之前的probe函数注册了中断ret = request_irq(res->start, s3c24xx_i2c_irq, IRQF_DISABLED pdev->name, i2c);从中可以看到中断的处理函数为s3c24xx_i2c_irq,,所以在进行过初始化工作以后,开始等待I2C中断的发生(查看芯片手册,中断一般发生在IICDS寄存器写入完成,以及发送完成时)。
//I2C的中断函数,这个函数在执行s3c24xx_i2c_irq函数时用request_irq注册
//irqno – 请求号
//dev_id – 设备ID
static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id)
{
struct s3c24xx_i2c *i2c = dev_id;
unsigned long status;
unsigned long tmp;
status = readl(i2c->regs + S3C2410_IICSTAT); //读状态
//总线仲裁位(0-成功, 1-失败)
if (status & S3C2410_IICSTAT_ARBITR) {
dev_err(i2c->dev, "deal with arbitration loss\n");
}
if (i2c->state == STATE_IDLE) {
//当前的I2C状态为空闲状态,出错,不该进入中断
dev_dbg(i2c->dev, "IRQ: error i2c->state == IDLE\n");
//清除挂起条件 并 恢复操作
tmp = readl(i2c->regs + S3C2410_IICCON);
tmp &= ~S3C2410_IICCON_IRQPEND;
writel(tmp, i2c->regs + S3C2410_IICCON);
goto out;
}
//继续下一字节的中断
i2s_s3c_irq_nextbyte(i2c, status);
out:
return IRQ_HANDLED;
}
这里基本就是判断中断是不是可用,是不是出错。
6、真正的中断处理,也就是IIC通讯处理是i2s_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat),这里包含了IIC通讯的全部处理
//I2C中断,根据状态机判断下一个操作
static int i2s_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", __FUNCTION__);
goto out;
break;
case STATE_STOP: //停止位发出状态
dev_err(i2c->dev, "%s: called in STATE_STOP\n", __FUNCTION__);
s3c24xx_i2c_disable_irq(i2c); //禁止I2C中断
goto out_ack;
case STATE_START: //I2C开始状态
if (iicstat & S3C2410_IICSTAT_LASTBIT //I2C最后收到的位是1(NO ACK)
&& !(i2c->msg->flags & I2C_M_IGNORE_NAK)) {//消息包可以忽略ACK
dev_dbg(i2c->dev, "ack was not received\n");
s3c24xx_i2c_stop(i2c, -EREMOTEIO); //发停止位
goto out_ack;
}
//运行到这里,说明收到了AC
//判断开始位之后的操作是读还是写?
if (i2c->msg->flags & I2C_M_RD)
i2c->state = STATE_READ; //读状态
else
i2c->state = STATE_WRITE; //写状态
//当前是最后一个包 且 包的长度为0
if (is_lastmsg(i2c) && i2c->msg->len == 0) {
s3c24xx_i2c_stop(i2c, 0); //发停止位
goto out_ack;
}
if (i2c->state == STATE_READ) //读操作,到“准备读”操作
goto prepare_read;
case STATE_WRITE: //I2C写状态
retry_write:
if (!is_msgend(i2c)) { //不是msg包的最后一个字节
byte = i2c->msg->buf[i2c->msg_ptr++]; //读出要写的字节
writeb(byte, i2c->regs + S3C2410_IICDS); //写入IICDS
ndelay(i2c->tx_setup); //等待建立时间
} else if (!is_lastmsg(i2c)) { //不是最后一个msg包
dev_dbg(i2c->dev, "WRITE: Next Message\n");
i2c->msg_ptr = 0; //当前操作的字节为首地址
i2c->msg_idx ++; //当前操作的msg包为下一个
i2c->msg++; //到下一个msg包
if (i2c->msg->flags & I2C_M_NOSTART) { //本msg包不需要发开始位
if (i2c->msg->flags & I2C_M_RD) { //本包是读操作
s3c24xx_i2c_stop(i2c, -EINVAL); //发停止位
}
goto retry_write; //写操作,连续写数据
}
else { //本msg包需要发开始位
s3c24xx_i2c_message_start(i2c, i2c->msg); //发开始位
i2c->state = STATE_START; //修改状态为开始
}
} else { //最后一个msg包的最后一个字节
s3c24xx_i2c_stop(i2c, 0); //发停止位
}
break;
case STATE_READ: //I2C读操作
//I2C读操作的最后一个字节是可以收NO ACK的,不明白的话就先看一下IIC协议
if (!(i2c->msg->flags & I2C_M_IGNORE_NAK) //不能忽略ACK
&& !(is_msglast(i2c) && is_lastmsg(i2c))) { //不是最后一个包的最后一个字节
if (iicstat & S3C2410_IICSTAT_LASTBIT) { //I2C接收到的最后一位为1
dev_dbg(i2c->dev, "READ: No Ack\n");
s3c24xx_i2c_stop(i2c, -ECONNREFUSED); //没有收到ACK,发停止位
goto out_ack;
}
}
byte = readb(i2c->regs + S3C2410_IICDS); //读取接到的字节
i2c->msg->buf[i2c->msg_ptr++] = byte;
//准备读下一字节
prepare_read:
if (is_msglast(i2c)) { //准备操作msg包的最后一个字节
if (is_lastmsg(i2c)) //当前操作的是最后一个msg包
s3c24xx_i2c_disable_ack(i2c); //禁止ACK(发NO ACK)
} else if (is_msgend(i2c)) { //当前msg包的最后一个字节操作结束
if (is_lastmsg(i2c)) { //当前操作的是最后一个msg包
dev_dbg(i2c->dev, "READ: Send Stop\n");
s3c24xx_i2c_stop(i2c, 0); //发停止位
} else { //当前操作的不是最后一个包
dev_dbg(i2c->dev, "READ: Next Transfer\n");
i2c->msg_ptr = 0; //操作的数据为第一个数据
i2c->msg_idx++; //下一个数据包
i2c->msg++;
}
}
break;
}
//当前状态为停止位发出
//没有收到ACK
//当前发的是空包
out_ack:
//清除挂起条件 并 恢复操作
tmp = readl(i2c->regs + S3C2410_IICCON);
tmp &= ~S3C2410_IICCON_IRQPEND;
writel(tmp, i2c->regs + S3C2410_IICCON);
//空闲状态下进中断,直接到这里
out:
return ret;
}
总结:这个驱动程序应该是最底层的驱动程序了,但是并没有注册设备,也就是只是注册了驱动程序,所以设备注册应该是在别的驱动程序里的,只有注册了驱动设备,才能在/dev/中有设备文件,上层程序才能通过打开/dev/中的设备文件来关联到驱动程序进而对具体设备进行操作。
问题:1、这个文件是IIC适配器驱动的加载程序,驱动的配对是通过.id_table同device中的name进行配对的,但是在driver结构体中.id_table同其本身的name是不同的,配对到底和哪个配是个问题(在IIC设备驱动加载程序中,driver中的.id_table同name是相同的,比如ft5x0x_ts中,所以不存在这个问题),难道是风格问题?