RM新时代网站-首页

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

電源管理入門-3. provider-reset驅(qū)動

yzcdx ? 來源:OS與AUTOSAR研究 ? 2023-10-16 17:11 ? 次閱讀

之前的文章電源管理入門-1關(guān)機重啟詳解介紹了整機SoC的重啟也可以說是reset,那么子系統(tǒng)的reset,例如某個驅(qū)動(網(wǎng)卡、USB等)或者某個子系統(tǒng)(NPU、ISP等運行在獨立的M核或者R核上的AI系統(tǒng)),這些零碎模塊的reset就需要用另外一種機制,Linux提供了reset framework框架,我們可以使用這個框架對子系統(tǒng)reset,然后操作硬件CRU寄存器進行硬件的reset操作。

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

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

本小節(jié)先介紹下Linux里面的通用reset框架,下篇介紹arm-scmi到SCP進行CRU硬件操作的實現(xiàn)。

1. 簡介

復(fù)雜SoC內(nèi)部有很多具有獨立功能的硬件模塊,例如CPU cores、GPU cores、USB控制器、MMC控制器、等等,出于功耗、穩(wěn)定性等方面的考慮,有些SoC在內(nèi)部為這些硬件模塊設(shè)計了復(fù)位信號(reset signals),軟件可通過寄存器(一般1個bit控制1個硬件)控制這些硬件模塊的復(fù)位狀態(tài)。

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

例如有3個軟件I2C/EMMC/IPC都有復(fù)位某個硬件模塊的需求,那么要寫三個復(fù)位操作代碼。

  • 這些代碼可以進行抽象出來一個獨立的軟件框架-reset framework,
  • 這樣軟件使用者(consumer:I2C/EMMC/IPC)直接使用硬件模塊的名字,就可以對硬件進行復(fù)位。
  • 一個模塊硬件的復(fù)位實現(xiàn)為單獨的reset driver(provider),只用實現(xiàn)一次就可以了。

再次說明了,解決復(fù)雜問題的普遍方法就是抽象,而Linux內(nèi)核可以說是玩得一手好抽象,也是操作系統(tǒng)的必備技能。

2. consumer-驅(qū)動軟件

對于硬件驅(qū)動來的需求來說,就是復(fù)位某個硬件,在驅(qū)動代碼里面可以通過硬件的名字進行復(fù)位,這個名字對應(yīng)設(shè)置放在了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驅(qū)動,0x50:寄存器偏移,11:使用那個bit 進行復(fù)位的時候,在驅(qū)動軟件里面加上

    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的結(jié)構(gòu)體

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驅(qū)動作為consumer調(diào)用了reset framework提供的API函數(shù)(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進行復(fù)位,或者通過reset_control_assert使設(shè)備處于復(fù)位生效狀態(tài),通過reset_control_deassert使復(fù)位失效 */ 
reset_control_deassert(struct reset_control *rstc)//解復(fù)位
reset_control_assert(struct reset_control *rstc)//復(fù)位
reset_control_reset(struct reset_control *rstc)//先復(fù)位,延遲一會,然后解復(fù)位

3. provider-reset驅(qū)動

3.1 整體介紹

reset驅(qū)動是一個獨立驅(qū)動,為其他驅(qū)動提供硬件復(fù)位的服務(wù)。首先在dts里面設(shè)置.compatible這樣驅(qū)動就可以加載了,如下定義了rst驅(qū)動:

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

上述是一個reset控制器的節(jié)點,0x91000000是寄存器基址,0x1000是映射大小。#reset-cells代表引用該reset時需要的cells個數(shù)。

然后就是reset驅(qū)動的實現(xiàn),reset驅(qū)動編寫的基本步驟:

  1. 實現(xiàn)struct reset_control_ops結(jié)構(gòu)體中的.reset、.assert、.deassert、.status函數(shù)
  2. 分配struct reset_controller_dev結(jié)構(gòu)體,填充ops、owner、nr_resets等成員內(nèi)容
  3. 調(diào)用reset_controller_register函數(shù)注冊reset設(shè)備

首先定義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);
}

系統(tǒng)初始化,dts中配置了此reset驅(qū)動,就會調(diào)用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;//復(fù)位控制操作函數(shù)
    struct list_head list;//全局鏈表,復(fù)位控制器注冊后掛載到全局鏈表
    struct list_head reset_control_head;//各個模塊復(fù)位的鏈表頭
    struct device *dev;int of_reset_n_cells;//dts中引用時,需要幾個參數(shù)
        
    //通過dts引用的參數(shù),解析復(fù)位控制器中相應(yīng)的參數(shù)
    int (*of_xlate)(struct reset_controller_dev *rcdev, const struct of_phandle_args *reset_spec);unsigned int nr_resets;//復(fù)位設(shè)備個數(shù)
}
  • ops提供reset操作的實現(xiàn),基本上是reset provider的所有工作量。
  • of_xlate和of_reset_n_cells用于解析consumer device dts node中的“resets = ; ”節(jié)點,如果reset controller比較簡單(僅僅是線性的索引),可以不實現(xiàn),使用reset framework提供的簡單版本----of_reset_simple_xlate即可。
  • nr_resets,該reset controller所控制的reset信號的個數(shù)。

a10_reset_ops定義了reset framework的回調(diào)函數(shù),對具體寄存器位進行操作

//reset可控制設(shè)備完成一次完整的復(fù)位過程。
//assert和deassert分別控制設(shè)備reset狀態(tài)的生效和失效。
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復(fù)位API說明

devm_reset_control_get

struct reset_control *devm_reset_control_get(struct device *dev, const char *id)
?作用:獲取相應(yīng)的reset句柄
?參數(shù):
? dev:指向申請reset資源的設(shè)備句柄
? id:指向要申請的reset資源名(字符串),可以為NULL
?返回:
? 成功:返回reset句柄
? 失敗:返回NULL

reset_control_deassert

int reset_control_deassert(struct reset_control *rstc)
?作用:對傳入的reset資源進行解復(fù)位操作
?參數(shù):
? rstc:指向申請reset資源的設(shè)備句柄
?返回:
? 成功:返回0
? 失?。悍祷劐e誤碼

reset_control_assert

int reset_control_assert(struct reset_control *rstc)
?作用:對傳入的reset資源進行復(fù)位操作。
參數(shù)和返回值與reset_control_deassert相同

reset_control_reset

int reset_control_reset(struct reset_control *rstc)
?作用:對傳入的reset資源先進行復(fù)位操作,然后等待5us,再進行解復(fù)位操作。
?相當于執(zhí)行了一遍reset_control_assert后,然后delay一會,再調(diào)用reset_control_deassert

后記:

使用markdown寫中文發(fā)現(xiàn)段落行首空格實在不好搞,然后調(diào)研了很多牛人寫的中文博客發(fā)現(xiàn)行首不用空格的很多,咱們這里為了方便書寫,也不要行首空格了。畢竟工具是服務(wù)人的,規(guī)則都是在變化的。

后續(xù)文章先在稀土掘金首發(fā)(寫的快),然后復(fù)制過來,歡迎關(guān)注:https://juejin.cn/user/2052111227697336

電源管理,可能很多人不喜歡看,我分幾次多篇一塊發(fā)完。也歡迎大家把喜歡看的技術(shù)留言。

電源管理這個專欄其實比較小眾,大伙并不是那么愛看,我就先多寫幾篇存著,到時一塊推送,避免公共資源的浪費,節(jié)省點大家的時間。有時候我也劃開微信看看直播和視頻號,發(fā)現(xiàn)很多無腦的直播,比如河邊鋼筋磨石頭、在家轉(zhuǎn)大棍子,什么科目三,感覺這些都有人看,這么無腦,我就算寫點垃圾文字也比這強的吧,也有可能人看視頻就是為了無腦休息下。


聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 控制器
    +關(guān)注

    關(guān)注

    112

    文章

    16332

    瀏覽量

    177803
  • 寄存器
    +關(guān)注

    關(guān)注

    31

    文章

    5336

    瀏覽量

    120228
  • reset
    +關(guān)注

    關(guān)注

    0

    文章

    34

    瀏覽量

    12877

原文標題:3. provider-reset驅(qū)動

文章出處:【微信號:OS與AUTOSAR研究,微信公眾號:OS與AUTOSAR研究】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    Linux reset子系統(tǒng)及驅(qū)動實例

    上篇講了Linux clock驅(qū)動,今天說說Linux的reset驅(qū)動。
    發(fā)表于 05-31 16:16 ?1127次閱讀
    Linux <b class='flag-5'>reset</b>子系統(tǒng)及<b class='flag-5'>驅(qū)動</b>實例

    電源管理入門-關(guān)機重啟基礎(chǔ)知識詳解

    當我們接觸電源管理的時候,最簡單的流程就是關(guān)機重啟,但是仔細分析其涉及的所有源代碼就會發(fā)現(xiàn),關(guān)機重啟雖然簡單
    發(fā)表于 09-19 11:41 ?2605次閱讀
    <b class='flag-5'>電源</b><b class='flag-5'>管理入門</b>-關(guān)機重啟基礎(chǔ)知識詳解

    電源管理入門-Regulator驅(qū)動是什么?Regulator的作用是什么?

    Regulator是Linux系統(tǒng)中電源管理的基礎(chǔ)設(shè)施之一,用于穩(wěn)壓電源管理,是各種驅(qū)動子系統(tǒng)中設(shè)置 電壓的標準接口。
    的頭像 發(fā)表于 11-16 16:51 ?9966次閱讀
    <b class='flag-5'>電源</b><b class='flag-5'>管理入門</b>-Regulator<b class='flag-5'>驅(qū)動</b>是什么?Regulator的作用是什么?

    電源管理入門:Thermal熱管理

    管理指的是在電子設(shè)備或系統(tǒng)中通過各種方式控制其溫度來保證其正常工作或延長壽命的過程。其中包括散熱設(shè)計、溫度監(jiān)測、溫度控制等方面。熱管理的重要性越來越凸顯,尤其在高性能計算、人工智能等領(lǐng)域的應(yīng)用中更為重要。
    的頭像 發(fā)表于 11-29 10:09 ?4840次閱讀
    <b class='flag-5'>電源</b><b class='flag-5'>管理入門</b>:Thermal熱<b class='flag-5'>管理</b>

    電源管理入門驅(qū)動Runtime PM管理

    Runtime PM管理也就是設(shè)備驅(qū)動里面的電源管理,即設(shè)備驅(qū)動結(jié)構(gòu)體里面的struct dev_pm_ops,只控制設(shè)備自己的
    的頭像 發(fā)表于 11-29 10:13 ?3024次閱讀
    <b class='flag-5'>電源</b><b class='flag-5'>管理入門</b>:<b class='flag-5'>驅(qū)動</b>Runtime PM<b class='flag-5'>管理</b>

    電源管理入門:Power supply子系統(tǒng)

    對于便攜設(shè)備來說,電源管理更加的重要,因為電池電量有限,容易電量焦慮。除了省電管理外,還需要對電池進行監(jiān)控管理和充放電管理,這樣保護好電池和
    的頭像 發(fā)表于 11-29 10:15 ?4165次閱讀
    <b class='flag-5'>電源</b><b class='flag-5'>管理入門</b>:Power supply子系統(tǒng)

    電源管理入門:Power Domain管理

    SoC中通常有很多IP,按邏輯可以把幾個相關(guān)功能的IP劃為一個電源域。一個電源域內(nèi)的IP,通常按相同的方式由同一個硬件模塊PMIC供電,電壓一樣并且電源管理例如休眠喚醒一致。
    的頭像 發(fā)表于 11-29 10:16 ?3385次閱讀
    <b class='flag-5'>電源</b><b class='flag-5'>管理入門</b>:Power Domain<b class='flag-5'>管理</b>

    電源管理入門-芯片設(shè)計中的電源管理介紹

    SCP直接控制SoC的電源和時鐘,而AP通過硬件和軟件接口協(xié)同管理。
    的頭像 發(fā)表于 12-06 09:16 ?3260次閱讀
    <b class='flag-5'>電源</b><b class='flag-5'>管理入門</b>-芯片設(shè)計中的<b class='flag-5'>電源</b><b class='flag-5'>管理</b>介紹

    電源管理入門:Hypervisor中的電源管理

    很多時候聽說Hypervisor,但是對底層軟件技術(shù)不了解的人感覺挺神秘。本篇文章簡單介紹下Hypervisor的基本概念,另外介紹下電源管理在Hypervisor之上多OS間怎么應(yīng)用。
    的頭像 發(fā)表于 12-06 09:27 ?1444次閱讀
    <b class='flag-5'>電源</b><b class='flag-5'>管理入門</b>:Hypervisor中的<b class='flag-5'>電源</b><b class='flag-5'>管理</b>

    防火墻原理入門

    防火墻原理入門 防火墻能增強機構(gòu)內(nèi)部網(wǎng)絡(luò)的安全性。防火墻系統(tǒng)決定了哪些內(nèi)部服務(wù)可以被外界訪問;外界的哪些人可以訪問內(nèi)部的服務(wù)以及哪
    發(fā)表于 08-01 10:21 ?1020次閱讀
    防火墻原<b class='flag-5'>理入門</b>

    數(shù)字信號處理入門指南

    數(shù)字信號處理入門指南什么是DSP? 數(shù)字信號處理器(DSP)采集已被數(shù)字化的現(xiàn)實世界的聲音、音頻、視頻、溫度、壓力或位置等信號,并從數(shù)學的角度對其進
    發(fā)表于 09-15 08:55 ?1345次閱讀
    數(shù)字信號處<b class='flag-5'>理入門</b>指南

    Linux reset子系統(tǒng)有什么功能

    Linux reset子系統(tǒng) reset子系統(tǒng)非常簡單,與clock子系統(tǒng)非常類似,但在驅(qū)動實現(xiàn)上,reset驅(qū)動更簡單。 因為clock
    的頭像 發(fā)表于 09-27 14:06 ?763次閱讀
    Linux <b class='flag-5'>reset</b>子系統(tǒng)有什么功能

    reset API使用示例步驟

    時鐘和復(fù)位是兩個不同的驅(qū)動,但通常都是由負責clock驅(qū)動的人,把reset驅(qū)動完成。同樣,reset驅(qū)
    的頭像 發(fā)表于 09-27 14:17 ?1098次閱讀

    Linux內(nèi)核reset驅(qū)動實例

    reset驅(qū)動實例 類似于clock驅(qū)動,reset驅(qū)動也是編進內(nèi)核的,在Linux啟動時,完成rese
    的頭像 發(fā)表于 09-27 14:21 ?766次閱讀

    電源管理入門-5 arm-scmi和mailbox核間通信

    在scmi協(xié)議初始化的時候,scmi_reset_register會注冊0x16的回調(diào)函數(shù),詳細分析見2.2.1 SCMI reset協(xié)議初始化內(nèi)容。在drivers/firmware/arm_scmi/reset.c中
    的頭像 發(fā)表于 10-16 17:09 ?5321次閱讀
    <b class='flag-5'>電源</b><b class='flag-5'>管理入門</b>-5 arm-scmi和mailbox核間通信
    RM新时代网站-首页