Linux reset子系统和驱动实例
文章目录
- Linux reset子系统
- reset复位API说明
- devm_reset_control_get
- reset_control_deassert
- reset_control_assert
- reset_control_reset
- reset API使用示例
- reset驱动实例
- 设备树
- 驱动编写
上篇讲了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_get
struct reset_control *devm_reset_control_get(struct device *dev, const char *id)
- 作用:获取相应的reset句柄
- 参数:
- dev:指向申请reset资源的设备句柄
- id:指向要申请的reset资源名(字符串),可以为NULL
- 返回:
- 成功:返回reset句柄
- 失败:返回NULL
reset_control_deassert
int reset_control_deassert(struct reset_control *rstc)
- 作用:对传入的reset资源进行解复位操作
- 参数:
- rstc:指向申请reset资源的设备句柄
- 返回:
- 成功:返回0
- 失败:返回错误码
reset_control_assert
int reset_control_assert(struct reset_control *rstc)
- 作用:对传入的reset资源进行复位操作。
参数和返回值与reset_control_deassert
相同
reset_control_reset
int reset_control_reset(struct reset_control *rstc)
- 作用:对传入的reset资源先进行复位操作,然后等待5us,再进行解复位操作。
- 相当于执行了一遍
reset_control_assert
后,然后delay一会,再调用reset_control_deassert
reset 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 = <0x0 0xc0000000 0x0 0x1000>;
#reset-cells = <1>;
};
上述是一个reset控制器的节点,0xc0000000
是寄存器基址,0x1000
是映射大小。#reset-cells
代表引用该reset时需要的cells个数。
例如,#reset-cells = <1>;
则正确引用为:
mmc:mmc@0x12345678{
......
resets = <&reset 0>;//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 <linux/of.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/reset-controller.h>
#include <linux/io.h>
#include <linux/delay.h>
// 自定义芯片厂的结构体,保存寄存器基址等信息
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");