Linux reset子系统及驱动实例

简介: Linux reset子系统及驱动实例

上篇讲了Linux clock驱动,今天说说Linux的reset驱动。

时钟和复位是两个不同的驱动,但通常都是由负责clock驱动的人,把reset驱动完成。同样,reset驱动也是由芯片厂商去完成的。

Linux reset子系统

reset子系统非常简单,与clock子系统非常类似,但在驱动实现上,reset驱动更简单。

因为clock驱动主要是时钟的实现,涉及到固定时钟、分频、门控等一些时钟的分级关系,需要弄清楚时钟树里每个时钟的关系。

而reset驱动有点相当于clock驱动的门控,它只有复位和解复位两个功能。

类似于clock子系统,reset子系统也分为了consumerprovider,结构体关系如下:

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结构体,填充opsownernr_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");

end

猜你喜欢

Linux clock子系统及驱动实例

Linux内核中常用的C语言技巧

Linux内核死锁检测工具——Lockdep

Linux内核基础篇——常用调试技巧汇总

Linux内核基础篇——动态输出调试

Linux内核基础篇——printk调试

Linux内核基础篇——initcall

RISC-V SiFive U64内核——HPM硬件性能监视器

RISC-V SiFive U64内核——L2 Prefetcher预取器

RISC-V SiFive U54内核——PMP物理内存保护

RISC-V SiFive U54内核——PLIC平台级中断控制器

RISC-V SiFive U54内核——CLINT中断控制器

RISC-V SiFive U54内核——中断和异常详解

实战 | RISC-V Linux入口地址2M预留内存优化

RISC-V Linux启动之页表创建分析

RISC-V Linux汇编启动过程分析

教你在QEMU上运行RISC-V Linux

写给新手的MMU工作原理

相关文章
|
30天前
|
存储 JSON Linux
|
1月前
|
Oracle 关系型数据库 Linux
讲解linux下的Qt如何编译oracle的驱动库libqsqloci.so
通过这一连串的步骤,可以专业且有效地在Linux下为Qt编译Oracle驱动库 `libqsqloci.so`,使得Qt应用能够通过OCI与Oracle数据库进行交互。这些步骤适用于具备一定Linux和Qt经验的开发者,并且能够为需要使用Qt开发数据库应用的专业人士提供指导。
49 1
讲解linux下的Qt如何编译oracle的驱动库libqsqloci.so
|
1月前
|
缓存 网络协议 算法
【Linux系统编程】深入剖析:四大IO模型机制与应用(阻塞、非阻塞、多路复用、信号驱动IO 全解读)
在Linux环境下,主要存在四种IO模型,它们分别是阻塞IO(Blocking IO)、非阻塞IO(Non-blocking IO)、IO多路复用(I/O Multiplexing)和异步IO(Asynchronous IO)。下面我将逐一介绍这些模型的定义:
|
1月前
|
Linux 数据处理
Linux命令reset详解
`reset`命令在Linux中用于修复终端显示异常,它是`tset`的软链接。通过重置显示属性和参数,恢复终端正常状态。特点包括自动检测终端类型、多参数支持及恢复显示设置。常用参数有 `-e`, `-i`, `-k` 用于设置控制字符,`-V` 显示ncurses版本。在终端出现问题时,简单运行`reset`即可重置,定期使用可维护终端整洁。记得确保已安装ncurses库。
|
2月前
|
Linux 程序员 芯片
【Linux驱动】普通字符设备驱动程序框架
【Linux驱动】普通字符设备驱动程序框架
|
1月前
|
Linux 开发者
Linux底层驱动社区饮水机系统详解
在Linux驱动开发中,入门时通常会关注驱动程序的三大核心步骤:入口函数、出口函数和声明许可证。这些步骤构成了驱动程序的基本结构,是驱动与内核交互的基础。下面是对这三个步骤的简要说明:
|
1月前
|
监控 算法 Linux
Linux下工具tc详细讲解及限制IP和端口实例
TC (Traffic Control) 是Linux内核中提供的一个用于控制和管理网络流量的强大工具,它允许用户实现QoS(Quality of Service)策略,包括带宽限制、优先级控制、延迟保证等。TC基于内核的队列 discipline (qdisc) 和流量类别(class) 体系结构,允许对进入或离开网络接口的数据流进行复杂的整形和过滤。
112 0
|
2月前
|
域名解析 网络协议 程序员
程序员必知:【转】adns解析库——域名解析实例(C++、linux)
程序员必知:【转】adns解析库——域名解析实例(C++、linux)
38 0
|
2月前
|
域名解析 网络协议 程序员
程序员必知:【转】adns解析库——域名解析实例(C++、linux)
程序员必知:【转】adns解析库——域名解析实例(C++、linux)
26 0
|
2月前
|
Linux
【Linux驱动学习(1)】USB与input子系统,linux统一设备模型,枚举,USB描述符深入剖析
【Linux驱动学习(1)】USB与input子系统,linux统一设备模型,枚举,USB描述符深入剖析

热门文章

最新文章