从总线模型看IIC架构

1、IIC总线:

struct bus_type i2c_bus_type = {

.name= "i2c",

.match= i2c_device_match,
.probe = i2c_device_probe,
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
.pm = &i2c_device_pm_ops,

};

调用位置iic-core.c:

i2c_init(void)

retval = bus_register(&i2c_bus_type);


IIC设备模型:

i2c_new_device(i2c_client结构体)

i2c_register_adapter(i2c_adapter结构体)


IIC驱动模型:

i2c_register_driver(i2c_driver结构体)


前两者是设备模型,后一个是驱动模型。


2、i2c_register_adapter是适配器,对应我们开发板的IIC控制器:

adap->dev.bus = &i2c_bus_type;
adap->dev.type = &i2c_adapter_type;


而i2c_new_device是我们的IIC设备了,如IIC芯片:

client->dev.bus = &i2c_bus_type;
client->dev.type = &i2c_client_type;


最后i2c_register_driver是我们的IIC驱动,一般由i2c_add_driver进行包装,使用的结构体是i2c_driver,这个主要用来实现IIC协议。



3、现在我们来看框架:

i2c_register_driver驱动注册过程:

static struct i2c_driver at24_driver = {
.driver = {
.name = "at24",
.owner = THIS_MODULE,
},
.probe = at24_probe,
.remove = __devexit_p(at24_remove),
.id_table = at24_ids,
};


然后通过i2c_add_driver(&at24_driver)注册:

i2c_register_driver

res = driver_register(&driver->driver); //挂到IIC总线驱动链表,从IIC设备链表进行匹配,注意这里只针对IIC设备,对IIC适配器没有用,这点看IIC总线匹配函数就知道。匹配成功的话,调用驱动probe函数,由于当前没有IIC设备,所以不会调用。


2、IIC设备注册过程:

首先是适配器,因为适配器是IIC控制器,是核心所在。注意适配器有编号的adapter->nr,IIC设备是根据编号来挂到适配器的。

这部分是通过平台总线模型来实现的,所以这部分是驱动工程师负责的地方,具体如下:

平台设备:

static struct resource s3c_i2c_resource[] = {
[0] = {
.start = S3C_PA_IIC1,
.end   = S3C_PA_IIC1 + SZ_4K - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_IIC1,
.end   = IRQ_IIC1,
.flags = IORESOURCE_IRQ,
},
};


struct platform_device s3c_device_i2c1 = {
.name  = "s3c-i2c",
.id  = 1,
.num_resources = ARRAY_SIZE(s3c_i2c_resource),
.resource  = s3c_i2c_resource,
};

而平台驱动:

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,
},
};

这样以匹配就调用s3c24xx_i2c_probe函数:

s3c24xx_i2c_probe函数任务:

1、设置s3c24xx_i2c结构体:

         i2c->adap.algo    = &s3c24xx_i2c_algorithm;  //底层操作函数,主要包括传输函数等。

        获取硬件信息填充结构体,注册中断

         platform_set_drvdata(pdev, i2c);

2、ret = i2c_add_numbered_adapter(&i2c->adap);  //通过i2c_register_adapter注册适配器,

注意i2c_add_numbered_adapter里的i2c_scan_static_board_info函数,这个函数回去__i2c_board_list链表找是否有IIC设备使用了本适配器,如果有要挂到本适配器,而该链表是通过i2c_register_board_info函数来实现的!所以对于IIC设备的注册有多种方式,一种直接i2c_new_device,一种就是这种情况,通过i2c_register_board_info函数实现,不过还有其他方式,这里就略去。再者i2c_add_numbered_adapter会调用device_register进而和IIC总线驱动匹配,不过不会匹配成功的,原因如下:

i2c_device_match

     i2c_verify_client(dev);

              (dev->type == &i2c_client_type)? to_i2c_client(dev): NULL; //适配器dev->type =i2c_adapter_type,返回NULL,不进行匹配!!



接着就是IIC设备注册,针对于IIC芯片之类的:

i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)

1、设置struct i2c_client*client;结构体

client->adapter = adap;  //适配器

client->addr = info->addr;//地址

status = i2c_check_client_addr_validity(client); //检查地址是否有效,手段:发一个开始信号看看对方是否回应

strlcpy(client->name, info->type, sizeof(client->name)); //设备名字

status = device_register(&client->dev); 这个就重要啦,又来匹配了,并且IIC驱动前面已经有了,所以匹配成功,调用驱动的probe函数!

2、要先填充i2c_board_info结构体,然后实现第1的赋值。



现在看看驱动的probe函数:

at24_probe:

1、struct at24_data *at24设置

at24->bin.read = at24_bin_read;  //最终将会调用底层的读函数

at24->bin.write = at24_bin_write;

err = sysfs_create_bin_file(&client->dev.kobj, &at24->bin); //通过创建文件实现读写,这个例子比较特殊,我们可以实现为file_operation来读写(创建设备文件)。

i2c_set_clientdata(client, at24);  //at24放到clien结构体里的私有数据。


到这里就OK了。


总结一下:

IIC适配器注册:

适配器对应于我们的IIC控制器,通过平台总线实现的,主要就是硬件相关的操作,尤其是底层的读写操作之类的,这些操作供给IIC驱动层的操作函数调用。


IIC的设备注册:

strlcpy(info.type, "wm8990", I2C_NAME_SIZE);  //设置i2c_board_info

adapter = i2c_get_adapter(setup->i2c_bus); //这点说明适配器必须先注册,设备是挂在适配器上

client = i2c_new_device(adapter, &info);  //注册设备,挂到适配器,匹配IIC总线的驱动,如果存在调用驱动probe

i2c_put_adapter(adapter)


IIC驱动注册:

static int __init at24_init(void)
{
io_limit = rounddown_pow_of_two(io_limit);
return i2c_add_driver(&at24_driver);  //i2c_driver结构体
}

实现了协议层的操作:

static ssize_t at24_read(struct at24_data *at24,
char *buf, loff_t off, size_t count)
{


}

static ssize_t at24_write(struct at24_data *at24, const char *buf, loff_t off,
 size_t count)
{

}

这些操作基底要靠适配器的底层来实现传输


最后一点:设备节点也是交给驱动层来实现


工作流程:

1、IIC驱动和IIC设备匹配,获取设备地址和名字等;

2、操作设备节点从IIC驱动函数集开始,这些最终要依靠适配器的底层操作来支撑完成!!


IIC设备层:地址和名字等

IIC驱动层:IIC协议

IIC适配器层:底层实现

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值