上篇讲了linux clock驱动,今天说说linux的reset驱动。
时钟和复位是两个不同的驱动,但通常都是由负责clock驱动的人,把reset驱动完成。同样,reset驱动也是由芯片厂商去完成的。
linux reset子系统reset子系统非常简单,与clock子系统非常类似,但在驱动实现上,reset驱动更简单。
因为clock驱动主要是时钟的实现,涉及到固定时钟、分频、门控等一些时钟的分级关系,需要弄清楚时钟树里每个时钟的关系。
而reset驱动有点相当于clock驱动的门控,它只有复位和解复位两个功能。
类似于clock子系统,reset子系统也分为了consumer和provider,结构体关系如下:
consumer :
reset api接口的使用者,内核提供了统一的reset接口:
devm_reset_control_get(struct device *dev, const char *id)//获取reset句柄reset_control_deassert(struct reset_control *rstc)//解复位reset_control_assert(struct reset_control *rstc)//复位reset_control_reset(struct reset_control *rstc)//先复位,延迟一会,然后解复位struct reset_control结构体表示一个reset句柄,驱动中使用reset api,需要先获取reset句柄
provider :
reset提供者,即reset驱动。struct reset_controller_dev结构体代表一个reset控制器,内部包含了reset操作函数集合struct reset_control_ops,注册reset驱动时,需要分配一个struct reset_controller_dev结构体,然后填充成员,最后将该结构体注册。
struct reset_controller_dev{ const struct reset_control_ops *ops;//复位控制操作函数 struct list_head list;//全局链表,复位控制器注册后挂载到全局链表 struct list_head reset_control_head;//各个模块复位的链表头 struct device *dev; int of_reset_n_cells;//dts中引用时,需要几个参数 //通过dts引用的参数,解析复位控制器中相应的参数 int (*of_xlate)(struct reset_controller_dev *rcdev, const struct of_phandle_args *reset_spec); unsigned int nr_resets;//复位设备个数}struct reset_control_ops{ int (*reset)(struct reset_controller_dev *rcdev, unsigned long id);//复位+解复位 int (*assert)(struct reset_controller_dev *rcdev, unsigned long id);//复位 int (*deassert)(struct reset_controller_dev *rcdev, unsigned long id);//解复位 int (*status)(struct reset_controller_dev *rcdev, unsigned long id);//复位状态查询}reset复位api说明devm_reset_control_getstruct reset_control *devm_reset_control_get(struct device *dev, const char *id)作用 :获取相应的reset句柄参数 :dev:指向申请reset资源的设备句柄id:指向要申请的reset资源名(字符串),可以为null返回 :成功:返回reset句柄失败:返回nullreset_control_deassertint reset_control_deassert(struct reset_control *rstc)作用 :对传入的reset资源进行解复位操作参数 :rstc:指向申请reset资源的设备句柄返回 :成功:返回0失败:返回错误码reset_control_assertint reset_control_assert(struct reset_control *rstc)作用 :对传入的reset资源进行复位操作。参数和返回值与reset_control_deassert相同
reset_control_resetint reset_control_reset(struct reset_control *rstc)作用:对传入的reset资源先进行复位操作,然后等待5us,再进行解复位操作。相当于执行了一遍reset_control_assert后,然后delay一会,再调用reset_control_deassertreset api使用示例基本步骤:
1、调用devm_reset_control_get()获取reset句柄
2、调用reset_control_assert()进行复位操作
3、调用reset_control_deassert()进行解复位操作
static int xx_probe(struct platform_device *pdev){ struct device_node* np = pdev->dev.of_node; ...... /* 1、获取reset句柄 */ host->rstc = devm_reset_control_get(&pdev->dev, np->name); if (is_err(host->rstc)) { dev_err(&pdev->dev, no reset controller specified\\n); return ptr_err(host->rstc); } if (host->rstc) { /* 2、复位 */ ret = reset_control_assert(host->rstc); if (ret) { dev_err(&pdev->dev, unable to reset_control_assert\\n); return ret; } udelay(1); /* 3、解复位 */ ret = reset_control_deassert(host->rstc); if (ret) { dev_err(&pdev->dev, unable to reset_control_deassert\\n); return ret; } } ......}reset驱动实例类似于clock驱动,reset驱动也是编进内核的,在linux启动时,完成reset驱动的加载。
设备树reset:reset-controller{ compatible = xx,xx-reset; reg = ; #reset-cells = ;};上述是一个reset控制器的节点,0xc0000000是寄存器基址,0x1000是映射大小。 #reset-cells代表引用该reset时需要的cells个数。
例如,#reset-cells = ; 则正确引用为:
mmc:mmc@0x12345678{ ...... resets = ;//0代表reset设备id,id是自定义的,但是不能超过reset驱动中指定的设备个数 ......};驱动编写reset驱动编写的基本步骤:
1、实现struct reset_control_ops结构体中的.reset、.assert、.deassert、.status函数
2、分配struct reset_controller_dev结构体,填充ops、owner、nr_resets等成员内容
3、调用reset_controller_register函数注册reset设备
以下是从实际项目中分离出来的reset驱动代码:
#include #include #include #include #include #include // 自定义芯片厂的结构体,保存寄存器基址等信息struct xx_reset{ struct reset_controller_dev rcdev; void __iomem *base; //......};static int xx_reset(struct reset_controller_dev *rcdev, unsigned long id){ //操作寄存器:先复位,延迟一会,然后解复位 return 0;}static int xx_reset_assert(struct reset_controller_dev *rcdev, unsigned long id){ //操作寄存器:复位 return 0;}static int xx_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id){ //操作寄存器:解复位 return 0;}static int xx_reset_status(struct reset_controller_dev *rcdev, unsigned long id){ //操作寄存器:获取复位状态 return 0; }static struct reset_control_ops xx_reset_ops = { .rest = xx_rest, .assert = xx_reset_asser, .deassert = xx_reset_deassert, .status = xx_rest_status,};static int xx_reset_probe(struct platform_device *pdev){ struct xx_reset *xx_reset; struct resource *res; xx_reset = devm_kzalloc(&pdev->dev, sizeof(*xx_reset), gfp_kernel); if (!xx_reset) return -enomem; platform_set_drvdata(pdev, xx_reset); res = platform_get_resource(pdev, ioresource_mem, 0); xx_reset->base = devm_ioremap_resource(&pdev->dev, res);//映射寄存器基址 if (is_err(xx_reset->base)) return ptr_err(xx_reset->base); xx_reset->rcdev.ops = &xx_reset_ops;//reset_ops操作函数集合 xx_reset->rcdev.owner = this_module; xx_reset->rcdev.of_node = pdev->dev.of_node; xx_reset->rcdev.of_reset_n_cells = 1; xx_reset->rcdev.nr_resets = bits_per_long;//reset设备个数 return reset_controller_register(&xx_reset->rcdev);//注册reset controller }static int xx_reset_remove(struct platform_device *pdev){ struct xx_reste *xx_reset = platform_get_drvdata(pdev); reset_controller_unregister(&xx_reset->rcdev); return 0;}static const struct of_device_id ak_reset_of_match[]={ {.compatible = xx,xx-reset}, {},};module_device_table(of, xx_reset_of_match);static struct platform_driver xx_reset_driver = { .probe = xx_reset_probe, .remove = xx_reset_remove, .driver = { .name = xx-reset, .of_match_table = ak_reset_of_match, },};module_platorm_driver(xx_reset_driver);module_license(gpl);module_descpription(xx reset controller driver);module_author(xx microelectronic);module_version(v1.0.00);
一文解析Linux中ARP学习和老化机制
LED电路的三种接线方式介绍
STM32单片机小Tips(1):充分准备与开始编程
基于ISO9141标准的K线通讯方式实现汽车天窗马达ECU通讯系统的应用
酷博短信发送软件
Linux reset子系统及驱动实例
什么是光感传感器?
“十四五”期间中国企业发展大趋势
X态是什么?X态有什么危害?如何避免X态的产生?X态怎么处理?
“幻夜派对”主角来了 华为畅享10今日正式首销
软硬整合,构筑可靠的数字标牌国产主板解决方案
赫兹是如何证实电磁波存在的?
S2C将FPGA设计原型带入云端:Prodigy完整原型设计平台能处理任何规模的工程
看3D电影应该如何呵护双眼
MAX4789, MAX4790, MAX4791, MAX
企业的移动性可以利用人工智能来改变吗
偏压、控制及电源管理IC简化LNB设计
曙光存储连续三年成功入围中移动集采,助力电信运营商的云化转型
红蚁数字人克隆系统:加速推动数字人产业升级新篇章
苹果重返CES AR将构筑前所未有的游戏空间