电源管理 —— 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 部分没分析

相关推荐
苦逼IT运维15 分钟前
YUM 源与 APT 源的详解及使用指南
linux·运维·ubuntu·centos·devops
仍有未知等待探索33 分钟前
Linux 传输层UDP
linux·运维·udp
zeruns80240 分钟前
如何搭建自己的域名邮箱服务器?Poste.io邮箱服务器搭建教程,Linux+Docker搭建邮件服务器的教程
linux·运维·服务器·docker·网站
卑微求AC41 分钟前
(C语言贪吃蛇)16.贪吃蛇食物位置随机(完结撒花)
linux·c语言·开发语言·嵌入式·c语言贪吃蛇
Hugo_McQueen1 小时前
pWnos1.0 靶机渗透 (Perl CGI 的反弹 shell 利用)
linux·服务器·网络安全
XY.散人1 小时前
初识Linux · 文件(1)
linux·运维·服务器
叶北辰CHINA2 小时前
nginx反向代理,负载均衡,HTTP配置简述(说人话)
linux·运维·nginx·http·云原生·https·负载均衡
不惑_3 小时前
在 Ubuntu 安装 Python3.7(没有弯路)
linux·运维·ubuntu
玉树临风江流儿4 小时前
Linux驱动开发(速记版)--设备模型
linux·驱动开发
杰哥在此4 小时前
Python知识点:如何使用Multiprocessing进行并行任务管理
linux·开发语言·python·面试·编程