完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
3个回答
|
|
三、adapter
adapter的驱动以rk3399的i2c为例子 rk3x_i2c_soc_data struct rk3x_i2c_soc_data { int grf_offset; int (*calc_timings)(unsigned long, struct i2c_timings *, struct rk3x_i2c_calced_timings *); }; rk3x_i2c struct rk3x_i2c { struct i2c_adapter adap; struct device *dev; struct rk3x_i2c_soc_data *soc_data; }; adapter驱动骨架 static const struct i2c_algorithm rk3x_i2c_algorithm = { /* 具体的i2c数据传输回调函数 */ .master_xfer = rk3x_i2c_xfer, .functionality = rk3x_i2c_func, }; static const struct rk3x_i2c_soc_data rk3399_soc_data = { .grf_offset = -1, .calc_timings = rk3x_i2c_v1_calc_timings, }; static const struct of_device_id rk3x_i2c_match[] = { { .compatible = "rockchip,rk3399-i2c", .data = (void *)&rk3399_soc_data }, {}, }; static struct platform_driver rk3x_i2c_driver = { .probe = rk3x_i2c_probe, .remove = rk3x_i2c_remove, .driver = { .name = "rk3x-i2c", .of_match_table = rk3x_i2c_match, .pm = &rk3x_i2c_pm_ops, }, }; module_platform_driver(rk3x_i2c_driver); rk3x_i2c_probe static int rk3x_i2c_probe(struct platform_device *pdev) { struct rk3x_i2c *i2c; struct resource *mem; int ret = 0; int irq; i2c = devm_kzalloc(&pdev->dev, sizeof(struct rk3x_i2c), GFP_KERNEL); /* 匹配具体平台后设置soc_data */ match = of_match_node(rk3x_i2c_match, np); i2c->soc_data = (struct rk3x_i2c_soc_data *)match->data; /* 实现i2c总线算法 */ i2c->adap.algo = &rk3x_i2c_algorithm; /* 获取并映射寄存器资源 */ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); i2c->regs = devm_ioremap_resource(&pdev->dev, mem); /* 获取中断并设置中断处理函数 */ irq = platform_get_irq(pdev, 0); ret = devm_request_irq(&pdev->dev, irq, rk3x_i2c_irq, 0, dev_name(&pdev->dev), i2c); /* 获取和准备时钟 */ if (i2c->soc_data->calc_timings == rk3x_i2c_v0_calc_timings) { } else { i2c->clk = devm_clk_get(&pdev->dev, "i2c"); i2c->pclk = devm_clk_get(&pdev->dev, "pclk"); } ret = clk_prepare(i2c->clk); ret = clk_prepare(i2c->pclk); /* 向core层添加adapter */ ret = i2c_add_adapter(&i2c->adap); } 四、client client部分的驱动以rk808电源管理芯片为例子 client驱动骨架 static const struct of_device_id rk808_of_match[] = { /* 跟dts文件的i2c上挂载的设备节点一致 */ { .compatible = "rockchip,rk808" }, { }, }; MODULE_DEVICE_TABLE(of, rk808_of_match); static const struct i2c_device_id rk808_ids[] = { { "rk808" }, { }, }; MODULE_DEVICE_TABLE(i2c, rk808_ids); static struct i2c_driver rk808_i2c_driver = { .driver = { .name = "rk808", .of_match_table = rk808_of_match, .pm = &rk808_pm_ops, }, .probe = rk808_probe, .remove = rk808_remove, .id_table = rk808_ids, }; module_i2c_driver(rk808_i2c_driver); module_i2c_driver 这个宏展开后最终会调用i2c_register_driver向i2c总线添加i2c_driver #define module_i2c_driver(__i2c_driver) module_driver(__i2c_driver, i2c_add_driver, i2c_del_driver) #define module_driver(__driver, __register, __unregister, ...) static int __init __driver##_init(void) { return __register(&(__driver) , ##__VA_ARGS__); } module_init(__driver##_init); static void __exit __driver##_exit(void) { __unregister(&(__driver) , ##__VA_ARGS__); } module_exit(__driver##_exit); #define i2c_add_driver(driver) i2c_register_driver(THIS_MODULE, driver) 五、时序图 1、i2c总线注册 < > < y="0" rx="3" ry="3" > < y="32.5" > i2c_core < > < y="0" rx="3" ry="3" > < y="32.5" > base/bus < > < > < y="93" > bus_register < > < y="110" rx="0" ry="0" > < y="139" > 向系统注册i2c < x="316"> 总线,设置mat < x="316"> ch和probe回调 < y="218" > < > < y="245" rx="3" ry="3" > < y="277.5" > i2c_core < y="245" rx="3" ry="3" > < y="277.5" > base/bus |
|
|
|
一、概述
本文讲述如何从源代码分析i2c驱动架构,i2c的主体包含i2c总线、adapter设备和client设备。总线由i2c-core.c实现;adapter是具体的芯片i2c控制器,rk3399的adapter驱动由i2c-rk3x.c实现;client则是具体的i2c设备,如使用i2c通信的电源管理芯片rk808的驱动为rk808.c。 二、core core主要实现一些与具体soc平台和具体i2c设备无关的接口,如adapter的注册、i2c设备注册、i2c总线初始化、和i2c设备读写功能等。读写通过底层回调接口实现。 1、core接口 i2c_init 这个接口主要向系统初始化i2c总线,设置总线操作的一些回调 struct bus_type i2c_bus_type = { .name = "i2c", .match = i2c_device_match, .probe = i2c_device_probe, .remove = i2c_device_remove, .shutdown = i2c_device_shutdown, }; EXPORT_SYMBOL_GPL(i2c_bus_type); static int __init i2c_init(void) { int retval; /* 注册i2c总线 */ retval = bus_register(&i2c_bus_type); } i2c_device_match 这个回调主要用于匹配i2c总线上的设备和驱动 static int i2c_device_match(struct device *dev, struct device_driver *drv) { /* 获取对应的i2c_client */ struct i2c_client *client = i2c_verify_client(dev); struct i2c_driver *driver; /* 使用of_match_table尽心匹配, */ if (of_driver_match_device(dev, drv)) return 1; /* 获取i2c驱动实例 */ driver = to_i2c_driver(drv); /* 使用dts具体i2c设备节点的compatible属性后半部和i2c_driver的id_table对应字段做对比,一样就返回真。 如rk808的compatible为"rockchip,rk808",而rk808驱动的id_table包含"rk808" */ if (driver->id_table) return i2c_match_id(driver->id_table, client) != NULL; } i2c_device_probe 这个回调主要在驱动和设备匹配成功后,调用驱动的probe回调执行一些初始化操作 static int i2c_device_probe(struct device *dev) { struct i2c_client *client = i2c_verify_client(dev); struct i2c_driver *driver; int status; if (!client->irq) { int irq = -ENOENT; if (dev->of_node) { irq = of_irq_get_byname(dev->of_node, "irq"); } /* 获取中断源 */ client->irq = irq; } /* 获取client的驱动, 并执行驱动的probe回调,比如rk808的rk808_probe函数 */ driver = to_i2c_driver(dev->driver); status = driver->probe(client, i2c_match_id(driver->id_table, client)); } 2、对接adapter的接口 i2c_adapter 这个结构描述一个i2c适配器,指的就是soc上的具体i2c控制器,控制器驱动需要实现i2c读写时需要用到的核心算法回调 struct i2c_adapter { const struct i2c_algorithm *algo; /* the algorithm to access the bus */ void *algo_data; struct device dev; /* the adapter device */ }; i2c_client 这个结构描述具体的i2c设备,它一般会包含地址和关联的适配器指针 struct i2c_client { unsigned short addr; /* chip address - NOTE: 7bit */ /* addresses are stored in the */ /* _LOWER_ 7 bits */ char name[I2C_NAME_SIZE]; struct i2c_adapter *adapter; /* the adapter we sit on */ struct device dev; /* the device structure */ int irq; /* irq issued by device */ }; i2c_add_adapter 这个接口实现如何向i2c总线添加一个适配器,并根据dts向总线添加一些列的i2c设备 int i2c_add_adapter(struct i2c_adapter *adapter) { if (dev->of_node) { /* 根据dts的alias获取后面的索引数字,可以查看rk3399.dtsi的alias定义 */ id = of_alias_get_id(dev->of_node, "i2c"); if (id >= 0) { adapter->nr = id; return __i2c_add_numbered_adapter(adapter); } } } __i2c_add_numbered_adapter static int __i2c_add_numbered_adapter(struct i2c_adapter *adap) { return i2c_register_adapter(adap); } i2c_register_adapter 该函数首先注册adapter设备,并根据adapter的dts节点描述遍历adapter上挂载的i2c设备,并一一添加到i2c总线 static int i2c_register_adapter(struct i2c_adapter *adap) { /* 设置设备名称 */ dev_set_name(&adap->dev, "i2c-%d", adap->nr); /* 把adapter设备注册到i2c总线,因为adapter不是client所以,匹配总是失败 */ adap->dev.bus = &i2c_bus_type; adap->dev.type = &i2c_adapter_type; res = device_register(&adap->dev); /* 根据dts描述注册adapter上的所有i2c设备 */ of_i2c_register_devices(adap); } of_i2c_register_devices static void of_i2c_register_devices(struct i2c_adapter *adap) { struct device_node *node; for_each_available_child_of_node(adap->dev.of_node, node) { if (of_node_test_and_set_flag(node, OF_POPULATED)) continue; /* 注册单个i2c设备 */ of_i2c_register_device(adap, node); } } of_i2c_register_device static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap, struct device_node *node) { struct i2c_client *result; struct i2c_board_info info = {}; struct dev_archdata dev_ad = {}; const __be32 *addr_be; u32 addr; /* 解析设备名字 */ if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) { } /* 解析地址 */ addr_be = of_get_property(node, "reg", &len); addr = be32_to_cpup(addr_be); info.addr = addr; info.of_node = of_node_get(node); info.archdata = &dev_ad; /* 创建新的i2c client并注册到i2c总线 */ result = i2c_new_device(adap, &info); } i2c_new_device struct i2c_client * i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info) { struct i2c_client *client; int status; client = kzalloc(sizeof *client, GFP_KERNEL); /* 关联client设备挂载的adapter设备 */ client->adapter = adap; /* 设置地址 */ client->addr = info->addr; /* 设置中断源*/ client->irq = info->irq; /* 设置父设备节点为关联的adapter设备 */ client->dev.parent = &client->adapter->dev; client->dev.bus = &i2c_bus_type; /* 设置类型,i2c_verify_client要用 */ client->dev.type = &i2c_client_type; client->dev.of_node = info->of_node; /* 正式注册到i2c总线 */ status = device_register(&client->dev); } 3、对接client的接口 i2c_driver 这个结构描述一个i2c驱动,是我们主要需要实现和填充的 struct i2c_driver { int (*probe)(struct i2c_client *, const struct i2c_device_id *); int (*remove)(struct i2c_client *); struct device_driver driver; const struct i2c_device_id *id_table; }; i2c_register_driver 这个接口实现如何向i2c总线注册一个i2c驱动 int i2c_register_driver(struct module *owner, struct i2c_driver *driver) { int res; driver->driver.owner = owner; /* 设置其关联i2c总线 */ driver->driver.bus = &i2c_bus_type; /* 一个驱动可以服务相同类型的设备 */ INIT_LIST_HEAD(&driver->clients); /* 正式向i2c总线注册i2c client设备 */ res = driver_register(&driver->driver); } |
|
|
|
2、adapter注册
< > < y="0" rx="3" ry="3" > < y="32.5" > i2c_rk3x < > < y="0" rx="3" ry="3" > < y="32.5" > i2c_core < > < y="0" rx="3" ry="3" > < y="32.5" > base/core < > < > < y="93" > 获取寄存器、中断和时钟资源 < > < y="158" > i2c_add_adapter < > < y="175" rx="0" ry="0" > < y="204" > 向i2c总线注册 < x="316"> adapter设备 < y="261" > __i2c_add_numbered_adapter < > < y="326" > i2c_register_adapter < > < y="391" > device_register < > < y="408" rx="0" ry="0" > < y="437" > 正式把adapt < x="516"> er设备注册到总线 < y="494" > < > < y="529" > of_i2c_register_devices < > < y="594" > of_i2c_register_device < > < y="659" > i2c_new_device < > < y="724" > device_register < > < y="741" rx="0" ry="0" > < y="770" > 把adapter上 < x="516"> 挂载的设备注册到总 < x="516"> 线 < y="849" > < > < y="884" > < > < y="911" rx="3" ry="3" > < y="943.5" > i2c_rk3x < y="911" rx="3" ry="3" > < y="943.5" > i2c_core < y="911" rx="3" ry="3" > < y="943.5" > base/core 3、client驱动注册 < > < y="0" rx="3" ry="3" > < y="32.5" > rk808 < > < y="0" rx="3" ry="3" > < y="32.5" > i2c_core < > < y="0" rx="3" ry="3" > < y="32.5" > base/dirver < > < > < y="93" > i2c_register_driver < > < y="110" rx="0" ry="0" > < y="139" > 向i2c总线注册c < x="316"> lient设备的驱动 < y="196" > driver_register < > < y="213" rx="0" ry="0" > < y="242" > 这里触发系统驱动框架 < x="516"> 调用i2c总线mat < x="516"> ch和probe回调 < y="321" > < > < y="356" > < > < y="383" rx="3" ry="3" > < y="415.5" > rk808 < y="383" rx="3" ry="3" > < y="415.5" > i2c_core < y="383" rx="3" ry="3" > < y="415.5" > base/dirver 4、i2c_client和i2c_driver匹配 通过driver_register注册i2c_driver会触发系统驱动框架调用i2c总线的match和probe < > < y="0" rx="3" ry="3" > < y="32.5" > base/dd < > < y="0" rx="3" ry="3" > < y="32.5" > i2c_core < > < y="0" rx="3" ry="3" > < y="32.5" > of/device < > < y="0" rx="3" ry="3" > < y="32.5" > rk808 < > < > < y="93" > i2c_device_match < > < y="128" > of_match_device < > < y="163" > < > < y="198" > i2c_match_id < > < y="245" rx="0" ry="0" > < y="274" > 对比i2c_driv < x="316"> er的id_tabl < x="316"> es只有存在才返回真 < y="353" > < > < y="388" > i2c_device_probe < > < y="405" rx="0" ry="0" > < y="434" > dd通过driver_p < x="316"> robe_device调用 < y="491" > rk808_probe < > < y="526" > < > < y="561" > < > < y="588" rx="3" ry="3" > < y="620.5" > base/dd < y="588" rx="3" ry="3" > < y="620.5" > i2c_core < y="588" rx="3" ry="3" > < y="620.5" > of/device < y="588" rx="3" ry="3" > < y="620.5" > rk808 |
|
|
|
你正在撰写答案
如果你是对答案或其他答案精选点评或询问,请使用“评论”功能。
1906 浏览 1 评论
synopsys 的design ware:DW_fpv_div,浮点数除法器,默认32位下,想提升覆盖率(TMAX),如果用功能case去提升覆盖率呢?
2382 浏览 1 评论
RK3588 GStreamer调试四路鱼眼摄像头四宫格显示报错
5175 浏览 1 评论
【飞凌嵌入式OK3576-C开发板体验】RKNN神经网络-YOLO图像识别
254 浏览 0 评论
【飞凌嵌入式OK3576-C开发板体验】SSH远程登录网络配置及CAN通讯
1336 浏览 0 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-22 00:54 , Processed in 0.650958 second(s), Total 74, Slave 58 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号