电源管理 ------ 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
- 描述 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
- 根据 config.h 中的 element index 创建 element,其 struct 形式来自 module.h 描述的 element 结构
- 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
- 描述 element cfg
- 描述 api index
- 描述 event index(如果有 event)
- 描述 response api(如果有 event)
- 描述 Response 结构(如果有 event)
- 描述 API 的函数原型
- 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
- 处理 config 中包含的头文件的 bind
- 处理被别的 config 中包含自己的 bind request
- 处理 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,这部分执行的步骤:
- 模块处理自己的 config,并把自己 config.c 中 element 的静态 config 变成数据结构
- 执行 module 的 .init 函数指针对应的函数
- 执行 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 部分没分析