查看: 1802|回复: 0

[评测分享] 【百度大脑AI计算盒FZ5C】zynq-firmware 驱动代码分析

[复制链接]
  • TA的每日心情
    开心
    2024-1-16 17:48
  • 签到天数: 592 天

    连续签到: 1 天

    [LV.9]以坛为家II

    发表于 2021-4-13 23:27:24 | 显示全部楼层 |阅读模式
    分享到:
    本帖最后由 robe.zhang 于 2021-4-14 00:02 编辑

    【百度大脑AI计算盒FZ5C】zynq-firmware 驱动代码分析

    内核代码是最新的 v5.4 版本,对应 petalinux v2020.1 版本,vitis 2020.1 版本:

    设备树 firmware 节点:
    1.         firmware {
    2.                 zynqmp_firmware: zynqmp-firmware {
    3.                         compatible = "xlnx,zynqmp-firmware";
    4.                         method = "smc";
    5.                         #power-domain-cells = <0x1>;
    6.                         u-boot,dm-pre-reloc;

    7.                         zynqmp_pcap: pcap {
    8.                                 compatible = "xlnx,zynqmp-pcap-fpga";
    9.                                 clock-names = "ref_clk";
    10.                         };

    11.                         zynqmp_power: zynqmp-power {
    12.                                 u-boot,dm-pre-reloc;
    13.                                 compatible = "xlnx,zynqmp-power";
    14.                                 interrupt-parent = <&gic>;
    15.                                 interrupts = <0 35 4>;
    16.                                 mboxes = <&ipi_mailbox_pmu1 0>,
    17.                                          <&ipi_mailbox_pmu1 1>;
    18.                                 mbox-names = "tx", "rx";
    19.                         };

    20.                         zynqmp_reset: reset-controller {
    21.                                 compatible = "xlnx,zynqmp-reset";
    22.                                 #reset-cells = <1>;
    23.                         };

    24.                         pinctrl0: pinctrl {
    25.                                 compatible = "xlnx,zynqmp-pinctrl";
    26.                                 status = "disabled";
    27.                         };
    28.                 };
    29.         };
    复制代码
    驱动 match table:
    1. static const struct of_device_id zynqmp_firmware_of_match[] = {
    2.         {.compatible = "xlnx,zynqmp-firmware"},
    3.         {.compatible = "xlnx,versal-firmware"},
    4.         {},
    5. };
    6. MODULE_DEVICE_TABLE(of, zynqmp_firmware_of_match);
    复制代码
    驱动:module_platform_driver
    1. static struct platform_driver zynqmp_firmware_driver = {
    2.         .driver = {
    3.                 .name = "zynqmp_firmware",
    4.                 .of_match_table = zynqmp_firmware_of_match,
    5.         },
    6.         .probe = zynqmp_firmware_probe,
    7.         .remove = zynqmp_firmware_remove,
    8. };
    9. module_platform_driver(zynqmp_firmware_driver);
    复制代码
    驱动:module_platform_driver,找到设备书节点,获取方法,获取 pm_api_version 版本,获取 pm_tz_version 版本,设置 eemi_ops_tbl = eemi_ops,初始化 sysfs,debugfs,添加 power 设备,解析本节点设备树
    1. static int zynqmp_firmware_probe(struct platform_device *pdev)
    2. {
    3.         struct device *dev = &pdev->dev;
    4.         struct device_node *np;
    5.         int ret;

    6.         np = of_find_compatible_node(NULL, NULL, "xlnx,zynqmp");                                # 找到节点:
    7.         if (!np) {
    8.                 np = of_find_compatible_node(NULL, NULL, "xlnx,versal");
    9.                 if (!np)
    10.                         return 0;

    11.                 feature_check_enabled = true;
    12.         }
    13.         of_node_put(np);

    14.         ret = get_set_conduit_method(dev->of_node);                                                                # 获取方法:smc
    15.         if (ret)
    16.                 return ret;

    17.         /* Check PM API version number */
    18.         zynqmp_pm_get_api_version(&pm_api_version);                                                                # 获取 pm_api_version 版本
    19.         if (pm_api_version < ZYNQMP_PM_VERSION) {
    20.                 panic("%s Platform Management API version error. Expected: v%d.%d - Found: v%d.%d\n",
    21.                       __func__,
    22.                       ZYNQMP_PM_VERSION_MAJOR, ZYNQMP_PM_VERSION_MINOR,
    23.                       pm_api_version >> 16, pm_api_version & 0xFFFF);
    24.         }

    25.         pr_info("%s Platform Management API v%d.%d\n", __func__,
    26.                 pm_api_version >> 16, pm_api_version & 0xFFFF);

    27.         /* Check trustzone version number */
    28.         ret = zynqmp_pm_get_trustzone_version(&pm_tz_version);                                        # 获取 pm_tz_version
    29.         if (ret)
    30.                 panic("Legacy trustzone found without version support\n");

    31.         if (pm_tz_version < ZYNQMP_TZ_VERSION)
    32.                 panic("%s Trustzone version error. Expected: v%d.%d - Found: v%d.%d\n",
    33.                       __func__,
    34.                       ZYNQMP_TZ_VERSION_MAJOR, ZYNQMP_TZ_VERSION_MINOR,
    35.                       pm_tz_version >> 16, pm_tz_version & 0xFFFF);

    36.         pr_info("%s Trustzone version v%d.%d\n", __func__,
    37.                 pm_tz_version >> 16, pm_tz_version & 0xFFFF);

    38.         /* Assign eemi_ops_table */
    39.         eemi_ops_tbl = &eemi_ops;                                                                                                # 设置 eemi_ops_tbl,eemi_ops 是驱动实现的,稍后说

    40.         ret = zynqmp_pm_sysfs_init();                                                                                        # sysfs init
    41.         if (ret) {
    42.                 pr_err("%s() sysfs init fail with error %d\n", __func__, ret);
    43.                 return ret;
    44.         }

    45.         zynqmp_pm_api_debugfs_init();                                                                                        # debugfs init

    46.         ret = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE, firmware_devs,        # 添加 power 设备
    47.                               ARRAY_SIZE(firmware_devs), NULL, 0, NULL);
    48.         if (ret) {
    49.                 dev_err(&pdev->dev, "failed to add MFD devices %d\n", ret);
    50.                 return ret;
    51.         }

    52.         return of_platform_populate(dev->of_node, NULL, NULL, dev);                                # 解析本节点设备树
    53. }
    复制代码
    详细解析 probe:
    #  获取方法 smc 时,设置 do_fw_call = do_fw_call_smc
    1. static int get_set_conduit_method(struct device_node *np)
    2. {
    3.         const char *method;

    4.         if (of_property_read_string(np, "method", &method)) {
    5.                 pr_warn("%s missing "method" property\n", __func__);
    6.                 return -ENXIO;
    7.         }

    8.         if (!strcmp("hvc", method)) {
    9.                 do_fw_call = do_fw_call_hvc;
    10.         } else if (!strcmp("smc", method)) {
    11.                 do_fw_call = do_fw_call_smc;
    12.         } else {
    13.                 pr_warn("%s Invalid "method" property: %s\n",
    14.                         __func__, method);
    15.                 return -EINVAL;
    16.         }

    17.         return 0;
    18. }
    复制代码
    # do_fw_call_smc 调用 arm_smccc_smc
    1. static noinline int do_fw_call_smc(u64 arg0, u64 arg1, u64 arg2,
    2.                                    u32 *ret_payload)
    3. {
    4.         struct arm_smccc_res res;

    5.         arm_smccc_smc(arg0, arg1, arg2, 0, 0, 0, 0, 0, &res);

    6.         if (ret_payload) {
    7.                 ret_payload[0] = lower_32_bits(res.a0);
    8.                 ret_payload[1] = upper_32_bits(res.a0);
    9.                 ret_payload[2] = lower_32_bits(res.a1);
    10.                 ret_payload[3] = upper_32_bits(res.a1);
    11.         }

    12.         return zynqmp_pm_ret_code((enum pm_ret_status)res.a0);
    13. }
    复制代码
    # arm_smccc_smc 是宏定义 __arm_smccc_smc
    1. #define arm_smccc_smc(...) __arm_smccc_smc(__VA_ARGS__, NULL)
    复制代码
    # __arm_smccc_smc 是宏 SMCCC        smc,SMCCC        smc 调用 smc #0 处理
    1. asmlinkage void __arm_smccc_smc(unsigned long a0, unsigned long a1,
    2.                         unsigned long a2, unsigned long a3, unsigned long a4,
    3.                         unsigned long a5, unsigned long a6, unsigned long a7,
    4.                         struct arm_smccc_res *res, struct arm_smccc_quirk *quirk);

    5.         .macro SMCCC instr
    6.         .cfi_startproc
    7.         \instr        #0
    8.         ldr        x4, [sp]
    9.         stp        x0, x1, [x4, #ARM_SMCCC_RES_X0_OFFS]
    10.         stp        x2, x3, [x4, #ARM_SMCCC_RES_X2_OFFS]
    11.         ldr        x4, [sp, #8]
    12.         cbz        x4, 1f /* no quirk structure */
    13.         ldr        x9, [x4, #ARM_SMCCC_QUIRK_ID_OFFS]
    14.         cmp        x9, #ARM_SMCCC_QUIRK_QCOM_A6
    15.         b.ne        1f
    16.         str        x6, [x4, ARM_SMCCC_QUIRK_STATE_OFFS]
    17. 1:        ret
    18.         .cfi_endproc
    19.         .endm

    20. /*
    21. * void arm_smccc_smc(unsigned long a0, unsigned long a1, unsigned long a2,
    22. *                  unsigned long a3, unsigned long a4, unsigned long a5,
    23. *                  unsigned long a6, unsigned long a7, struct arm_smccc_res *res,
    24. *                  struct arm_smccc_quirk *quirk)
    25. */
    26. ENTRY(__arm_smccc_smc)
    27.         SMCCC        smc
    28. ENDPROC(__arm_smccc_smc)
    29. EXPORT_SYMBOL(__arm_smccc_smc)
    复制代码
    # ==== eemi_ops 结构体和实现
    1. struct zynqmp_eemi_ops {
    2.         int (*get_api_version)(u32 *version);
    3.         int (*get_chipid)(u32 *idcode, u32 *version);
    4.         int (*fpga_load)(const u64 address, const u32 size, const u32 flags);
    5.         int (*fpga_get_status)(u32 *value);
    6.         int (*query_data)(struct zynqmp_pm_query_data qdata, u32 *out);
    7.         int (*clock_enable)(u32 clock_id);
    8.         int (*clock_disable)(u32 clock_id);
    9.         int (*clock_getstate)(u32 clock_id, u32 *state);
    10.         int (*clock_setdivider)(u32 clock_id, u32 divider);
    11.         int (*clock_getdivider)(u32 clock_id, u32 *divider);
    12.         int (*clock_setrate)(u32 clock_id, u64 rate);
    13.         int (*clock_getrate)(u32 clock_id, u64 *rate);
    14.         int (*clock_setparent)(u32 clock_id, u32 parent_id);
    15.         int (*clock_getparent)(u32 clock_id, u32 *parent_id);
    16.         int (*ioctl)(u32 node_id, u32 ioctl_id, u32 arg1, u32 arg2, u32 *out);
    17.         int (*reset_assert)(const enum zynqmp_pm_reset reset,
    18.                             const enum zynqmp_pm_reset_action assert_flag);
    19.         int (*reset_get_status)(const enum zynqmp_pm_reset reset, u32 *status);
    20.         int (*init_finalize)(void);
    21.         int (*set_suspend_mode)(u32 mode);
    22.         int (*request_node)(const u32 node,
    23.                             const u32 capabilities,
    24.                             const u32 qos,
    25.                             const enum zynqmp_pm_request_ack ack);
    26.         int (*release_node)(const u32 node);
    27.         int (*set_requirement)(const u32 node,
    28.                                const u32 capabilities,
    29.                                const u32 qos,
    30.                                const enum zynqmp_pm_request_ack ack);
    31.         int (*fpga_read)(const u32 reg_numframes, const u64 phys_address,
    32.                          u32 readback_type, u32 *value);
    33.         int (*sha_hash)(const u64 address, const u32 size, const u32 flags);
    34.         int (*rsa)(const u64 address, const u32 size, const u32 flags);
    35.         int (*request_suspend)(const u32 node,
    36.                                const enum zynqmp_pm_request_ack ack,
    37.                                const u32 latency,
    38.                                const u32 state);
    39.         int (*force_powerdown)(const u32 target,
    40.                                const enum zynqmp_pm_request_ack ack);
    41.         int (*request_wakeup)(const u32 node,
    42.                               const bool set_addr,
    43.                               const u64 address,
    44.                               const enum zynqmp_pm_request_ack ack);
    45.         int (*set_wakeup_source)(const u32 target,
    46.                                  const u32 wakeup_node,
    47.                                  const u32 enable);
    48.         int (*system_shutdown)(const u32 type, const u32 subtype);
    49.         int (*set_max_latency)(const u32 node, const u32 latency);
    50.         int (*set_configuration)(const u32 physical_addr);
    51.         int (*get_node_status)(const u32 node, u32 *const status,
    52.                                u32 *const requirements, u32 *const usage);
    53.         int (*get_operating_characteristic)(const u32 node,
    54.                                             const enum zynqmp_pm_opchar_type
    55.                                             type, u32 *const result);
    56.         int (*pinctrl_request)(const u32 pin);
    57.         int (*pinctrl_release)(const u32 pin);
    58.         int (*pinctrl_get_function)(const u32 pin, u32 *id);
    59.         int (*pinctrl_set_function)(const u32 pin, const u32 id);
    60.         int (*pinctrl_get_config)(const u32 pin, const u32 param, u32 *value);
    61.         int (*pinctrl_set_config)(const u32 pin, const u32 param, u32 value);
    62.         int (*register_access)(u32 register_access_id, u32 address,
    63.                                u32 mask, u32 value, u32 *out);
    64.         int (*aes)(const u64 address, u32 *out);
    65.         int (*efuse_access)(const u64 address, u32 *out);
    66.         int (*secure_image)(const u64 src_addr, u64 key_addr, u64 *dst);
    67.         int (*pdi_load)(const u32 src, const u64 address);
    68. };
    复制代码
    实现
    1. static const struct zynqmp_eemi_ops eemi_ops = {
    2.         .get_api_version = zynqmp_pm_get_api_version,
    3.         .get_chipid = zynqmp_pm_get_chipid,
    4.         .query_data = zynqmp_pm_query_data,
    5.         .clock_enable = zynqmp_pm_clock_enable,
    6.         .clock_disable = zynqmp_pm_clock_disable,
    7.         .clock_getstate = zynqmp_pm_clock_getstate,
    8.         .clock_setdivider = zynqmp_pm_clock_setdivider,
    9.         .clock_getdivider = zynqmp_pm_clock_getdivider,
    10.         .clock_setrate = zynqmp_pm_clock_setrate,
    11.         .clock_getrate = zynqmp_pm_clock_getrate,
    12.         .clock_setparent = zynqmp_pm_clock_setparent,
    13.         .clock_getparent = zynqmp_pm_clock_getparent,
    14.         .ioctl = zynqmp_pm_ioctl,
    15.         .reset_assert = zynqmp_pm_reset_assert,
    16.         .reset_get_status = zynqmp_pm_reset_get_status,
    17.         .init_finalize = zynqmp_pm_init_finalize,
    18.         .set_suspend_mode = zynqmp_pm_set_suspend_mode,
    19.         .request_node = zynqmp_pm_request_node,
    20.         .release_node = zynqmp_pm_release_node,
    21.         .set_requirement = zynqmp_pm_set_requirement,
    22.         .fpga_load = zynqmp_pm_fpga_load,
    23.         .fpga_get_status = zynqmp_pm_fpga_get_status,
    24.         .fpga_read = zynqmp_pm_fpga_read,
    25.         .sha_hash = zynqmp_pm_sha_hash,
    26.         .rsa = zynqmp_pm_rsa,
    27.         .request_suspend = zynqmp_pm_request_suspend,
    28.         .force_powerdown = zynqmp_pm_force_powerdown,
    29.         .request_wakeup = zynqmp_pm_request_wakeup,
    30.         .set_wakeup_source = zynqmp_pm_set_wakeup_source,
    31.         .system_shutdown = zynqmp_pm_system_shutdown,
    32.         .set_max_latency = zynqmp_pm_set_max_latency,
    33.         .set_configuration = zynqmp_pm_set_configuration,
    34.         .get_node_status = zynqmp_pm_get_node_status,
    35.         .get_operating_characteristic = zynqmp_pm_get_operating_characteristic,
    36.         .pinctrl_request = zynqmp_pm_pinctrl_request,
    37.         .pinctrl_release = zynqmp_pm_pinctrl_release,
    38.         .pinctrl_get_function = zynqmp_pm_pinctrl_get_function,
    39.         .pinctrl_set_function = zynqmp_pm_pinctrl_set_function,
    40.         .pinctrl_get_config = zynqmp_pm_pinctrl_get_config,
    41.         .pinctrl_set_config = zynqmp_pm_pinctrl_set_config,
    42.         .register_access = zynqmp_pm_config_reg_access,
    43.         .aes = zynqmp_pm_aes_engine,
    44.         .efuse_access = zynqmp_pm_efuse_access,
    45.         .pdi_load = zynqmp_pm_load_pdi,
    46.         .secure_image = zynqmp_pm_secure_load,
    47. };
    复制代码
    # 看几个实现:获取 api_version 版本:zynqmp_pm_get_api_version
    1. static int zynqmp_pm_get_api_version(u32 *version)
    2. {
    3.         u32 ret_payload[PAYLOAD_ARG_CNT];
    4.         int ret;

    5.         if (!version)
    6.                 return -EINVAL;

    7.         /* Check is PM API version already verified */
    8.         if (pm_api_version > 0) {                                                                                                        # probe 时已经获取保存在 pm_api_version 中了
    9.                 *version = pm_api_version;
    10.                 return 0;
    11.         }
    12.         ret = zynqmp_pm_invoke_fn(PM_GET_API_VERSION, 0, 0, 0, 0, ret_payload);                # 如果没有调用 zynqmp_pm_invoke_fn
    13.         *version = ret_payload[1];                                                                                                        # ret_payload 是函数返回值,返回给 version

    14.         return ret;
    15. }

    16. static int zynqmp_pm_get_chipid(u32 *idcode, u32 *version)                                                # 获取 chiped 和 version
    17. {
    18.         u32 ret_payload[PAYLOAD_ARG_CNT];
    19.         int ret;

    20.         if (!idcode || !version)
    21.                 return -EINVAL;

    22.         ret = zynqmp_pm_invoke_fn(PM_GET_CHIPID, 0, 0, 0, 0, ret_payload);
    23.         *idcode = ret_payload[1];
    24.         *version = ret_payload[2];

    25.         return ret;
    26. }

    27. static int zynqmp_pm_query_data(struct zynqmp_pm_query_data qdata, u32 *out)        # query_data,查询结果放在 out 中,返回值给 ret
    28. {
    29.         int ret;

    30.         ret = zynqmp_pm_invoke_fn(PM_QUERY_DATA, qdata.qid, qdata.arg1,
    31.                                   qdata.arg2, qdata.arg3, out);

    32.         /*
    33.          * For clock name query, all bytes in SMC response are clock name
    34.          * characters and return code is always success. For invalid clocks,
    35.          * clock name bytes would be zeros.
    36.          */
    37.         return qdata.qid == PM_QID_CLOCK_GET_NAME ? 0 : ret;
    38. }

    39. static int zynqmp_pm_clock_enable(u32 clock_id)                                                                # 没有返回值,直接写数据
    40. {
    41.         return zynqmp_pm_invoke_fn(PM_CLOCK_ENABLE, clock_id, 0, 0, 0, NULL);
    42. }

    43. static int zynqmp_pm_clock_getstate(u32 clock_id, u32 *state)                                # 有返回值,保存返回值
    44. {
    45.         u32 ret_payload[PAYLOAD_ARG_CNT];
    46.         int ret;

    47.         ret = zynqmp_pm_invoke_fn(PM_CLOCK_GETSTATE, clock_id, 0,
    48.                                   0, 0, ret_payload);
    49.         *state = ret_payload[1];

    50.         return ret;
    51. }

    52. static int zynqmp_pm_pinctrl_get_function(const u32 pin, u32 *id)
    53. {
    54.         u32 ret_payload[PAYLOAD_ARG_CNT];
    55.         int ret;

    56.         if (!id)
    57.                 return -EINVAL;

    58.         ret = zynqmp_pm_invoke_fn(PM_PINCTRL_GET_FUNCTION, pin, 0,
    59.                                   0, 0, ret_payload);
    60.         *id = ret_payload[1];

    61.         return ret;
    62. }
    复制代码
    # 以上这些方法有个特点,全部调用了 zynqmp_pm_invoke_fn 函数实现的

    # zynqmp_pm_invoke_fn 函数调用了 do_fw_call 函数
    1. int zynqmp_pm_invoke_fn(u32 pm_api_id, u32 arg0, u32 arg1,
    2.                         u32 arg2, u32 arg3, u32 *ret_payload)
    3. {
    4.         /*
    5.          * Added SIP service call Function Identifier
    6.          * Make sure to stay in x0 register
    7.          */
    8.         u64 smc_arg[4];

    9.         if (zynqmp_pm_feature(pm_api_id) == PM_FEATURE_INVALID)
    10.                 return -ENOTSUPP;

    11.         smc_arg[0] = PM_SIP_SVC | pm_api_id;
    12.         smc_arg[1] = ((u64)arg1 << 32) | arg0;
    13.         smc_arg[2] = ((u64)arg3 << 32) | arg2;

    14.         return do_fw_call(smc_arg[0], smc_arg[1], smc_arg[2], ret_payload);
    15. }
    复制代码
    # do_fw_call 初始化的时候为 do_fw_call_fail,也就是没有初始化的话,调用它会报错
    1. static int (*do_fw_call)(u64, u64, u64, u32 *ret_payload) = do_fw_call_fail;

    2. static noinline int do_fw_call_fail(u64 arg0, u64 arg1, u64 arg2,
    3.                                     u32 *ret_payload)
    4. {
    5.         return -ENODEV;
    6. }
    复制代码
    看 probe 中的 get_set_conduit_method 函数,初始化了 do_fw_call = do_fw_call_smc
    1. static int get_set_conduit_method(struct device_node *np)
    2. {
    3.         const char *method;

    4.         if (of_property_read_string(np, "method", &method)) {
    5.                 pr_warn("%s missing "method" property\n", __func__);
    6.                 return -ENXIO;
    7.         }

    8.         if (!strcmp("hvc", method)) {
    9.                 do_fw_call = do_fw_call_hvc;
    10.         } else if (!strcmp("smc", method)) {
    11.                 do_fw_call = do_fw_call_smc;
    12.         } else {
    13.                 pr_warn("%s Invalid "method" property: %s\n",
    14.                         __func__, method);
    15.                 return -EINVAL;
    16.         }

    17.         return 0;
    18. }
    复制代码
    do_fw_call 就是被 zynqmp_pm_invoke_fn 调用,实现 firmware 所有的 ops
    1. int zynqmp_pm_invoke_fn(u32 pm_api_id, u32 arg0, u32 arg1,
    2.                         u32 arg2, u32 arg3, u32 *ret_payload)
    3. {
    4.         /*
    5.          * Added SIP service call Function Identifier
    6.          * Make sure to stay in x0 register
    7.          */
    8.         u64 smc_arg[4];

    9.         if (zynqmp_pm_feature(pm_api_id) == PM_FEATURE_INVALID)
    10.                 return -ENOTSUPP;

    11.         smc_arg[0] = PM_SIP_SVC | pm_api_id;
    12.         smc_arg[1] = ((u64)arg1 << 32) | arg0;
    13.         smc_arg[2] = ((u64)arg3 << 32) | arg2;

    14.         return do_fw_call(smc_arg[0], smc_arg[1], smc_arg[2], ret_payload);
    15. }
    复制代码
    结论:

    1,firmware 驱动方法包括 版本号、芯片ID、fpga 管理、clock 复位 电源 安全 加密 pinctrl efuse 等管理
    2,firmware 所有方法通过 smc #0 指令处理,处理完成返回结果和返回值。




    回复

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /1 下一条

    手机版|小黑屋|与非网

    GMT+8, 2024-6-3 08:48 , Processed in 0.110776 second(s), 16 queries , MemCache On.

    ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

    苏公网安备 32059002001037号

    Powered by Discuz! X3.4

    Copyright © 2001-2024, Tencent Cloud.