电源管理 —— ARM SCP-firmware 框架(1)

电源管理 ------ ARM SCP-firmware 框架(1)

前言

探究下 SCP-firmware 的组织结构以及作为 product 的供应者需要写哪些代码适配框架

SCP 的数据结构

c 复制代码
typedef union __fwk_id fwk_id_t;
cpp 复制代码
// framework/include/internal/fwk_id.h
union __fwk_id {
    uint32_t value; /* Integer value */

    struct {
        uint32_t type : 4; /* Identifier type */
        uint32_t module_idx : 8; /* Module index */
        uint32_t reserved : 20; /* Reserved */
    } common; /* Common fields */

    struct {
        uint32_t type : 4; /* Identifier type */
        uint32_t module_idx : 8; /* Module index */
        uint32_t element_idx : 12; /* Element index */
        uint32_t reserved : 8; /* Reserved */
    } element; /* Element variant */

    struct {
        uint32_t type : 4; /* Identifier type */
        uint32_t module_idx : 8; /* Module index */
        uint32_t element_idx : 12; /* Element index */
        uint32_t sub_element_idx : 8; /* Sub-element index */
    } sub_element; /* Sub-element variant */

    struct {
        uint32_t type : 4; /* Identifier type */
        uint32_t module_idx : 8; /* Module index */
        uint32_t api_idx : 4; /* API index */
        uint32_t reserved : 16; /* Reserved */
    } api; /* API variant */

    struct {
        uint32_t type : 4; /* Identifier type */
        uint32_t module_idx : 8; /* Module index */
        uint32_t event_idx : 6; /* Event index */
        uint32_t reserved : 14; /* Reserved */
    } event; /* Event variant */

    struct {
        uint32_t type : 4; /* Identifier type */
        uint32_t module_idx : 8; /* Module index */
        uint32_t notification_idx : 6; /* Notification index */
        uint32_t reserved : 14; /* Reserved */
    } notification; /* Notification variant */
};

Union __fwk_id 即 fwk_id_t ,用的最多的变量,所有带 id 后缀的变量都是这个 union 类型,用 module_idx filed 和 type 统一了所有 id 变量的访问方式

SCP 的 .h .c

Module 是 SCP 组织文件的单元,module 下面的元素是一组 element

不一定正确:element 是支持 Module 运行的数据,是静态的,module 中的函数是方法,即 module 类似 class

module 的 element 静态数据和自身方法之间,用以下四个文件描述

config.h

  1. 描述 element index
cpp 复制代码
// product/rcar/scp_ramfw/config_rcar_pmic.h
#ifndef CONFIG_RCAR_PMIC_H
#define CONFIG_RCAR_PMIC_H

/* Element indexes for rcar PMIC */
enum mod_rcar_pmic_element_idx {
    MOD_RCAR_PMIC_ELEMENT_IDX_LITTLE,
    MOD_RCAR_PMIC_ELEMENT_IDX_BIG,
    MOD_RCAR_PMIC_ELEMENT_IDX_DDR_BKUP,
    MOD_RCAR_PMIC_ELEMENT_IDX_GPU,
    MOD_RCAR_PMIC_ELEMENT_IDX_COUNT
};

#endif /* CONFIG_RCAR_PMIC_H */

config.c

  1. 根据 config.h 中的 element index 创建 element,其 struct 形式来自 module.h 描述的 element 结构
  2. Module 相关,driver_id 的 module_index 都是全局的,driver_id 的第二个属性 element index 来自包含的头文件的 config.h,driver_api_id 的第二个属性 api index 来自包含的头文件的 module.h
cpp 复制代码
// product/rcar/scp_ramfw/config_rcar_pmic.c
static const struct fwk_element element_table[] = {
    [MOD_RCAR_PMIC_ELEMENT_IDX_LITTLE] = {
        .name = "CPU_GROUP_LITTLE",
        .data =
            &(const struct mod_rcar_pmic_device_config){
                .driver_id = FWK_ID_ELEMENT_INIT(
                    FWK_MODULE_IDX_RCAR_MOCK_PMIC_BD9571,
                    MOD_RCAR_MOCK_PMIC_ELEMENT_IDX_LITTLE),
                .driver_api_id = FWK_ID_API_INIT(
                    FWK_MODULE_IDX_RCAR_MOCK_PMIC_BD9571,
                    MOD_RCAR_MOCK_PMIC_API_IDX_PSU_DRIVER) },
    },

module.h

  1. 描述 element cfg
  2. 描述 api index
  3. 描述 event index(如果有 event)
  4. 描述 response api(如果有 event)
  5. 描述 Response 结构(如果有 event)
  6. 描述 API 的函数原型
  7. Module 的 Index 来自全局
cpp 复制代码
// product/rcar/module/rcar_pmic/src/mod_rcar_pmic.h
struct mod_rcar_pmic_device_api {
};

struct mod_rcar_pmic_driver_api {
};

enum rcar_pmic_id {
    RCAR_PMIC_CPU_LITTLE,
    RCAR_PMIC_CPU_BIG,
    RCAR_PMIC_CPU_DDR_BKUP,
    RCAR_PMIC_CPU_GPU,
    RCAR_PMIC_CPU_VPU
};

enum mod_rcar_pmic_api_idx {
};

modulc.c

  1. 处理 config 中包含的头文件的 bind
  2. 处理被别的 config 中包含自己的 bind request
  3. 处理 event (如果有 event)
cpp 复制代码
/* Module description */
// product/rcar/module/rcar_pmic/src/mod_rcar_pmic_module.c
const struct fwk_module module_rcar_pmic = {
    .type = FWK_MODULE_TYPE_HAL,
    .init = rcar_pmic_init,
    .element_init = rcar_pmic_element_init,
    .bind = rcar_pmic_bind,
    .process_bind_request = rcar_pmic_process_bind_request,
    .process_event = __mod_rcar_pmic_process_event,
    .api_count = MOD_RCAR_PMIC_API_IDX_COUNT,
    .event_count = MOD_RCAR_PMIC_EVENT_IDX_COUNT,
};

小结

关系

Config.h module.h 更多是定义

mod.c 是具体功能实现,核心是 ops

四个文件中对于模块互相关联关系最重要的文件是 config.c,config.c 说明了这个模块的依赖,即调用关系,看 conifg.c 的头文件包含是最直观的体现,除了 include 自己的 config.h mod.h 其他的都是依赖

举例说明: /product/morello/config_dvfs.c ,看头文件包含可知它依赖 config_clock config_psu 用到了 mod_scmi_perf.h 中定义的常量

cpp 复制代码
// product/morello/scp_ramfw_soc/config_dvfs.c
#include "config_clock.h"
#include "config_dvfs.h"
#include "config_psu.h"

#include <mod_dvfs.h>
#include <mod_scmi_perf.h>

如果要看谁会依赖 config_dvfs.c 全局搜索 config_dvfs.h 即可,搜索结果可知 config_scmi_perf 会调用 config_dvfs.c

组织方式

非常多的模块是 scp module 下面包含的,即在 product 内部实现时,module.c .h 使用通用的,config.c .h 为 product 自定义的

若 module 不是 scp module 自带的,则 module config 都要自己写

模块的初始化

cpp 复制代码
int fwk_module_start(void)
{
    int status;
    unsigned int bind_round;

    if (fwk_module_ctx.initialized) {
        FWK_LOG_CRIT(fwk_module_err_msg_func, FWK_E_STATE, __func__);
        return FWK_E_STATE;
    }

    CLI_DEBUGGER();

    status = __fwk_init(FWK_MODULE_EVENT_COUNT);
    if (status != FWK_SUCCESS) {
        return status;
    }

    fwk_module_ctx.stage = MODULE_STAGE_INITIALIZE;
    fwk_module_init_modules();

    fwk_module_ctx.stage = MODULE_STAGE_BIND;
    for (bind_round = 0; bind_round <= FWK_MODULE_BIND_ROUND_MAX;
         bind_round++) {
        status = fwk_module_bind_modules(bind_round);
        if (status != FWK_SUCCESS) {
            return status;
        }
    }

    fwk_module_ctx.stage = MODULE_STAGE_START;
    status = start_modules();
    if (status != FWK_SUCCESS) {
        return status;
    }

    fwk_module_ctx.initialized = true;

    FWK_LOG_CRIT("[FWK] Module initialization complete!");

    return FWK_SUCCESS;
}

主函数中会用 STAGE 标记当前初始化运行到哪个步骤,可以看到代码中有 INITIALIZE BIND START

每个步骤下都会对当前 product 下所有 module 按在 makefile 中的顺序依次执行要进行的操作。

需要注意这些是需要从静态变成数据结构的 element 的操作,纯静态的 module_index api_index element_index 都不再做操作,尤其是 module_index 是 product makefile 里规定好的,每个 module 都有一个 ID ,且后面会根据这个 ID 做查询操作

Initialize 阶段

Initialize 阶段是 init,这部分执行的步骤:

  1. 模块处理自己的 config,并把自己 config.c 中 element 的静态 config 变成数据结构
  2. 执行 module 的 .init 函数指针对应的函数
  3. 执行 post_init 对应的函数(如果有)

Bind 阶段

Bind 阶段有 module 的 bind,有 element 的 bind,但是绑定的 target 一定是一个 module 不是 module 的 element。就像 spi 总线是一个 module,spi 从设备信息是 spi 总线 module 的 element,spi 从设备驱动是一个 module。spi 从设备信息绑定从设备驱动即 module->element 绑定 module

cpp 复制代码
// framework/src/fwk_module.c
int fwk_module_bind(fwk_id_t target_id, fwk_id_t api_id, const void *api)

值得关注的就是 fwk_module_bind 的函数原型,第三个参数 api 是做 out 用处。函数 bind 返回的结果放在 api 中,为被绑定 module 的函数指针。通过 api 去调用被绑定 module 的 api 实现 include 的效果。

cpp 复制代码
// framework/src/fwk_module.c

fwk_mod_ctx = fwk_module_get_ctx(target_id);

    if (((fwk_module_ctx.stage != MODULE_STAGE_INITIALIZE) ||
         (fwk_mod_ctx->state != FWK_MODULE_STATE_INITIALIZED)) &&
        (fwk_module_ctx.stage != MODULE_STAGE_BIND)) {
        status = FWK_E_STATE;
        goto error;
    }

    status = fwk_mod_ctx->desc->process_bind_request(
        fwk_module_ctx.bind_id, target_id, api_id, (const void **)api);
    if (!fwk_expect(status == FWK_SUCCESS)) {
        FWK_LOG_CRIT(fwk_module_err_msg_line, status, __func__, __LINE__);
        return status;
    }

Bind 中会执行上面的 fwk_mod_ctx->desc->process_bind,fwk_mod_ctx->desc 是 target_id 代表的 module 的函数指针信息,说明 bind 的调用流程为:

Module A : bind -> module core : fwk_module_bind -> module B : process_bind_request

Process bind request 根据传过来的 api_id 返回 api 回去

模块与模块之间的互相绑定示例

具体说明以下 module rcar_pmic 的绑定代码

config_rcar_pmic.c 中的代码段,根据上文所说,应该包含描述 element 的信息

c 复制代码
// SCP-firmware-master/product/rcar/scp_ramfw/config_rcar_pmic.c
#include <config_rcar_pmic.h>
#include <config_rcar_mock_pmic_bd9571.h>

#include <mod_rcar_mock_pmic_bd9571.h>
#include <mod_rcar_pmic.h>

struct mod_rcar_pmic_device_config 定义在 mod_rcar_pmic.h (module.h)

MOD_RCAR_PMIC_ELEMENT_IDX_LITTLE 定义在 config_rcar_pmic.h (config.h)

FWK_MODULE_IDX_RCAR_MOCK_PMIC_BD9571 定义在 mod_rcar_mock_pmic_bd9571.h (module.

MOD_RCAR_MOCK_PMIC_ELEMENT_IDX_LITTLE 定义在 config_rcar_mock_pmic_bd9571.h

cpp 复制代码
// SCP-firmware-master/product/rcar/scp_ramfw/config_rcar_pmic.c
static const struct fwk_element element_table[] = {
    [MOD_RCAR_PMIC_ELEMENT_IDX_LITTLE] = {
        .name = "CPU_GROUP_LITTLE",
        .data =
            &(const struct mod_rcar_pmic_device_config){
                .driver_id = FWK_ID_ELEMENT_INIT(
                    FWK_MODULE_IDX_RCAR_MOCK_PMIC_BD9571,
                    MOD_RCAR_MOCK_PMIC_ELEMENT_IDX_LITTLE),
                .driver_api_id = FWK_ID_API_INIT(
                    FWK_MODULE_IDX_RCAR_MOCK_PMIC_BD9571,
                    MOD_RCAR_MOCK_PMIC_API_IDX_PSU_DRIVER) },

上图为 element 数组,描述了该 element 需要绑定的模块和 API ,config_rcar_pmic 这个配置文件中写明了试图去找哪个模块进行绑定。driver_id driver_api_id 中的 FWK_MODULE_IDX_RCAR_MOCK_PMIC_BD9571 是 module 的 ID

具体模块

关于 Include 链:

在 SCP 中 INCLUDE 就会调用,一般都是上层框架 include 底层具体实现,故看清 include 链条基本也看清了直接调用的链条,这里不包含 event 调用。以下的图不完全是按着代码 config.c 的互相包含来的,仅是示意图

CLOCK & RESET

include:

PMIC

include:

mod_psu 会 bind 特定 PMIC 型号芯片,如 module xr77128

cpp 复制代码
// product/morello/scp_ramfw_soc/config_xr77128.c
#include "config_cdns_i2c.h"
#include "config_psu.h"
#include "config_xr77128.h"

#include <morello_alarm_idx.h>

#include <mod_cdns_i2c.h>
#include <mod_morello_sensor.h>
#include <mod_psu.h>
#include <mod_xr77128.h>

#include <fwk_id.h>
#include <fwk_module.h>
#include <fwk_module_idx.h>
cpp 复制代码
// /module/xr77128/src/mod_xr77128.c
static struct mod_psu_driver_api psu_driver_api = {
    .set_enabled = xr77128_set_enabled,
    .get_enabled = xr77128_get_enabled,
    .set_voltage = xr77128_set_voltage,
    .get_voltage = xr77128_get_voltage,
};

POWER DOMAIN

和 system 相关,内容较多,暂缓

小结

稍微看了下文件组织方式,看完文章应该能加速对 SCP 理解

整体还不是很清楚,event/notification,没分析,system 部分没分析

相关推荐
正在努力的小河1 小时前
Linux设备树简介
linux·运维·服务器
荣光波比1 小时前
Linux(十一)——LVM磁盘配额整理
linux·运维·云计算
LLLLYYYRRRRRTT1 小时前
WordPress (LNMP 架构) 一键部署 Playbook
linux·架构·ansible·mariadb
轻松Ai享生活2 小时前
crash 进程分析流程图
linux
大路谈数字化3 小时前
Centos中内存CPU硬盘的查询
linux·运维·centos
luoqice4 小时前
linux下查看 UDP Server 端口的启用情况
linux
倔强的石头_5 小时前
【Linux指南】动静态库与链接机制:从原理到实践
linux
赏点剩饭7785 小时前
linux中的hostpath卷、nfs卷以及静态持久卷的区别
linux·运维·服务器
神鸟云6 小时前
DELL服务器 R系列 IPMI的配置
linux·运维·服务器·网络·边缘计算·pcdn
herderl6 小时前
**僵尸进程(Zombie Process)** 和**孤儿进程(Orphan Process)**
linux·运维·服务器·网络·网络协议