|
20 | 20 | #include <linux/rcupdate.h> |
21 | 21 | #include <linux/slab.h> |
22 | 22 | #include <linux/workqueue.h> |
| 23 | +#include <linux/firmware.h> |
23 | 24 | #include <asm/byteorder.h> |
24 | 25 | #include <net/devlink.h> |
25 | 26 | #include <trace/events/devlink.h> |
|
32 | 33 | #include "emad.h" |
33 | 34 | #include "reg.h" |
34 | 35 | #include "resources.h" |
| 36 | +#include "../mlxfw/mlxfw.h" |
35 | 37 |
|
36 | 38 | static LIST_HEAD(mlxsw_core_driver_list); |
37 | 39 | static DEFINE_SPINLOCK(mlxsw_core_driver_list_lock); |
@@ -864,6 +866,257 @@ static struct mlxsw_driver *mlxsw_core_driver_get(const char *kind) |
864 | 866 | return mlxsw_driver; |
865 | 867 | } |
866 | 868 |
|
| 869 | +struct mlxsw_core_fw_info { |
| 870 | + struct mlxfw_dev mlxfw_dev; |
| 871 | + struct mlxsw_core *mlxsw_core; |
| 872 | +}; |
| 873 | + |
| 874 | +static int mlxsw_core_fw_component_query(struct mlxfw_dev *mlxfw_dev, |
| 875 | + u16 component_index, u32 *p_max_size, |
| 876 | + u8 *p_align_bits, u16 *p_max_write_size) |
| 877 | +{ |
| 878 | + struct mlxsw_core_fw_info *mlxsw_core_fw_info = |
| 879 | + container_of(mlxfw_dev, struct mlxsw_core_fw_info, mlxfw_dev); |
| 880 | + struct mlxsw_core *mlxsw_core = mlxsw_core_fw_info->mlxsw_core; |
| 881 | + char mcqi_pl[MLXSW_REG_MCQI_LEN]; |
| 882 | + int err; |
| 883 | + |
| 884 | + mlxsw_reg_mcqi_pack(mcqi_pl, component_index); |
| 885 | + err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mcqi), mcqi_pl); |
| 886 | + if (err) |
| 887 | + return err; |
| 888 | + mlxsw_reg_mcqi_unpack(mcqi_pl, p_max_size, p_align_bits, p_max_write_size); |
| 889 | + |
| 890 | + *p_align_bits = max_t(u8, *p_align_bits, 2); |
| 891 | + *p_max_write_size = min_t(u16, *p_max_write_size, MLXSW_REG_MCDA_MAX_DATA_LEN); |
| 892 | + return 0; |
| 893 | +} |
| 894 | + |
| 895 | +static int mlxsw_core_fw_fsm_lock(struct mlxfw_dev *mlxfw_dev, u32 *fwhandle) |
| 896 | +{ |
| 897 | + struct mlxsw_core_fw_info *mlxsw_core_fw_info = |
| 898 | + container_of(mlxfw_dev, struct mlxsw_core_fw_info, mlxfw_dev); |
| 899 | + struct mlxsw_core *mlxsw_core = mlxsw_core_fw_info->mlxsw_core; |
| 900 | + char mcc_pl[MLXSW_REG_MCC_LEN]; |
| 901 | + u8 control_state; |
| 902 | + int err; |
| 903 | + |
| 904 | + mlxsw_reg_mcc_pack(mcc_pl, 0, 0, 0, 0); |
| 905 | + err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mcc), mcc_pl); |
| 906 | + if (err) |
| 907 | + return err; |
| 908 | + |
| 909 | + mlxsw_reg_mcc_unpack(mcc_pl, fwhandle, NULL, &control_state); |
| 910 | + if (control_state != MLXFW_FSM_STATE_IDLE) |
| 911 | + return -EBUSY; |
| 912 | + |
| 913 | + mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_LOCK_UPDATE_HANDLE, 0, *fwhandle, 0); |
| 914 | + return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mcc), mcc_pl); |
| 915 | +} |
| 916 | + |
| 917 | +static int mlxsw_core_fw_fsm_component_update(struct mlxfw_dev *mlxfw_dev, u32 fwhandle, |
| 918 | + u16 component_index, u32 component_size) |
| 919 | +{ |
| 920 | + struct mlxsw_core_fw_info *mlxsw_core_fw_info = |
| 921 | + container_of(mlxfw_dev, struct mlxsw_core_fw_info, mlxfw_dev); |
| 922 | + struct mlxsw_core *mlxsw_core = mlxsw_core_fw_info->mlxsw_core; |
| 923 | + char mcc_pl[MLXSW_REG_MCC_LEN]; |
| 924 | + |
| 925 | + mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_UPDATE_COMPONENT, |
| 926 | + component_index, fwhandle, component_size); |
| 927 | + return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mcc), mcc_pl); |
| 928 | +} |
| 929 | + |
| 930 | +static int mlxsw_core_fw_fsm_block_download(struct mlxfw_dev *mlxfw_dev, u32 fwhandle, |
| 931 | + u8 *data, u16 size, u32 offset) |
| 932 | +{ |
| 933 | + struct mlxsw_core_fw_info *mlxsw_core_fw_info = |
| 934 | + container_of(mlxfw_dev, struct mlxsw_core_fw_info, mlxfw_dev); |
| 935 | + struct mlxsw_core *mlxsw_core = mlxsw_core_fw_info->mlxsw_core; |
| 936 | + char mcda_pl[MLXSW_REG_MCDA_LEN]; |
| 937 | + |
| 938 | + mlxsw_reg_mcda_pack(mcda_pl, fwhandle, offset, size, data); |
| 939 | + return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mcda), mcda_pl); |
| 940 | +} |
| 941 | + |
| 942 | +static int mlxsw_core_fw_fsm_component_verify(struct mlxfw_dev *mlxfw_dev, u32 fwhandle, |
| 943 | + u16 component_index) |
| 944 | +{ |
| 945 | + struct mlxsw_core_fw_info *mlxsw_core_fw_info = |
| 946 | + container_of(mlxfw_dev, struct mlxsw_core_fw_info, mlxfw_dev); |
| 947 | + struct mlxsw_core *mlxsw_core = mlxsw_core_fw_info->mlxsw_core; |
| 948 | + char mcc_pl[MLXSW_REG_MCC_LEN]; |
| 949 | + |
| 950 | + mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_VERIFY_COMPONENT, |
| 951 | + component_index, fwhandle, 0); |
| 952 | + return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mcc), mcc_pl); |
| 953 | +} |
| 954 | + |
| 955 | +static int mlxsw_core_fw_fsm_activate(struct mlxfw_dev *mlxfw_dev, u32 fwhandle) |
| 956 | +{ |
| 957 | + struct mlxsw_core_fw_info *mlxsw_core_fw_info = |
| 958 | + container_of(mlxfw_dev, struct mlxsw_core_fw_info, mlxfw_dev); |
| 959 | + struct mlxsw_core *mlxsw_core = mlxsw_core_fw_info->mlxsw_core; |
| 960 | + char mcc_pl[MLXSW_REG_MCC_LEN]; |
| 961 | + |
| 962 | + mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_ACTIVATE, 0, fwhandle, 0); |
| 963 | + return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mcc), mcc_pl); |
| 964 | +} |
| 965 | + |
| 966 | +static int mlxsw_core_fw_fsm_query_state(struct mlxfw_dev *mlxfw_dev, u32 fwhandle, |
| 967 | + enum mlxfw_fsm_state *fsm_state, |
| 968 | + enum mlxfw_fsm_state_err *fsm_state_err) |
| 969 | +{ |
| 970 | + struct mlxsw_core_fw_info *mlxsw_core_fw_info = |
| 971 | + container_of(mlxfw_dev, struct mlxsw_core_fw_info, mlxfw_dev); |
| 972 | + struct mlxsw_core *mlxsw_core = mlxsw_core_fw_info->mlxsw_core; |
| 973 | + char mcc_pl[MLXSW_REG_MCC_LEN]; |
| 974 | + u8 control_state; |
| 975 | + u8 error_code; |
| 976 | + int err; |
| 977 | + |
| 978 | + mlxsw_reg_mcc_pack(mcc_pl, 0, 0, fwhandle, 0); |
| 979 | + err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mcc), mcc_pl); |
| 980 | + if (err) |
| 981 | + return err; |
| 982 | + |
| 983 | + mlxsw_reg_mcc_unpack(mcc_pl, NULL, &error_code, &control_state); |
| 984 | + *fsm_state = control_state; |
| 985 | + *fsm_state_err = min_t(enum mlxfw_fsm_state_err, error_code, MLXFW_FSM_STATE_ERR_MAX); |
| 986 | + return 0; |
| 987 | +} |
| 988 | + |
| 989 | +static void mlxsw_core_fw_fsm_cancel(struct mlxfw_dev *mlxfw_dev, u32 fwhandle) |
| 990 | +{ |
| 991 | + struct mlxsw_core_fw_info *mlxsw_core_fw_info = |
| 992 | + container_of(mlxfw_dev, struct mlxsw_core_fw_info, mlxfw_dev); |
| 993 | + struct mlxsw_core *mlxsw_core = mlxsw_core_fw_info->mlxsw_core; |
| 994 | + char mcc_pl[MLXSW_REG_MCC_LEN]; |
| 995 | + |
| 996 | + mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_CANCEL, 0, fwhandle, 0); |
| 997 | + mlxsw_reg_write(mlxsw_core, MLXSW_REG(mcc), mcc_pl); |
| 998 | +} |
| 999 | + |
| 1000 | +static void mlxsw_core_fw_fsm_release(struct mlxfw_dev *mlxfw_dev, u32 fwhandle) |
| 1001 | +{ |
| 1002 | + struct mlxsw_core_fw_info *mlxsw_core_fw_info = |
| 1003 | + container_of(mlxfw_dev, struct mlxsw_core_fw_info, mlxfw_dev); |
| 1004 | + struct mlxsw_core *mlxsw_core = mlxsw_core_fw_info->mlxsw_core; |
| 1005 | + char mcc_pl[MLXSW_REG_MCC_LEN]; |
| 1006 | + |
| 1007 | + mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_RELEASE_UPDATE_HANDLE, 0, fwhandle, 0); |
| 1008 | + mlxsw_reg_write(mlxsw_core, MLXSW_REG(mcc), mcc_pl); |
| 1009 | +} |
| 1010 | + |
| 1011 | +static const struct mlxfw_dev_ops mlxsw_core_fw_mlxsw_dev_ops = { |
| 1012 | + .component_query = mlxsw_core_fw_component_query, |
| 1013 | + .fsm_lock = mlxsw_core_fw_fsm_lock, |
| 1014 | + .fsm_component_update = mlxsw_core_fw_fsm_component_update, |
| 1015 | + .fsm_block_download = mlxsw_core_fw_fsm_block_download, |
| 1016 | + .fsm_component_verify = mlxsw_core_fw_fsm_component_verify, |
| 1017 | + .fsm_activate = mlxsw_core_fw_fsm_activate, |
| 1018 | + .fsm_query_state = mlxsw_core_fw_fsm_query_state, |
| 1019 | + .fsm_cancel = mlxsw_core_fw_fsm_cancel, |
| 1020 | + .fsm_release = mlxsw_core_fw_fsm_release, |
| 1021 | +}; |
| 1022 | + |
| 1023 | +static int mlxsw_core_fw_flash(struct mlxsw_core *mlxsw_core, const struct firmware *firmware, |
| 1024 | + struct netlink_ext_ack *extack) |
| 1025 | +{ |
| 1026 | + struct mlxsw_core_fw_info mlxsw_core_fw_info = { |
| 1027 | + .mlxfw_dev = { |
| 1028 | + .ops = &mlxsw_core_fw_mlxsw_dev_ops, |
| 1029 | + .psid = mlxsw_core->bus_info->psid, |
| 1030 | + .psid_size = strlen(mlxsw_core->bus_info->psid), |
| 1031 | + .devlink = priv_to_devlink(mlxsw_core), |
| 1032 | + }, |
| 1033 | + .mlxsw_core = mlxsw_core |
| 1034 | + }; |
| 1035 | + int err; |
| 1036 | + |
| 1037 | + mlxsw_core->fw_flash_in_progress = true; |
| 1038 | + err = mlxfw_firmware_flash(&mlxsw_core_fw_info.mlxfw_dev, firmware, extack); |
| 1039 | + mlxsw_core->fw_flash_in_progress = false; |
| 1040 | + |
| 1041 | + return err; |
| 1042 | +} |
| 1043 | + |
| 1044 | +static int mlxsw_core_fw_rev_validate(struct mlxsw_core *mlxsw_core, |
| 1045 | + const struct mlxsw_bus_info *mlxsw_bus_info, |
| 1046 | + const struct mlxsw_fw_rev *req_rev, |
| 1047 | + const char *filename) |
| 1048 | +{ |
| 1049 | + const struct mlxsw_fw_rev *rev = &mlxsw_bus_info->fw_rev; |
| 1050 | + union devlink_param_value value; |
| 1051 | + const struct firmware *firmware; |
| 1052 | + int err; |
| 1053 | + |
| 1054 | + /* Don't check if driver does not require it */ |
| 1055 | + if (!req_rev || !filename) |
| 1056 | + return 0; |
| 1057 | + |
| 1058 | + /* Don't check if devlink 'fw_load_policy' param is 'flash' */ |
| 1059 | + err = devlink_param_driverinit_value_get(priv_to_devlink(mlxsw_core), |
| 1060 | + DEVLINK_PARAM_GENERIC_ID_FW_LOAD_POLICY, |
| 1061 | + &value); |
| 1062 | + if (err) |
| 1063 | + return err; |
| 1064 | + if (value.vu8 == DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_FLASH) |
| 1065 | + return 0; |
| 1066 | + |
| 1067 | + /* Validate driver & FW are compatible */ |
| 1068 | + if (rev->major != req_rev->major) { |
| 1069 | + WARN(1, "Mismatch in major FW version [%d:%d] is never expected; Please contact support\n", |
| 1070 | + rev->major, req_rev->major); |
| 1071 | + return -EINVAL; |
| 1072 | + } |
| 1073 | + if (mlxsw_core_fw_rev_minor_subminor_validate(rev, req_rev)) |
| 1074 | + return 0; |
| 1075 | + |
| 1076 | + dev_err(mlxsw_bus_info->dev, "The firmware version %d.%d.%d is incompatible with the driver (required >= %d.%d.%d)\n", |
| 1077 | + rev->major, rev->minor, rev->subminor, req_rev->major, |
| 1078 | + req_rev->minor, req_rev->subminor); |
| 1079 | + dev_info(mlxsw_bus_info->dev, "Flashing firmware using file %s\n", filename); |
| 1080 | + |
| 1081 | + err = request_firmware_direct(&firmware, filename, mlxsw_bus_info->dev); |
| 1082 | + if (err) { |
| 1083 | + dev_err(mlxsw_bus_info->dev, "Could not request firmware file %s\n", filename); |
| 1084 | + return err; |
| 1085 | + } |
| 1086 | + |
| 1087 | + err = mlxsw_core_fw_flash(mlxsw_core, firmware, NULL); |
| 1088 | + release_firmware(firmware); |
| 1089 | + if (err) |
| 1090 | + dev_err(mlxsw_bus_info->dev, "Could not upgrade firmware\n"); |
| 1091 | + |
| 1092 | + /* On FW flash success, tell the caller FW reset is needed |
| 1093 | + * if current FW supports it. |
| 1094 | + */ |
| 1095 | + if (rev->minor >= req_rev->can_reset_minor) |
| 1096 | + return err ? err : -EAGAIN; |
| 1097 | + else |
| 1098 | + return 0; |
| 1099 | +} |
| 1100 | + |
| 1101 | +static int mlxsw_core_fw_flash_update(struct mlxsw_core *mlxsw_core, |
| 1102 | + const char *file_name, const char *component, |
| 1103 | + struct netlink_ext_ack *extack) |
| 1104 | +{ |
| 1105 | + const struct firmware *firmware; |
| 1106 | + int err; |
| 1107 | + |
| 1108 | + if (component) |
| 1109 | + return -EOPNOTSUPP; |
| 1110 | + |
| 1111 | + err = request_firmware_direct(&firmware, file_name, mlxsw_core->bus_info->dev); |
| 1112 | + if (err) |
| 1113 | + return err; |
| 1114 | + err = mlxsw_core_fw_flash(mlxsw_core, firmware, extack); |
| 1115 | + release_firmware(firmware); |
| 1116 | + |
| 1117 | + return err; |
| 1118 | +} |
| 1119 | + |
867 | 1120 | static int mlxsw_devlink_port_split(struct devlink *devlink, |
868 | 1121 | unsigned int port_index, |
869 | 1122 | unsigned int count, |
@@ -1143,12 +1396,8 @@ static int mlxsw_devlink_flash_update(struct devlink *devlink, |
1143 | 1396 | struct netlink_ext_ack *extack) |
1144 | 1397 | { |
1145 | 1398 | struct mlxsw_core *mlxsw_core = devlink_priv(devlink); |
1146 | | - struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver; |
1147 | 1399 |
|
1148 | | - if (!mlxsw_driver->flash_update) |
1149 | | - return -EOPNOTSUPP; |
1150 | | - return mlxsw_driver->flash_update(mlxsw_core, file_name, |
1151 | | - component, extack); |
| 1400 | + return mlxsw_core_fw_flash_update(mlxsw_core, file_name, component, extack); |
1152 | 1401 | } |
1153 | 1402 |
|
1154 | 1403 | static int mlxsw_devlink_trap_init(struct devlink *devlink, |
@@ -1374,6 +1623,11 @@ __mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info, |
1374 | 1623 | goto err_register_params; |
1375 | 1624 | } |
1376 | 1625 |
|
| 1626 | + err = mlxsw_core_fw_rev_validate(mlxsw_core, mlxsw_bus_info, mlxsw_driver->fw_req_rev, |
| 1627 | + mlxsw_driver->fw_filename); |
| 1628 | + if (err) |
| 1629 | + goto err_fw_rev_validate; |
| 1630 | + |
1377 | 1631 | if (mlxsw_driver->init) { |
1378 | 1632 | err = mlxsw_driver->init(mlxsw_core, mlxsw_bus_info, extack); |
1379 | 1633 | if (err) |
@@ -1403,6 +1657,7 @@ __mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info, |
1403 | 1657 | if (mlxsw_core->driver->fini) |
1404 | 1658 | mlxsw_core->driver->fini(mlxsw_core); |
1405 | 1659 | err_driver_init: |
| 1660 | +err_fw_rev_validate: |
1406 | 1661 | if (mlxsw_driver->params_unregister && !reload) |
1407 | 1662 | mlxsw_driver->params_unregister(mlxsw_core); |
1408 | 1663 | err_register_params: |
@@ -2410,18 +2665,6 @@ int mlxsw_core_kvd_sizes_get(struct mlxsw_core *mlxsw_core, |
2410 | 2665 | } |
2411 | 2666 | EXPORT_SYMBOL(mlxsw_core_kvd_sizes_get); |
2412 | 2667 |
|
2413 | | -void mlxsw_core_fw_flash_start(struct mlxsw_core *mlxsw_core) |
2414 | | -{ |
2415 | | - mlxsw_core->fw_flash_in_progress = true; |
2416 | | -} |
2417 | | -EXPORT_SYMBOL(mlxsw_core_fw_flash_start); |
2418 | | - |
2419 | | -void mlxsw_core_fw_flash_end(struct mlxsw_core *mlxsw_core) |
2420 | | -{ |
2421 | | - mlxsw_core->fw_flash_in_progress = false; |
2422 | | -} |
2423 | | -EXPORT_SYMBOL(mlxsw_core_fw_flash_end); |
2424 | | - |
2425 | 2668 | int mlxsw_core_resources_query(struct mlxsw_core *mlxsw_core, char *mbox, |
2426 | 2669 | struct mlxsw_res *res) |
2427 | 2670 | { |
|
0 commit comments