TA的每日心情 | 开心 2024-11-20 21:23 |
---|
签到天数: 597 天 连续签到: 1 天 [LV.9]以坛为家II
|
本帖最后由 robe.zhang 于 2021-4-14 00:02 编辑
【百度大脑AI计算盒FZ5C】zynq-firmware 驱动代码分析
内核代码是最新的 v5.4 版本,对应 petalinux v2020.1 版本,vitis 2020.1 版本:
设备树 firmware 节点:
- firmware {
- zynqmp_firmware: zynqmp-firmware {
- compatible = "xlnx,zynqmp-firmware";
- method = "smc";
- #power-domain-cells = <0x1>;
- u-boot,dm-pre-reloc;
- zynqmp_pcap: pcap {
- compatible = "xlnx,zynqmp-pcap-fpga";
- clock-names = "ref_clk";
- };
- zynqmp_power: zynqmp-power {
- u-boot,dm-pre-reloc;
- compatible = "xlnx,zynqmp-power";
- interrupt-parent = <&gic>;
- interrupts = <0 35 4>;
- mboxes = <&ipi_mailbox_pmu1 0>,
- <&ipi_mailbox_pmu1 1>;
- mbox-names = "tx", "rx";
- };
- zynqmp_reset: reset-controller {
- compatible = "xlnx,zynqmp-reset";
- #reset-cells = <1>;
- };
- pinctrl0: pinctrl {
- compatible = "xlnx,zynqmp-pinctrl";
- status = "disabled";
- };
- };
- };
复制代码 驱动 match table:
- static const struct of_device_id zynqmp_firmware_of_match[] = {
- {.compatible = "xlnx,zynqmp-firmware"},
- {.compatible = "xlnx,versal-firmware"},
- {},
- };
- MODULE_DEVICE_TABLE(of, zynqmp_firmware_of_match);
复制代码 驱动:module_platform_driver
- static struct platform_driver zynqmp_firmware_driver = {
- .driver = {
- .name = "zynqmp_firmware",
- .of_match_table = zynqmp_firmware_of_match,
- },
- .probe = zynqmp_firmware_probe,
- .remove = zynqmp_firmware_remove,
- };
- module_platform_driver(zynqmp_firmware_driver);
复制代码 驱动:module_platform_driver,找到设备书节点,获取方法,获取 pm_api_version 版本,获取 pm_tz_version 版本,设置 eemi_ops_tbl = eemi_ops,初始化 sysfs,debugfs,添加 power 设备,解析本节点设备树
- static int zynqmp_firmware_probe(struct platform_device *pdev)
- {
- struct device *dev = &pdev->dev;
- struct device_node *np;
- int ret;
- np = of_find_compatible_node(NULL, NULL, "xlnx,zynqmp"); # 找到节点:
- if (!np) {
- np = of_find_compatible_node(NULL, NULL, "xlnx,versal");
- if (!np)
- return 0;
- feature_check_enabled = true;
- }
- of_node_put(np);
- ret = get_set_conduit_method(dev->of_node); # 获取方法:smc
- if (ret)
- return ret;
- /* Check PM API version number */
- zynqmp_pm_get_api_version(&pm_api_version); # 获取 pm_api_version 版本
- if (pm_api_version < ZYNQMP_PM_VERSION) {
- panic("%s Platform Management API version error. Expected: v%d.%d - Found: v%d.%d\n",
- __func__,
- ZYNQMP_PM_VERSION_MAJOR, ZYNQMP_PM_VERSION_MINOR,
- pm_api_version >> 16, pm_api_version & 0xFFFF);
- }
- pr_info("%s Platform Management API v%d.%d\n", __func__,
- pm_api_version >> 16, pm_api_version & 0xFFFF);
- /* Check trustzone version number */
- ret = zynqmp_pm_get_trustzone_version(&pm_tz_version); # 获取 pm_tz_version
- if (ret)
- panic("Legacy trustzone found without version support\n");
- if (pm_tz_version < ZYNQMP_TZ_VERSION)
- panic("%s Trustzone version error. Expected: v%d.%d - Found: v%d.%d\n",
- __func__,
- ZYNQMP_TZ_VERSION_MAJOR, ZYNQMP_TZ_VERSION_MINOR,
- pm_tz_version >> 16, pm_tz_version & 0xFFFF);
- pr_info("%s Trustzone version v%d.%d\n", __func__,
- pm_tz_version >> 16, pm_tz_version & 0xFFFF);
- /* Assign eemi_ops_table */
- eemi_ops_tbl = &eemi_ops; # 设置 eemi_ops_tbl,eemi_ops 是驱动实现的,稍后说
- ret = zynqmp_pm_sysfs_init(); # sysfs init
- if (ret) {
- pr_err("%s() sysfs init fail with error %d\n", __func__, ret);
- return ret;
- }
- zynqmp_pm_api_debugfs_init(); # debugfs init
- ret = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE, firmware_devs, # 添加 power 设备
- ARRAY_SIZE(firmware_devs), NULL, 0, NULL);
- if (ret) {
- dev_err(&pdev->dev, "failed to add MFD devices %d\n", ret);
- return ret;
- }
- return of_platform_populate(dev->of_node, NULL, NULL, dev); # 解析本节点设备树
- }
复制代码 详细解析 probe:
# 获取方法 smc 时,设置 do_fw_call = do_fw_call_smc
- static int get_set_conduit_method(struct device_node *np)
- {
- const char *method;
- if (of_property_read_string(np, "method", &method)) {
- pr_warn("%s missing "method" property\n", __func__);
- return -ENXIO;
- }
- if (!strcmp("hvc", method)) {
- do_fw_call = do_fw_call_hvc;
- } else if (!strcmp("smc", method)) {
- do_fw_call = do_fw_call_smc;
- } else {
- pr_warn("%s Invalid "method" property: %s\n",
- __func__, method);
- return -EINVAL;
- }
- return 0;
- }
复制代码 # do_fw_call_smc 调用 arm_smccc_smc
- static noinline int do_fw_call_smc(u64 arg0, u64 arg1, u64 arg2,
- u32 *ret_payload)
- {
- struct arm_smccc_res res;
- arm_smccc_smc(arg0, arg1, arg2, 0, 0, 0, 0, 0, &res);
- if (ret_payload) {
- ret_payload[0] = lower_32_bits(res.a0);
- ret_payload[1] = upper_32_bits(res.a0);
- ret_payload[2] = lower_32_bits(res.a1);
- ret_payload[3] = upper_32_bits(res.a1);
- }
- return zynqmp_pm_ret_code((enum pm_ret_status)res.a0);
- }
复制代码 # arm_smccc_smc 是宏定义 __arm_smccc_smc
- #define arm_smccc_smc(...) __arm_smccc_smc(__VA_ARGS__, NULL)
复制代码 # __arm_smccc_smc 是宏 SMCCC smc,SMCCC smc 调用 smc #0 处理
- asmlinkage void __arm_smccc_smc(unsigned long a0, unsigned long a1,
- unsigned long a2, unsigned long a3, unsigned long a4,
- unsigned long a5, unsigned long a6, unsigned long a7,
- struct arm_smccc_res *res, struct arm_smccc_quirk *quirk);
- .macro SMCCC instr
- .cfi_startproc
- \instr #0
- ldr x4, [sp]
- stp x0, x1, [x4, #ARM_SMCCC_RES_X0_OFFS]
- stp x2, x3, [x4, #ARM_SMCCC_RES_X2_OFFS]
- ldr x4, [sp, #8]
- cbz x4, 1f /* no quirk structure */
- ldr x9, [x4, #ARM_SMCCC_QUIRK_ID_OFFS]
- cmp x9, #ARM_SMCCC_QUIRK_QCOM_A6
- b.ne 1f
- str x6, [x4, ARM_SMCCC_QUIRK_STATE_OFFS]
- 1: ret
- .cfi_endproc
- .endm
- /*
- * void arm_smccc_smc(unsigned long a0, unsigned long a1, unsigned long a2,
- * unsigned long a3, unsigned long a4, unsigned long a5,
- * unsigned long a6, unsigned long a7, struct arm_smccc_res *res,
- * struct arm_smccc_quirk *quirk)
- */
- ENTRY(__arm_smccc_smc)
- SMCCC smc
- ENDPROC(__arm_smccc_smc)
- EXPORT_SYMBOL(__arm_smccc_smc)
复制代码 # ==== eemi_ops 结构体和实现
- struct zynqmp_eemi_ops {
- int (*get_api_version)(u32 *version);
- int (*get_chipid)(u32 *idcode, u32 *version);
- int (*fpga_load)(const u64 address, const u32 size, const u32 flags);
- int (*fpga_get_status)(u32 *value);
- int (*query_data)(struct zynqmp_pm_query_data qdata, u32 *out);
- int (*clock_enable)(u32 clock_id);
- int (*clock_disable)(u32 clock_id);
- int (*clock_getstate)(u32 clock_id, u32 *state);
- int (*clock_setdivider)(u32 clock_id, u32 divider);
- int (*clock_getdivider)(u32 clock_id, u32 *divider);
- int (*clock_setrate)(u32 clock_id, u64 rate);
- int (*clock_getrate)(u32 clock_id, u64 *rate);
- int (*clock_setparent)(u32 clock_id, u32 parent_id);
- int (*clock_getparent)(u32 clock_id, u32 *parent_id);
- int (*ioctl)(u32 node_id, u32 ioctl_id, u32 arg1, u32 arg2, u32 *out);
- int (*reset_assert)(const enum zynqmp_pm_reset reset,
- const enum zynqmp_pm_reset_action assert_flag);
- int (*reset_get_status)(const enum zynqmp_pm_reset reset, u32 *status);
- int (*init_finalize)(void);
- int (*set_suspend_mode)(u32 mode);
- int (*request_node)(const u32 node,
- const u32 capabilities,
- const u32 qos,
- const enum zynqmp_pm_request_ack ack);
- int (*release_node)(const u32 node);
- int (*set_requirement)(const u32 node,
- const u32 capabilities,
- const u32 qos,
- const enum zynqmp_pm_request_ack ack);
- int (*fpga_read)(const u32 reg_numframes, const u64 phys_address,
- u32 readback_type, u32 *value);
- int (*sha_hash)(const u64 address, const u32 size, const u32 flags);
- int (*rsa)(const u64 address, const u32 size, const u32 flags);
- int (*request_suspend)(const u32 node,
- const enum zynqmp_pm_request_ack ack,
- const u32 latency,
- const u32 state);
- int (*force_powerdown)(const u32 target,
- const enum zynqmp_pm_request_ack ack);
- int (*request_wakeup)(const u32 node,
- const bool set_addr,
- const u64 address,
- const enum zynqmp_pm_request_ack ack);
- int (*set_wakeup_source)(const u32 target,
- const u32 wakeup_node,
- const u32 enable);
- int (*system_shutdown)(const u32 type, const u32 subtype);
- int (*set_max_latency)(const u32 node, const u32 latency);
- int (*set_configuration)(const u32 physical_addr);
- int (*get_node_status)(const u32 node, u32 *const status,
- u32 *const requirements, u32 *const usage);
- int (*get_operating_characteristic)(const u32 node,
- const enum zynqmp_pm_opchar_type
- type, u32 *const result);
- int (*pinctrl_request)(const u32 pin);
- int (*pinctrl_release)(const u32 pin);
- int (*pinctrl_get_function)(const u32 pin, u32 *id);
- int (*pinctrl_set_function)(const u32 pin, const u32 id);
- int (*pinctrl_get_config)(const u32 pin, const u32 param, u32 *value);
- int (*pinctrl_set_config)(const u32 pin, const u32 param, u32 value);
- int (*register_access)(u32 register_access_id, u32 address,
- u32 mask, u32 value, u32 *out);
- int (*aes)(const u64 address, u32 *out);
- int (*efuse_access)(const u64 address, u32 *out);
- int (*secure_image)(const u64 src_addr, u64 key_addr, u64 *dst);
- int (*pdi_load)(const u32 src, const u64 address);
- };
复制代码 实现
- static const struct zynqmp_eemi_ops eemi_ops = {
- .get_api_version = zynqmp_pm_get_api_version,
- .get_chipid = zynqmp_pm_get_chipid,
- .query_data = zynqmp_pm_query_data,
- .clock_enable = zynqmp_pm_clock_enable,
- .clock_disable = zynqmp_pm_clock_disable,
- .clock_getstate = zynqmp_pm_clock_getstate,
- .clock_setdivider = zynqmp_pm_clock_setdivider,
- .clock_getdivider = zynqmp_pm_clock_getdivider,
- .clock_setrate = zynqmp_pm_clock_setrate,
- .clock_getrate = zynqmp_pm_clock_getrate,
- .clock_setparent = zynqmp_pm_clock_setparent,
- .clock_getparent = zynqmp_pm_clock_getparent,
- .ioctl = zynqmp_pm_ioctl,
- .reset_assert = zynqmp_pm_reset_assert,
- .reset_get_status = zynqmp_pm_reset_get_status,
- .init_finalize = zynqmp_pm_init_finalize,
- .set_suspend_mode = zynqmp_pm_set_suspend_mode,
- .request_node = zynqmp_pm_request_node,
- .release_node = zynqmp_pm_release_node,
- .set_requirement = zynqmp_pm_set_requirement,
- .fpga_load = zynqmp_pm_fpga_load,
- .fpga_get_status = zynqmp_pm_fpga_get_status,
- .fpga_read = zynqmp_pm_fpga_read,
- .sha_hash = zynqmp_pm_sha_hash,
- .rsa = zynqmp_pm_rsa,
- .request_suspend = zynqmp_pm_request_suspend,
- .force_powerdown = zynqmp_pm_force_powerdown,
- .request_wakeup = zynqmp_pm_request_wakeup,
- .set_wakeup_source = zynqmp_pm_set_wakeup_source,
- .system_shutdown = zynqmp_pm_system_shutdown,
- .set_max_latency = zynqmp_pm_set_max_latency,
- .set_configuration = zynqmp_pm_set_configuration,
- .get_node_status = zynqmp_pm_get_node_status,
- .get_operating_characteristic = zynqmp_pm_get_operating_characteristic,
- .pinctrl_request = zynqmp_pm_pinctrl_request,
- .pinctrl_release = zynqmp_pm_pinctrl_release,
- .pinctrl_get_function = zynqmp_pm_pinctrl_get_function,
- .pinctrl_set_function = zynqmp_pm_pinctrl_set_function,
- .pinctrl_get_config = zynqmp_pm_pinctrl_get_config,
- .pinctrl_set_config = zynqmp_pm_pinctrl_set_config,
- .register_access = zynqmp_pm_config_reg_access,
- .aes = zynqmp_pm_aes_engine,
- .efuse_access = zynqmp_pm_efuse_access,
- .pdi_load = zynqmp_pm_load_pdi,
- .secure_image = zynqmp_pm_secure_load,
- };
复制代码 # 看几个实现:获取 api_version 版本:zynqmp_pm_get_api_version
- static int zynqmp_pm_get_api_version(u32 *version)
- {
- u32 ret_payload[PAYLOAD_ARG_CNT];
- int ret;
- if (!version)
- return -EINVAL;
- /* Check is PM API version already verified */
- if (pm_api_version > 0) { # probe 时已经获取保存在 pm_api_version 中了
- *version = pm_api_version;
- return 0;
- }
- ret = zynqmp_pm_invoke_fn(PM_GET_API_VERSION, 0, 0, 0, 0, ret_payload); # 如果没有调用 zynqmp_pm_invoke_fn
- *version = ret_payload[1]; # ret_payload 是函数返回值,返回给 version
- return ret;
- }
- static int zynqmp_pm_get_chipid(u32 *idcode, u32 *version) # 获取 chiped 和 version
- {
- u32 ret_payload[PAYLOAD_ARG_CNT];
- int ret;
- if (!idcode || !version)
- return -EINVAL;
- ret = zynqmp_pm_invoke_fn(PM_GET_CHIPID, 0, 0, 0, 0, ret_payload);
- *idcode = ret_payload[1];
- *version = ret_payload[2];
- return ret;
- }
- static int zynqmp_pm_query_data(struct zynqmp_pm_query_data qdata, u32 *out) # query_data,查询结果放在 out 中,返回值给 ret
- {
- int ret;
- ret = zynqmp_pm_invoke_fn(PM_QUERY_DATA, qdata.qid, qdata.arg1,
- qdata.arg2, qdata.arg3, out);
- /*
- * For clock name query, all bytes in SMC response are clock name
- * characters and return code is always success. For invalid clocks,
- * clock name bytes would be zeros.
- */
- return qdata.qid == PM_QID_CLOCK_GET_NAME ? 0 : ret;
- }
- static int zynqmp_pm_clock_enable(u32 clock_id) # 没有返回值,直接写数据
- {
- return zynqmp_pm_invoke_fn(PM_CLOCK_ENABLE, clock_id, 0, 0, 0, NULL);
- }
- static int zynqmp_pm_clock_getstate(u32 clock_id, u32 *state) # 有返回值,保存返回值
- {
- u32 ret_payload[PAYLOAD_ARG_CNT];
- int ret;
- ret = zynqmp_pm_invoke_fn(PM_CLOCK_GETSTATE, clock_id, 0,
- 0, 0, ret_payload);
- *state = ret_payload[1];
- return ret;
- }
- static int zynqmp_pm_pinctrl_get_function(const u32 pin, u32 *id)
- {
- u32 ret_payload[PAYLOAD_ARG_CNT];
- int ret;
- if (!id)
- return -EINVAL;
- ret = zynqmp_pm_invoke_fn(PM_PINCTRL_GET_FUNCTION, pin, 0,
- 0, 0, ret_payload);
- *id = ret_payload[1];
- return ret;
- }
复制代码 # 以上这些方法有个特点,全部调用了 zynqmp_pm_invoke_fn 函数实现的
# zynqmp_pm_invoke_fn 函数调用了 do_fw_call 函数
- int zynqmp_pm_invoke_fn(u32 pm_api_id, u32 arg0, u32 arg1,
- u32 arg2, u32 arg3, u32 *ret_payload)
- {
- /*
- * Added SIP service call Function Identifier
- * Make sure to stay in x0 register
- */
- u64 smc_arg[4];
- if (zynqmp_pm_feature(pm_api_id) == PM_FEATURE_INVALID)
- return -ENOTSUPP;
- smc_arg[0] = PM_SIP_SVC | pm_api_id;
- smc_arg[1] = ((u64)arg1 << 32) | arg0;
- smc_arg[2] = ((u64)arg3 << 32) | arg2;
- return do_fw_call(smc_arg[0], smc_arg[1], smc_arg[2], ret_payload);
- }
复制代码 # do_fw_call 初始化的时候为 do_fw_call_fail,也就是没有初始化的话,调用它会报错
- static int (*do_fw_call)(u64, u64, u64, u32 *ret_payload) = do_fw_call_fail;
- static noinline int do_fw_call_fail(u64 arg0, u64 arg1, u64 arg2,
- u32 *ret_payload)
- {
- return -ENODEV;
- }
复制代码 看 probe 中的 get_set_conduit_method 函数,初始化了 do_fw_call = do_fw_call_smc
- static int get_set_conduit_method(struct device_node *np)
- {
- const char *method;
- if (of_property_read_string(np, "method", &method)) {
- pr_warn("%s missing "method" property\n", __func__);
- return -ENXIO;
- }
- if (!strcmp("hvc", method)) {
- do_fw_call = do_fw_call_hvc;
- } else if (!strcmp("smc", method)) {
- do_fw_call = do_fw_call_smc;
- } else {
- pr_warn("%s Invalid "method" property: %s\n",
- __func__, method);
- return -EINVAL;
- }
- return 0;
- }
复制代码 do_fw_call 就是被 zynqmp_pm_invoke_fn 调用,实现 firmware 所有的 ops
- int zynqmp_pm_invoke_fn(u32 pm_api_id, u32 arg0, u32 arg1,
- u32 arg2, u32 arg3, u32 *ret_payload)
- {
- /*
- * Added SIP service call Function Identifier
- * Make sure to stay in x0 register
- */
- u64 smc_arg[4];
- if (zynqmp_pm_feature(pm_api_id) == PM_FEATURE_INVALID)
- return -ENOTSUPP;
- smc_arg[0] = PM_SIP_SVC | pm_api_id;
- smc_arg[1] = ((u64)arg1 << 32) | arg0;
- smc_arg[2] = ((u64)arg3 << 32) | arg2;
- return do_fw_call(smc_arg[0], smc_arg[1], smc_arg[2], ret_payload);
- }
复制代码 结论:
1,firmware 驱动方法包括 版本号、芯片ID、fpga 管理、clock 复位 电源 安全 加密 pinctrl efuse 等管理
2,firmware 所有方法通过 smc #0 指令处理,处理完成返回结果和返回值。
|
|