文章代码分析基于linux-5.19.13,架构基于aarch64(ARM64)。

1. 前言

复杂IC内部有很多具有独立功能的硬件模块,例如CPU cores、GPU cores、USB控制器、MMC控制器、等等,出于功耗、稳定性等方面的考虑,有些IC在内部为这些硬件模块设计了复位信号(reset signals),软件可通过寄存器(一般1个bit控制1个硬件)控制这些硬件模块的复位状态。

Linux kernel为了方便设备驱动的编写,抽象出一个简单的软件框架----reset framework,为reset的provider提供统一的reset资源管理手段,并为reset的consumer(各个硬件模块)提供便捷、统一的复位控制API。

2. 前言

reset子系统也分为了consumer和provider,结构体关系如下:

3. consumer

对于一个具体的硬件模块,它的要求很简单:复位我的硬件模块,而不必关注具体复位的手段(例如控制哪个寄存器的哪个bit位,等等)。

Linux kernel基于device tree提供了对应的reset framework:

  1. 首先,提供描述系统中reset资源的方法(参考provider的介绍),这样consumer可以基于这种描述,在自己的dts node中引用所需的reset信号。

  2. 然后,consumer设备在自己的dts node中使用“resets”、“reset-names”等关键字声明所需的reset的资源,例如("resets"字段的具体格式由reset provider决定):

device {
resets = <&rst 20>;
reset-names = "reset";
};
This represents a device with a single reset signal named "reset". bus {
resets = <&rst 10> <&rst 11> <&rst 12> <&rst 11>;
reset-names = "i2s1", "i2s2", "dma", "mixer";
};
This represents a bus that controls the reset signal of each of four sub- ordinate devices. Consider for example a bus that fails to operate unless no child device has reset asserted.
  1. 最后,consumer driver在需要的时候,可以调用下面的API复位自己(具体可参考"include\linux\reset.h"):
  • 只有一个reset信号的话,可以使用最简单的device_reset API
static inline int __must_check device_reset(struct device *dev)
  • 如果需要更为复杂的控制(例如有多个reset信号、需要控制处于reset状态的长度的等),可以使用稍微复杂的API
/* 通过reset_control_get或者devm_reset_control_get获得reset句柄 */
struct reset_control *reset_control_get(struct device *dev, const char *id);
struct reset_control *devm_reset_control_get(struct device *dev, const char *id); /* 通过reset_control_put释放reset句柄 */
void reset_control_put(struct reset_control *rstc); /* 通过reset_control_reset进行复位,或者通过reset_control_assert使设备处于复位生效状态,通过reset_control_deassert使复位失效 */
int reset_control_reset(struct reset_control *rstc); /先复位,延迟一会,然后解复位
int reset_control_assert(struct reset_control *rstc); //复位
int reset_control_deassert(struct reset_control *rstc);//解复位

4. provider

kernel为reset provider提供的API位于"include/linux/reset-controller.h"中,很简单,无非就是:创建并填充reset controller设备(struct reset_controller_dev),并调用相应的接口:

  • reset_controller_register //注册reset_controller
  • reset_controller_unregister //注销reset_controller

reset controller的抽象也很简单:

/**
* struct reset_controller_dev - reset controller entity that might
* provide multiple reset controls
* @ops: a pointer to device specific struct reset_control_ops
* @owner: kernel module of the reset controller driver
* @list: internal list of reset controller devices
* @reset_control_head: head of internal list of requested reset controls
* @dev: corresponding driver model device struct
* @of_node: corresponding device tree node as phandle target
* @of_reset_n_cells: number of cells in reset line specifiers
* @of_xlate: translation function to translate from specifier as found in the
* device tree to id as given to the reset control ops, defaults
* to :c:func:`of_reset_simple_xlate`.
* @nr_resets: number of reset controls in this reset controller device
*/
struct reset_controller_dev {
const struct reset_control_ops *ops;//ops提供reset操作的实现,基本上是reset provider的所有工作量。
struct module *owner;
struct list_head list;////全局链表,复位控制器注册后挂载到全局链表
struct list_head reset_control_head;////各个模块复位的链表头
struct device *dev;
struct device_node *of_node;
int of_reset_n_cells;////用于解析consumer device dts node中的“resets = <>; ”节点,指示dts中引用时,需要几个参数
int (*of_xlate)(struct reset_controller_dev *rcdev,
const struct of_phandle_args *reset_spec);//用于解析consumer device dts node中的“resets = <>; ”节点
unsigned int nr_resets;//该reset controller所控制的reset信号的个数
};

struct reset_control_ops也比较单纯,如下:

/**
* struct reset_control_ops - reset controller driver callbacks
*
* @reset: for self-deasserting resets, does all necessary
* things to reset the device
* @assert: manually assert the reset line, if supported
* @deassert: manually deassert the reset line, if supported
* @status: return the status of the reset line, if supported
*/
struct reset_control_ops {
int (*reset)(struct reset_controller_dev *rcdev, unsigned long id); //控制设备完成一次完整的复位过程
int (*assert)(struct reset_controller_dev *rcdev, unsigned long id); //控制设备reset状态的生效
int (*deassert)(struct reset_controller_dev *rcdev, unsigned long id);//控制设备reset状态的失效。
int (*status)(struct reset_controller_dev *rcdev, unsigned long id); //复位状态查询
};

5. reset驱动的设备树描述总结

5.1 对于provider

reset:reset-controller{
compatible = "xx,xx-reset";
reg = <0x0 0x30390000 0x0 0x10000>;
#reset-cells = <1>;
};

上述是一个reset控制器的节点,0x30390000 是寄存器基址,0x1000是映射大小。"#reset-cells"代表引用该reset时需要的cells个数。

5.2 对于consumer

例如,#reset-cells = <1>; 则正确引用为:

mmc:mmc@0x12345678{
......
resets = <&reset 0>;//0代表reset设备id,id是自定义的,但是不能超过reset驱动中指定的设备个数
......
};

6. 开源reset驱动实例

6.1 实例1(比较容易理解)

设备树: arch/arm/boot/dts/imx7d.dtsi

	pcie: pcie@0x33800000 {
compatible = "fsl,imx7d-pcie", "snps,dw-pcie"; .... resets = <&src IMX7_RESET_PCIEPHY>,
<&src IMX7_RESET_PCIE_CTRL_APPS_EN>;
reset-names = "pciephy", "apps";
status = "disabled";
};

驱动代码: drivers/reset/reset-imx7.c

...

struct imx7_src {
struct reset_controller_dev rcdev;
struct regmap *regmap;
}; enum imx7_src_registers {
SRC_A7RCR0 = 0x0004,
SRC_M4RCR = 0x000c,
SRC_ERCR = 0x0014,
SRC_HSICPHY_RCR = 0x001c,
SRC_USBOPHY1_RCR = 0x0020,
SRC_USBOPHY2_RCR = 0x0024,
SRC_MIPIPHY_RCR = 0x0028,
SRC_PCIEPHY_RCR = 0x002c,
SRC_DDRC_RCR = 0x1000,
}; struct imx7_src_signal {
unsigned int offset, bit;
}; static const struct imx7_src_signal imx7_src_signals[IMX7_RESET_NUM] = {
[IMX7_RESET_A7_CORE_POR_RESET0] = { SRC_A7RCR0, BIT(0) },
[IMX7_RESET_A7_CORE_POR_RESET1] = { SRC_A7RCR0, BIT(1) },
[IMX7_RESET_A7_CORE_RESET0] = { SRC_A7RCR0, BIT(4) },
[IMX7_RESET_A7_CORE_RESET1] = { SRC_A7RCR0, BIT(5) },
[IMX7_RESET_A7_DBG_RESET0] = { SRC_A7RCR0, BIT(8) },
[IMX7_RESET_A7_DBG_RESET1] = { SRC_A7RCR0, BIT(9) },
...
}; static struct imx7_src *to_imx7_src(struct reset_controller_dev *rcdev)
{
return container_of(rcdev, struct imx7_src, rcdev);
} static int imx7_reset_set(struct reset_controller_dev *rcdev,
unsigned long id, bool assert)
{
struct imx7_src *imx7src = to_imx7_src(rcdev);
const struct imx7_src_signal *signal = &imx7_src_signals[id];
unsigned int value = assert ? signal->bit : 0; switch (id) {
case IMX7_RESET_PCIEPHY:
/*
* wait for more than 10us to release phy g_rst and
* btnrst
*/
if (!assert)
udelay(10);
break; case IMX7_RESET_PCIE_CTRL_APPS_EN:
value = (assert) ? 0 : signal->bit;
break;
} return regmap_update_bits(imx7src->regmap,
signal->offset, signal->bit, value);
} static int imx7_reset_assert(struct reset_controller_dev *rcdev,
unsigned long id)
{
return imx7_reset_set(rcdev, id, true);
} static int imx7_reset_deassert(struct reset_controller_dev *rcdev,
unsigned long id)
{
return imx7_reset_set(rcdev, id, false);
} static const struct reset_control_ops imx7_reset_ops = {
.assert = imx7_reset_assert,
.deassert = imx7_reset_deassert,
}; static int imx7_reset_probe(struct platform_device *pdev)
{
struct imx7_src *imx7src;
struct device *dev = &pdev->dev;
struct regmap_config config = { .name = "src" }; imx7src = devm_kzalloc(dev, sizeof(*imx7src), GFP_KERNEL);
if (!imx7src)
return -ENOMEM; imx7src->regmap = syscon_node_to_regmap(dev->of_node);
if (IS_ERR(imx7src->regmap)) {
dev_err(dev, "Unable to get imx7-src regmap");
return PTR_ERR(imx7src->regmap);
}
regmap_attach_dev(dev, imx7src->regmap, &config); imx7src->rcdev.owner = THIS_MODULE;
imx7src->rcdev.nr_resets = IMX7_RESET_NUM;
imx7src->rcdev.ops = &imx7_reset_ops;
imx7src->rcdev.of_node = dev->of_node; return devm_reset_controller_register(dev, &imx7src->rcdev);
} static const struct of_device_id imx7_reset_dt_ids[] = {
{ .compatible = "fsl,imx7d-src", },
{ /* sentinel */ },
}; static struct platform_driver imx7_reset_driver = {
.probe = imx7_reset_probe,
.driver = {
.name = KBUILD_MODNAME,
.of_match_table = imx7_reset_dt_ids,
},
};
builtin_platform_driver(imx7_reset_driver);

6.2 实例2(在gpio子系统中嵌套reset子系统)

设备树: arc/arm64/boot/dts/myzr/myimx8mm.dts

&pcie0{
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c4_pcieclk>, <&pinctrl_gpio1_pciendis>, <&pinctrl_sd2_pciewake>, <&pinctrl_sai2_pcienrst>;
disable-gpio = <&gpio1 5 GPIO_ACTIVE_LOW>;
reset-gpio = <&gpio4 21 GPIO_ACTIVE_LOW>;
ext_osc = <1>;
status = "okay";
};

驱动代码: drivers/reset/gpio-reset.c

...

struct gpio_reset_data {
struct reset_controller_dev rcdev;
unsigned int gpio;
bool active_low;
s32 delay_us;
s32 post_delay_ms;
}; static void gpio_reset_set(struct reset_controller_dev *rcdev, int asserted)
{
struct gpio_reset_data *drvdata = container_of(rcdev,
struct gpio_reset_data, rcdev);
int value = asserted; if (drvdata->active_low)
value = !value; gpio_set_value_cansleep(drvdata->gpio, value);
} static int gpio_reset(struct reset_controller_dev *rcdev, unsigned long id)
{
struct gpio_reset_data *drvdata = container_of(rcdev,
struct gpio_reset_data, rcdev); if (drvdata->delay_us < 0)
return -ENOSYS; gpio_reset_set(rcdev, 1);
udelay(drvdata->delay_us);
gpio_reset_set(rcdev, 0); if (drvdata->post_delay_ms < 0)
return 0; msleep(drvdata->post_delay_ms);
return 0;
} static int gpio_reset_assert(struct reset_controller_dev *rcdev,
unsigned long id)
{
gpio_reset_set(rcdev, 1); return 0;
} static int gpio_reset_deassert(struct reset_controller_dev *rcdev,
unsigned long id)
{
gpio_reset_set(rcdev, 0); return 0;
} static struct reset_control_ops gpio_reset_ops = {
.reset = gpio_reset,
.assert = gpio_reset_assert,
.deassert = gpio_reset_deassert,
}; static int of_gpio_reset_xlate(struct reset_controller_dev *rcdev,
const struct of_phandle_args *reset_spec)
{
if (WARN_ON(reset_spec->args_count != 0))
return -EINVAL; return 0;
} static int gpio_reset_probe(struct platform_device *pdev)
{
... drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL); ... drvdata->rcdev.of_node = np;
drvdata->rcdev.owner = THIS_MODULE;
drvdata->rcdev.nr_resets = 1; ////该reset controller所控制的reset信号的个数
drvdata->rcdev.ops = &gpio_reset_ops; //ops提供reset操作的实现。
drvdata->rcdev.of_xlate = of_gpio_reset_xlate;
reset_controller_register(&drvdata->rcdev); //注册reset controller return 0;
} static int gpio_reset_remove(struct platform_device *pdev)
{
struct gpio_reset_data *drvdata = platform_get_drvdata(pdev); reset_controller_unregister(&drvdata->rcdev); return 0;
} static struct of_device_id gpio_reset_dt_ids[] = {
{ .compatible = "gpio-reset" },
{ }
}; #ifdef CONFIG_PM_SLEEP
static int gpio_reset_suspend(struct device *dev)
{
pinctrl_pm_select_sleep_state(dev); return 0;
}
static int gpio_reset_resume(struct device *dev)
{
pinctrl_pm_select_default_state(dev); return 0;
}
#endif static const struct dev_pm_ops gpio_reset_pm_ops = {
SET_LATE_SYSTEM_SLEEP_PM_OPS(gpio_reset_suspend, gpio_reset_resume)
}; static struct platform_driver gpio_reset_driver = {
.probe = gpio_reset_probe,
.remove = gpio_reset_remove,
.driver = {
.name = "gpio-reset",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(gpio_reset_dt_ids),
.pm = &gpio_reset_pm_ops,
},
}; static int __init gpio_reset_init(void)
{
return platform_driver_register(&gpio_reset_driver);
}
arch_initcall(gpio_reset_init); static void __exit gpio_reset_exit(void)
{
platform_driver_unregister(&gpio_reset_driver);
}
...

7. reset驱动的实质

操作soc对应的reset寄存器,以实现内核IP的复位,或者操作gpio管脚的电平,间接复位接到该pin脚的从设备。

参考

[1] Documentation/devicetree/bindings/reset/reset.txt

[2] Linux reset framework

[2] Linux reset子系统及驱动实例

Linux reset子系统的更多相关文章

  1. Linux时间子系统之(五):POSIX Clock

    专题文档汇总目录 Notes: 本章主要介绍了若干种类的静态时钟,这些时钟都可以通过k_clock表示,注册到posix_clocks中.这些都是静态时钟,可以分为三大类:各种REALTIME时钟.带 ...

  2. Linux时间子系统之(十四):tick broadcast framework

    专题文档汇总目录 Notes:BroadcastTick作为cpuidle的waker,硬件基础.BroadcastTick嵌入在当前系统Tick框架中.BroadcastTick设备初始化:周期性T ...

  3. Linux时间子系统之(十五):clocksource

    专题文档汇总目录 Notes:clocksource基本概念,struct clocksource详解:注册和注销clocksource:内核如何选取clocksource:clocksource相关 ...

  4. Linux时间子系统之(十七):ARM generic timer驱动代码分析

    专题文档汇总目录 Notes:ARM平台Clock/Timer架构:System counter.Timer以及两者之间关系:Per cpu timer通过CP15访问,System counter通 ...

  5. Linux时间子系统(十七) ARM generic timer驱动代码分析

    一.前言 关注ARM平台上timer driver(clocksource chip driver和clockevent chip driver)的驱动工程师应该会注意到timer硬件的演化过程.在单 ...

  6. Linux时间子系统(十五) clocksource

    一.前言 和洋葱一样,软件也是有层次的,内核往往需要对形形色色的某类型的驱动进行抽象,屏蔽掉其具体的特质,获取该类驱动共同的逻辑,而又根据这些逻辑撰写该类驱动的抽象层.嵌入式系统总是会提供timer的 ...

  7. linux输入子系统(input subsystem)之evdev.c事件处理过程

    1.代码 input_subsys.drv.c 在linux输入子系统(input subsystem)之按键输入和LED控制的基础上有小改动,input_subsys_test.c不变. input ...

  8. Linux 网络子系统

    今天记录一下Linux网络子系统相关的东西. 因为感觉对这一块还是有一个很大的空白,这件事情太可怕了. 摘抄多份博客进行总结一下Linux网络子系统的相关东西. 一. Linux网络子系统体系结构 L ...

  9. Linux时间子系统之六:高精度定时器(HRTIMER)的原理和实现

    转自:http://blog.csdn.net/droidphone/article/details/8074892 上一篇文章,我介绍了传统的低分辨率定时器的实现原理.而随着内核的不断演进,大牛们已 ...

  10. Linux输入子系统(转)

    Linux输入子系统(Input Subsystem) 1.1.input子系统概述 输入设备(如按键,键盘,触摸屏,鼠标等)是典型的字符设备,其一般的工作机制是低层在按键,触摸等动作发生时产生一个中 ...

随机推荐

  1. 非关系型数据库---Redis安装与基本使用

    一.数据库类型 关系数据库管理系统(RDBMS) 非关系数据库管理系统(NoSQL) 按照预先设置的组织机构,将数据存储在物理介质上(即:硬盘上) 数据之间可以做无关联操作 (例如: 多表查询,嵌套查 ...

  2. 家用wife密码设置

    1.在浏览器上面输入ip地址:http://192.168.1.1/或http://192.168.0.1/出现路由器登陆窗口输入用户名跟密码.用户名默认一般为:admin,密码为空或为:admin ...

  3. Node.js入门学习笔记

    NodeJs是js的运行时,意味着可以在浏览器外运行js.可以使用nodejs来构建服务器端应用.CLI应用.Web API,甚至用electron构建桌面端应用. 使用nvm来管理node版本. 在 ...

  4. 利用Velero对K8S备份还原与集群迁移实战

    一.简介 Velero 是一款云原生时代的灾难恢复和迁移工具,采用 Go 语言编写,并在 github 上进行了开源,利用 velero 用户可以安全的备份.恢复和迁移 Kubernetes 集群资源 ...

  5. Linux redhat7.2 制作u盘问题总结

    Linux  redhat7.2  制作u盘问题总结 其实呢,觉得本来没必要写一篇关于装系统的文章,毕竟我觉得大多数搞it的人都会,比如win10.ubuntu做个启动盘啥的应该都会,但是说实在的今天 ...

  6. chrome Dev Tools 性能分析 performance

    chrome 的performance用来分析性能优化性能非常好用,下面以一个页面来举例 性能分析 性能分析最好使用隐私无痕模式,以保证干净的环境下,避免chrome插件对性能分析结果的影响 Perf ...

  7. [Pytorch框架] 5.3 Fashion MNIST进行分类

    文章目录 5.3 Fashion MNIST进行分类 Fashion MNIST 介绍 数据集介绍 分类 格式 数据提交 数据加载 创建网络 损失函数 优化器 开始训练 训练后操作 可视化损失函数 保 ...

  8. linux安装tomcat,mysql

    环境:centos7.6 ssh连接工具:tabby 安装tomcat 创建目录 mkdir /opt/tomcat 获取tomcat: 1.自己百度下载 2.我这里提供百度网盘 链接:https:/ ...

  9. 【Azure 应用服务】Azure JS Function 异步方法中日志无法输出问题引发的(await\async)关键字问题

    问题描述 开发 Azure JS Function(NodeJS),使用 mssql 组件操作数据库.当SQL语句执行完成后,在Callback函数中执行日志输出 context.log(" ...

  10. ATL创建的ActiveX实现JS回调

    最近公司的产品需要使用ActiveX文件上传.讨论了基本所有的技术,最后还是决定C++搞个ActiveX...但上传的回调费了半天劲,才搞定.代码如下 STDMETHODIMP CFileSelect ...