0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

电源管理入门-3. provider-reset驱动

yzcdx 来源:OS与AUTOSAR研究 2023-10-16 17:11 次阅读

之前的文章电源管理入门-1关机重启详解介绍了整机SoC的重启也可以说是reset,那么子系统的reset,例如某个驱动(网卡、USB等)或者某个子系统(NPU、ISP等运行在独立的M核或者R核上的AI系统),这些零碎模块的reset就需要用另外一种机制,Linux提供了reset framework框架,我们可以使用这个框架对子系统reset,然后操作硬件CRU寄存器进行硬件的reset操作。

考虑到安全的因素对CRU寄存器的操作可以放在:

  • ATF里面的BL31(通过SMC指令)
  • 或者放到SCP里面(通过Linux-SCMI-》SCP)里面进行。

本小节先介绍下Linux里面的通用reset框架,下篇介绍arm-scmi到SCP进行CRU硬件操作的实现。

1. 简介

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

fabb64f4-6bf8-11ee-939d-92fbcf53809c.png

例如有3个软件I2C/EMMC/IPC都有复位某个硬件模块的需求,那么要写三个复位操作代码。

  • 这些代码可以进行抽象出来一个独立的软件框架-reset framework,
  • 这样软件使用者(consumer:I2C/EMMC/IPC)直接使用硬件模块的名字,就可以对硬件进行复位。
  • 一个模块硬件的复位实现为单独的reset driver(provider),只用实现一次就可以了。

再次说明了,解决复杂问题的普遍方法就是抽象,而Linux内核可以说是玩得一手好抽象,也是操作系统的必备技能。

2. consumer-驱动软件

对于硬件驱动来的需求来说,就是复位某个硬件,在驱动代码里面可以通过硬件的名字进行复位,这个名字对应设置放在了dts文件中,例如:

i2c0: i2c@0xA1006000 {
        compatible = "arch64,a10-i2c";
        reg = <0 0xA1006000 0 0x100>;
        interrupt-parent = <&gic>;
        interrupts = <0 32 4>;
        clock-frequency = <24000000>;
        resets = <&rst 0x50 11>;
        reset-names = "i2c0";
        status = "disabled";
};

&rst:使用rst驱动,0x50:寄存器偏移,11:使用那个bit 进行复位的时候,在驱动软件里面加上

    i2c_dev->i2c_rst =
            devm_reset_control_get(i2c_dev->dev, "i2c0");

static int i2c_reset_assert(struct reset_control *rstc)
{
        int rc = 0;
        rc = reset_control_assert(rstc);
        if (rc < 0) {
                pr_err("%s: failed
", __func__);
                return rc;
        }

        return rc;
}
static int i2c_reset_assert(struct reset_control *rstc)
{
        int rc = 0;
        rc = reset_control_assert(rstc);
        if (rc < 0) {
                pr_err("%s: failed
", __func__);
                return rc;
        }

        return rc;
}

static int i2c_hw_reset(struct i2c_dev *i2c_dev)
{
                i2c_reset_assert(i2c_dev->i2c_rst );
                udelay(1);
                i2c_reset_release(i2c_dev->i2c_rst );

}

i2c_dev->i2c_rst是一个reset_control的结构体

struct reset_control {
    struct reset_controller_dev *rcdev;
    struct list_head list;
    unsigned int id;
    struct kref refcnt;
    bool acquired;
    bool shared;
    bool array;
    atomic_t deassert_count;
    atomic_t triggered_count;
};

上面i2c驱动作为consumer调用了reset framework提供的API函数(include/linux/reset.h),如下:

/* 通过reset_control_get或者devm_reset_control_get获得reset句柄 */ 
struct reset_control *reset_control_get(struct device *dev, const char *id);    
void reset_control_put(struct reset_control *rstc);                             
struct reset_control *devm_reset_control_get(struct device *dev, const char *id);

/* 通过reset_control_reset进行复位,或者通过reset_control_assert使设备处于复位生效状态,通过reset_control_deassert使复位失效 */ 
reset_control_deassert(struct reset_control *rstc)//解复位
reset_control_assert(struct reset_control *rstc)//复位
reset_control_reset(struct reset_control *rstc)//先复位,延迟一会,然后解复位

3. provider-reset驱动

3.1 整体介绍

reset驱动是一个独立驱动,为其他驱动提供硬件复位的服务。首先在dts里面设置.compatible这样驱动就可以加载了,如下定义了rst驱动:

        rst: reset-controller {
                compatible = "arch64,a10-reset";
                #reset-cells = <2>;
                reg = <0x0 0x91000000 0x0 0x1000>;
        };

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

然后就是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设备

首先定义platform_driver:

static const struct of_device_id a10_reset_dt_ids[] = {
        { .compatible = "hobot,a10-reset", },
        { },
};
static struct platform_driver a10_reset_driver = {
        .probe  = a10_reset_probe,
        .driver = {
                .name       = KBUILD_MODNAME,
                .of_match_table = a10_reset_dt_ids,
        },
};

static int __init a10_reset_init(void)
{
    return platform_driver_register(&a10_reset_driver);
}

系统初始化,dts中配置了此reset驱动,就会调用a10_reset_probe

static int a10_reset_probe(struct platform_device *pdev)
{
        struct a10_reset_data *data;
        struct resource *res;
        struct device *dev = &pdev->dev;
        struct device_node *np = dev->of_node;
        u32 modrst_offset;

        /*
         * The binding was mainlined without the required property.
         * Do not continue, when we encounter an old DT.
         */
        if (!of_find_property(pdev->dev.of_node, "#reset-cells", NULL)) {
                dev_err(&pdev->dev, "%s missing #reset-cells property
",
                        pdev->dev.of_node->full_name);
                return -EINVAL;
        }

        data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
        if (!data)
                return -ENOMEM;

        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        data->membase = devm_ioremap_resource(&pdev->dev, res);
        if (IS_ERR(data->membase))
                return PTR_ERR(data->membase);

        spin_lock_init(&data->lock);

        data->rcdev.owner = THIS_MODULE;
        data->rcdev.nr_resets = a10_MAX_NR_RESETS;
        data->rcdev.ops = &a10_reset_ops;
        data->rcdev.of_node = pdev->dev.of_node;
        data->rcdev.of_xlate = a10_reset_of_xlate;
        data->rcdev.of_reset_n_cells = 2;

        return devm_reset_controller_register(dev, &data->rcdev);
}

data->rcdev的定义如下:

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;//复位设备个数
}
  • ops提供reset操作的实现,基本上是reset provider的所有工作量。
  • of_xlate和of_reset_n_cells用于解析consumer device dts node中的“resets = ; ”节点,如果reset controller比较简单(仅仅是线性的索引),可以不实现,使用reset framework提供的简单版本----of_reset_simple_xlate即可。
  • nr_resets,该reset controller所控制的reset信号的个数。

a10_reset_ops定义了reset framework的回调函数,对具体寄存器位进行操作

//reset可控制设备完成一次完整的复位过程。
//assert和deassert分别控制设备reset状态的生效和失效。
static const struct reset_control_ops a10_reset_ops = {
        .assert     = a10_reset_assert,
        .deassert   = a10_reset_deassert,
        .status     = a10_reset_status,
};

static int a10_reset_assert(struct reset_controller_dev *rcdev,
        unsigned long id)
{
        void __iomem    *regaddr;
        uint32_t reg_val, offset;
        unsigned long flags;
        u8 bit;
        struct a10_reset_data *data = to_a10_reset_data(rcdev);

        if (rcdev == NULL || id < 0)
                return -EINVAL;

        spin_lock_irqsave(&data->lock, flags);
        offset = (id & RESET_REG_OFFSET_MASK) >> RESET_REG_OFFSET_SHIFT;
        regaddr = data->membase + offset;

        reg_val = readl(regaddr);
        bit = (id & RESET_REG_BIT_MASK);
        reg_val |= BIT(bit);
        writel(reg_val, regaddr);

        spin_unlock_irqrestore(&data->lock, flags);

        return 0;
}

static int a10_reset_deassert(struct reset_controller_dev *rcdev,
        unsigned long id)
{
        void __iomem    *regaddr;
        uint32_t reg_val, offset;
        unsigned long flags;
        u8 bit;
        struct a10_reset_data *data = to_a10_reset_data(rcdev);

        if (rcdev == NULL || id < 0)
                return -EINVAL;

        spin_lock_irqsave(&data->lock, flags);
        offset = (id & RESET_REG_OFFSET_MASK) >> RESET_REG_OFFSET_SHIFT;
        regaddr = data->membase + offset;

        reg_val = readl(regaddr);
        bit = (id & RESET_REG_BIT_MASK);
        reg_val &= ~(BIT(bit));
        writel(reg_val, regaddr);

        spin_unlock_irqrestore(&data->lock, flags);
        return 0;
}
static int a10_reset_status(struct reset_controller_dev *rcdev,
        unsigned long id)
{
        return 0;
}

3.2 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

后记:

使用markdown写中文发现段落行首空格实在不好搞,然后调研了很多牛人写的中文博客发现行首不用空格的很多,咱们这里为了方便书写,也不要行首空格了。毕竟工具是服务人的,规则都是在变化的。

后续文章先在稀土掘金首发(写的快),然后复制过来,欢迎关注:https://juejin.cn/user/2052111227697336

电源管理,可能很多人不喜欢看,我分几次多篇一块发完。也欢迎大家把喜欢看的技术留言。

电源管理这个专栏其实比较小众,大伙并不是那么爱看,我就先多写几篇存着,到时一块推送,避免公共资源的浪费,节省点大家的时间。有时候我也划开微信看看直播和视频号,发现很多无脑的直播,比如河边钢筋磨石头、在家转大棍子,什么科目三,感觉这些都有人看,这么无脑,我就算写点垃圾文字也比这强的吧,也有可能人看视频就是为了无脑休息下。


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 控制器
    +关注

    关注

    112

    文章

    15245

    浏览量

    171340
  • 寄存器
    +关注

    关注

    30

    文章

    5042

    浏览量

    117802
  • reset
    +关注

    关注

    0

    文章

    33

    浏览量

    12761

原文标题:3. provider-reset驱动

文章出处:【微信号:OS与AUTOSAR研究,微信公众号:OS与AUTOSAR研究】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    Linux reset子系统及驱动实例

    上篇讲了Linux clock驱动,今天说说Linux的reset驱动
    发表于 05-31 16:16 627次阅读
    Linux <b class='flag-5'>reset</b>子系统及<b class='flag-5'>驱动</b>实例

    电源管理入门-Regulator驱动是什么?Regulator的作用是什么?

    Regulator是Linux系统中电源管理的基础设施之一,用于稳压电源管理,是各种驱动子系统中设置 电压的标准接口。
    的头像 发表于 11-16 16:51 3284次阅读
    <b class='flag-5'>电源</b><b class='flag-5'>管理入门</b>-Regulator<b class='flag-5'>驱动</b>是什么?Regulator的作用是什么?

    电源管理入门:Thermal热管理

    管理指的是在电子设备或系统中通过各种方式控制其温度来保证其正常工作或延长寿命的过程。其中包括散热设计、温度监测、温度控制等方面。热管理的重要性越来越凸显,尤其在高性能计算、人工智能等领域的应用中更为重要。
    的头像 发表于 11-29 10:09 1295次阅读
    <b class='flag-5'>电源</b><b class='flag-5'>管理入门</b>:Thermal热<b class='flag-5'>管理</b>

    电源管理入门驱动Runtime PM管理

    Runtime PM管理也就是设备驱动里面的电源管理,即设备驱动结构体里面的struct dev_pm_ops,只控制设备自己的
    的头像 发表于 11-29 10:13 782次阅读
    <b class='flag-5'>电源</b><b class='flag-5'>管理入门</b>:<b class='flag-5'>驱动</b>Runtime PM<b class='flag-5'>管理</b>

    电源管理入门:Power supply子系统

    对于便携设备来说,电源管理更加的重要,因为电池电量有限,容易电量焦虑。除了省电管理外,还需要对电池进行监控管理和充放电管理,这样保护好电池和
    的头像 发表于 11-29 10:15 1592次阅读
    <b class='flag-5'>电源</b><b class='flag-5'>管理入门</b>:Power supply子系统

    电源管理入门:Power Domain管理

    SoC中通常有很多IP,按逻辑可以把几个相关功能的IP划为一个电源域。一个电源域内的IP,通常按相同的方式由同一个硬件模块PMIC供电,电压一样并且电源管理例如休眠唤醒一致。
    的头像 发表于 11-29 10:16 1329次阅读
    <b class='flag-5'>电源</b><b class='flag-5'>管理入门</b>:Power Domain<b class='flag-5'>管理</b>

    电源管理入门-芯片设计中的电源管理介绍

    SCP直接控制SoC的电源和时钟,而AP通过硬件和软件接口协同管理
    的头像 发表于 12-06 09:16 904次阅读
    <b class='flag-5'>电源</b><b class='flag-5'>管理入门</b>-芯片设计中的<b class='flag-5'>电源</b><b class='flag-5'>管理</b>介绍

    电源管理入门:Hypervisor中的电源管理

    很多时候听说Hypervisor,但是对底层软件技术不了解的人感觉挺神秘。本篇文章简单介绍下Hypervisor的基本概念,另外介绍下电源管理在Hypervisor之上多OS间怎么应用。
    的头像 发表于 12-06 09:27 776次阅读
    <b class='flag-5'>电源</b><b class='flag-5'>管理入门</b>:Hypervisor中的<b class='flag-5'>电源</b><b class='flag-5'>管理</b>

    防火墙原理入门

    防火墙原理入门 防火墙能增强机构内部网络的安全性。防火墙系统决定了哪些内部服务可以被外界访问;外界的哪些人可以访问内部的服务以及哪
    发表于 08-01 10:21 950次阅读
    防火墙原<b class='flag-5'>理入门</b>

    数字信号处理入门指南

    数字信号处理入门指南什么是DSP? 数字信号处理器(DSP)采集已被数字化的现实世界的声音、音频、视频、温度、压力或位置等信号,并从数学的角度对其进
    发表于 09-15 08:55 1270次阅读
    数字信号处<b class='flag-5'>理入门</b>指南

    VPN术语-Provider Network

    VPN术语-Provider Network    英文原义:Provider Network 中文释义:服务商网络 注  解:简称P网,由服务商
    发表于 02-24 10:43 794次阅读

    《收音机修理入门》【王光迅编著%2C黑龙江科学技术出版社】pdf

    收音机修理入门
    发表于 01-29 10:38 0次下载

    Linux reset子系统有什么功能

    Linux reset子系统 reset子系统非常简单,与clock子系统非常类似,但在驱动实现上,reset驱动更简单。 因为clock
    的头像 发表于 09-27 14:06 368次阅读
    Linux <b class='flag-5'>reset</b>子系统有什么功能

    Linux内核reset驱动实例

    reset驱动实例 类似于clock驱动reset驱动也是编进内核的,在Linux启动时,完成rese
    的头像 发表于 09-27 14:21 354次阅读

    电源管理入门-5 arm-scmi和mailbox核间通信

    在scmi协议初始化的时候,scmi_reset_register会注册0x16的回调函数,详细分析见2.2.1 SCMI reset协议初始化内容。在drivers/firmware/arm_scmi/reset.c中
    的头像 发表于 10-16 17:09 2345次阅读
    <b class='flag-5'>电源</b><b class='flag-5'>管理入门</b>-5 arm-scmi和mailbox核间通信