mlxsw: core_linecards: Implement line card device flashing
authorJiri Pirko <jiri@nvidia.com>
Mon, 25 Jul 2022 08:29:23 +0000 (10:29 +0200)
committerJakub Kicinski <kuba@kernel.org>
Tue, 26 Jul 2022 20:56:44 +0000 (13:56 -0700)
Implement flash_update() devlink op for the line card devlink instance
to allow user to update line card gearbox FW using MDDT register
and mlxfw.

Example:
$ devlink dev flash auxiliary/mlxsw_core.lc.0 file mellanox/fw-AGB-rel-19_2010_1312-022-EVB.mfa2

Signed-off-by: Jiri Pirko <jiri@nvidia.com>
Reviewed-by: Ido Schimmel <idosch@nvidia.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/mellanox/mlxsw/core.c
drivers/net/ethernet/mellanox/mlxsw/core.h
drivers/net/ethernet/mellanox/mlxsw/core_linecard_dev.c
drivers/net/ethernet/mellanox/mlxsw/core_linecards.c

index 1c8b72faf9e2118006294cf748dd943b2f56d6cf..a48f893cf7b01c7d9589f70c62c06a0e55212741 100644 (file)
@@ -951,6 +951,20 @@ static struct mlxsw_driver *mlxsw_core_driver_get(const char *kind)
        return mlxsw_driver;
 }
 
+int mlxsw_core_fw_flash(struct mlxsw_core *mlxsw_core,
+                       struct mlxfw_dev *mlxfw_dev,
+                       const struct firmware *firmware,
+                       struct netlink_ext_ack *extack)
+{
+       int err;
+
+       mlxsw_core->fw_flash_in_progress = true;
+       err = mlxfw_firmware_flash(mlxfw_dev, firmware, extack);
+       mlxsw_core->fw_flash_in_progress = false;
+
+       return err;
+}
+
 struct mlxsw_core_fw_info {
        struct mlxfw_dev mlxfw_dev;
        struct mlxsw_core *mlxsw_core;
@@ -1105,8 +1119,9 @@ static const struct mlxfw_dev_ops mlxsw_core_fw_mlxsw_dev_ops = {
        .fsm_release            = mlxsw_core_fw_fsm_release,
 };
 
-static int mlxsw_core_fw_flash(struct mlxsw_core *mlxsw_core, const struct firmware *firmware,
-                              struct netlink_ext_ack *extack)
+static int mlxsw_core_dev_fw_flash(struct mlxsw_core *mlxsw_core,
+                                  const struct firmware *firmware,
+                                  struct netlink_ext_ack *extack)
 {
        struct mlxsw_core_fw_info mlxsw_core_fw_info = {
                .mlxfw_dev = {
@@ -1117,13 +1132,9 @@ static int mlxsw_core_fw_flash(struct mlxsw_core *mlxsw_core, const struct firmw
                },
                .mlxsw_core = mlxsw_core
        };
-       int err;
 
-       mlxsw_core->fw_flash_in_progress = true;
-       err = mlxfw_firmware_flash(&mlxsw_core_fw_info.mlxfw_dev, firmware, extack);
-       mlxsw_core->fw_flash_in_progress = false;
-
-       return err;
+       return mlxsw_core_fw_flash(mlxsw_core, &mlxsw_core_fw_info.mlxfw_dev,
+                                  firmware, extack);
 }
 
 static int mlxsw_core_fw_rev_validate(struct mlxsw_core *mlxsw_core,
@@ -1169,7 +1180,7 @@ static int mlxsw_core_fw_rev_validate(struct mlxsw_core *mlxsw_core,
                return err;
        }
 
-       err = mlxsw_core_fw_flash(mlxsw_core, firmware, NULL);
+       err = mlxsw_core_dev_fw_flash(mlxsw_core, firmware, NULL);
        release_firmware(firmware);
        if (err)
                dev_err(mlxsw_bus_info->dev, "Could not upgrade firmware\n");
@@ -1187,7 +1198,7 @@ static int mlxsw_core_fw_flash_update(struct mlxsw_core *mlxsw_core,
                                      struct devlink_flash_update_params *params,
                                      struct netlink_ext_ack *extack)
 {
-       return mlxsw_core_fw_flash(mlxsw_core, params->fw, extack);
+       return mlxsw_core_dev_fw_flash(mlxsw_core, params->fw, extack);
 }
 
 static int mlxsw_core_devlink_param_fw_load_policy_validate(struct devlink *devlink, u32 id,
index 57849888139f6d421e3c1447c6c4f90c809a25b0..7213e452829811af6c25116da9f9ef46f047fae5 100644 (file)
@@ -19,6 +19,7 @@
 #include "reg.h"
 #include "cmd.h"
 #include "resources.h"
+#include "../mlxfw/mlxfw.h"
 
 enum mlxsw_core_resource_id {
        MLXSW_CORE_RESOURCE_PORTS = 1,
@@ -48,6 +49,11 @@ mlxsw_core_fw_rev_minor_subminor_validate(const struct mlxsw_fw_rev *rev,
 int mlxsw_core_driver_register(struct mlxsw_driver *mlxsw_driver);
 void mlxsw_core_driver_unregister(struct mlxsw_driver *mlxsw_driver);
 
+int mlxsw_core_fw_flash(struct mlxsw_core *mlxsw_core,
+                       struct mlxfw_dev *mlxfw_dev,
+                       const struct firmware *firmware,
+                       struct netlink_ext_ack *extack);
+
 int mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info,
                                   const struct mlxsw_bus *mlxsw_bus,
                                   void *bus_priv, bool reload,
@@ -590,6 +596,7 @@ struct mlxsw_linecard {
        struct mlxsw_linecard_bdev *bdev;
        struct {
                struct mlxsw_linecard_device_info info;
+               u8 index;
        } device;
 };
 
@@ -614,6 +621,10 @@ mlxsw_linecard_get(struct mlxsw_linecards *linecards, u8 slot_index)
 int mlxsw_linecard_devlink_info_get(struct mlxsw_linecard *linecard,
                                    struct devlink_info_req *req,
                                    struct netlink_ext_ack *extack);
+int mlxsw_linecard_flash_update(struct devlink *linecard_devlink,
+                               struct mlxsw_linecard *linecard,
+                               const struct firmware *firmware,
+                               struct netlink_ext_ack *extack);
 
 int mlxsw_linecards_init(struct mlxsw_core *mlxsw_core,
                         const struct mlxsw_bus_info *bus_info);
index 13c20b83b19014538c9f2503672f6ac3bad04fc4..49fee038a99c7bbcb89af6b8c3ece00f0be8972a 100644 (file)
@@ -108,8 +108,21 @@ static int mlxsw_linecard_dev_devlink_info_get(struct devlink *devlink,
        return mlxsw_linecard_devlink_info_get(linecard, req, extack);
 }
 
+static int
+mlxsw_linecard_dev_devlink_flash_update(struct devlink *devlink,
+                                       struct devlink_flash_update_params *params,
+                                       struct netlink_ext_ack *extack)
+{
+       struct mlxsw_linecard_dev *linecard_dev = devlink_priv(devlink);
+       struct mlxsw_linecard *linecard = linecard_dev->linecard;
+
+       return mlxsw_linecard_flash_update(devlink, linecard,
+                                          params->fw, extack);
+}
+
 static const struct devlink_ops mlxsw_linecard_dev_devlink_ops = {
        .info_get                       = mlxsw_linecard_dev_devlink_info_get,
+       .flash_update                   = mlxsw_linecard_dev_devlink_flash_update,
 };
 
 static int mlxsw_linecard_bdev_probe(struct auxiliary_device *adev,
index 771a3e43b8bb04341e5871fa3ee73e8242512c79..ca59f0b946da96f89e1c7bb905ecc9b6b83c6a9b 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/vmalloc.h>
 
 #include "core.h"
+#include "../mlxfw/mlxfw.h"
 
 struct mlxsw_linecard_ini_file {
        __le16 size;
@@ -87,6 +88,282 @@ static const char *mlxsw_linecard_type_name(struct mlxsw_linecard *linecard)
        return linecard->name;
 }
 
+struct mlxsw_linecard_device_fw_info {
+       struct mlxfw_dev mlxfw_dev;
+       struct mlxsw_core *mlxsw_core;
+       struct mlxsw_linecard *linecard;
+};
+
+static int mlxsw_linecard_device_fw_component_query(struct mlxfw_dev *mlxfw_dev,
+                                                   u16 component_index,
+                                                   u32 *p_max_size,
+                                                   u8 *p_align_bits,
+                                                   u16 *p_max_write_size)
+{
+       struct mlxsw_linecard_device_fw_info *info =
+               container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,
+                            mlxfw_dev);
+       struct mlxsw_linecard *linecard = info->linecard;
+       struct mlxsw_core *mlxsw_core = info->mlxsw_core;
+       char mddt_pl[MLXSW_REG_MDDT_LEN];
+       char *mcqi_pl;
+       int err;
+
+       mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
+                           linecard->device.index,
+                           MLXSW_REG_MDDT_METHOD_QUERY,
+                           MLXSW_REG(mcqi), &mcqi_pl);
+
+       mlxsw_reg_mcqi_pack(mcqi_pl, component_index);
+       err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
+       if (err)
+               return err;
+       mlxsw_reg_mcqi_unpack(mcqi_pl, p_max_size, p_align_bits,
+                             p_max_write_size);
+
+       *p_align_bits = max_t(u8, *p_align_bits, 2);
+       *p_max_write_size = min_t(u16, *p_max_write_size,
+                                 MLXSW_REG_MCDA_MAX_DATA_LEN);
+       return 0;
+}
+
+static int mlxsw_linecard_device_fw_fsm_lock(struct mlxfw_dev *mlxfw_dev,
+                                            u32 *fwhandle)
+{
+       struct mlxsw_linecard_device_fw_info *info =
+               container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,
+                            mlxfw_dev);
+       struct mlxsw_linecard *linecard = info->linecard;
+       struct mlxsw_core *mlxsw_core = info->mlxsw_core;
+       char mddt_pl[MLXSW_REG_MDDT_LEN];
+       u8 control_state;
+       char *mcc_pl;
+       int err;
+
+       mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
+                           linecard->device.index,
+                           MLXSW_REG_MDDT_METHOD_QUERY,
+                           MLXSW_REG(mcc), &mcc_pl);
+       mlxsw_reg_mcc_pack(mcc_pl, 0, 0, 0, 0);
+       err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
+       if (err)
+               return err;
+
+       mlxsw_reg_mcc_unpack(mcc_pl, fwhandle, NULL, &control_state);
+       if (control_state != MLXFW_FSM_STATE_IDLE)
+               return -EBUSY;
+
+       mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
+                           linecard->device.index,
+                           MLXSW_REG_MDDT_METHOD_WRITE,
+                           MLXSW_REG(mcc), &mcc_pl);
+       mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_LOCK_UPDATE_HANDLE,
+                          0, *fwhandle, 0);
+       return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
+}
+
+static int
+mlxsw_linecard_device_fw_fsm_component_update(struct mlxfw_dev *mlxfw_dev,
+                                             u32 fwhandle,
+                                             u16 component_index,
+                                             u32 component_size)
+{
+       struct mlxsw_linecard_device_fw_info *info =
+               container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,
+                            mlxfw_dev);
+       struct mlxsw_linecard *linecard = info->linecard;
+       struct mlxsw_core *mlxsw_core = info->mlxsw_core;
+       char mddt_pl[MLXSW_REG_MDDT_LEN];
+       char *mcc_pl;
+
+       mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
+                           linecard->device.index,
+                           MLXSW_REG_MDDT_METHOD_WRITE,
+                           MLXSW_REG(mcc), &mcc_pl);
+       mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_UPDATE_COMPONENT,
+                          component_index, fwhandle, component_size);
+       return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
+}
+
+static int
+mlxsw_linecard_device_fw_fsm_block_download(struct mlxfw_dev *mlxfw_dev,
+                                           u32 fwhandle, u8 *data,
+                                           u16 size, u32 offset)
+{
+       struct mlxsw_linecard_device_fw_info *info =
+               container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,
+                            mlxfw_dev);
+       struct mlxsw_linecard *linecard = info->linecard;
+       struct mlxsw_core *mlxsw_core = info->mlxsw_core;
+       char mddt_pl[MLXSW_REG_MDDT_LEN];
+       char *mcda_pl;
+
+       mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
+                           linecard->device.index,
+                           MLXSW_REG_MDDT_METHOD_WRITE,
+                           MLXSW_REG(mcda), &mcda_pl);
+       mlxsw_reg_mcda_pack(mcda_pl, fwhandle, offset, size, data);
+       return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
+}
+
+static int
+mlxsw_linecard_device_fw_fsm_component_verify(struct mlxfw_dev *mlxfw_dev,
+                                             u32 fwhandle, u16 component_index)
+{
+       struct mlxsw_linecard_device_fw_info *info =
+               container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,
+                            mlxfw_dev);
+       struct mlxsw_linecard *linecard = info->linecard;
+       struct mlxsw_core *mlxsw_core = info->mlxsw_core;
+       char mddt_pl[MLXSW_REG_MDDT_LEN];
+       char *mcc_pl;
+
+       mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
+                           linecard->device.index,
+                           MLXSW_REG_MDDT_METHOD_WRITE,
+                           MLXSW_REG(mcc), &mcc_pl);
+       mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_VERIFY_COMPONENT,
+                          component_index, fwhandle, 0);
+       return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
+}
+
+static int mlxsw_linecard_device_fw_fsm_activate(struct mlxfw_dev *mlxfw_dev,
+                                                u32 fwhandle)
+{
+       struct mlxsw_linecard_device_fw_info *info =
+               container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,
+                            mlxfw_dev);
+       struct mlxsw_linecard *linecard = info->linecard;
+       struct mlxsw_core *mlxsw_core = info->mlxsw_core;
+       char mddt_pl[MLXSW_REG_MDDT_LEN];
+       char *mcc_pl;
+
+       mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
+                           linecard->device.index,
+                           MLXSW_REG_MDDT_METHOD_WRITE,
+                           MLXSW_REG(mcc), &mcc_pl);
+       mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_ACTIVATE,
+                          0, fwhandle, 0);
+       return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
+}
+
+static int
+mlxsw_linecard_device_fw_fsm_query_state(struct mlxfw_dev *mlxfw_dev,
+                                        u32 fwhandle,
+                                        enum mlxfw_fsm_state *fsm_state,
+                                        enum mlxfw_fsm_state_err *fsm_state_err)
+{
+       struct mlxsw_linecard_device_fw_info *info =
+               container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,
+                            mlxfw_dev);
+       struct mlxsw_linecard *linecard = info->linecard;
+       struct mlxsw_core *mlxsw_core = info->mlxsw_core;
+       char mddt_pl[MLXSW_REG_MDDT_LEN];
+       u8 control_state;
+       u8 error_code;
+       char *mcc_pl;
+       int err;
+
+       mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
+                           linecard->device.index,
+                           MLXSW_REG_MDDT_METHOD_QUERY,
+                           MLXSW_REG(mcc), &mcc_pl);
+       mlxsw_reg_mcc_pack(mcc_pl, 0, 0, fwhandle, 0);
+       err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
+       if (err)
+               return err;
+
+       mlxsw_reg_mcc_unpack(mcc_pl, NULL, &error_code, &control_state);
+       *fsm_state = control_state;
+       *fsm_state_err = min_t(enum mlxfw_fsm_state_err, error_code,
+                              MLXFW_FSM_STATE_ERR_MAX);
+       return 0;
+}
+
+static void mlxsw_linecard_device_fw_fsm_cancel(struct mlxfw_dev *mlxfw_dev,
+                                               u32 fwhandle)
+{
+       struct mlxsw_linecard_device_fw_info *info =
+               container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,
+                            mlxfw_dev);
+       struct mlxsw_linecard *linecard = info->linecard;
+       struct mlxsw_core *mlxsw_core = info->mlxsw_core;
+       char mddt_pl[MLXSW_REG_MDDT_LEN];
+       char *mcc_pl;
+
+       mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
+                           linecard->device.index,
+                           MLXSW_REG_MDDT_METHOD_WRITE,
+                           MLXSW_REG(mcc), &mcc_pl);
+       mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_CANCEL,
+                          0, fwhandle, 0);
+       mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
+}
+
+static void mlxsw_linecard_device_fw_fsm_release(struct mlxfw_dev *mlxfw_dev,
+                                                u32 fwhandle)
+{
+       struct mlxsw_linecard_device_fw_info *info =
+               container_of(mlxfw_dev, struct mlxsw_linecard_device_fw_info,
+                            mlxfw_dev);
+       struct mlxsw_linecard *linecard = info->linecard;
+       struct mlxsw_core *mlxsw_core = info->mlxsw_core;
+       char mddt_pl[MLXSW_REG_MDDT_LEN];
+       char *mcc_pl;
+
+       mlxsw_reg_mddt_pack(mddt_pl, linecard->slot_index,
+                           linecard->device.index,
+                           MLXSW_REG_MDDT_METHOD_WRITE,
+                           MLXSW_REG(mcc), &mcc_pl);
+       mlxsw_reg_mcc_pack(mcc_pl,
+                          MLXSW_REG_MCC_INSTRUCTION_RELEASE_UPDATE_HANDLE,
+                          0, fwhandle, 0);
+       mlxsw_reg_write(mlxsw_core, MLXSW_REG(mddt), mddt_pl);
+}
+
+static const struct mlxfw_dev_ops mlxsw_linecard_device_dev_ops = {
+       .component_query        = mlxsw_linecard_device_fw_component_query,
+       .fsm_lock               = mlxsw_linecard_device_fw_fsm_lock,
+       .fsm_component_update   = mlxsw_linecard_device_fw_fsm_component_update,
+       .fsm_block_download     = mlxsw_linecard_device_fw_fsm_block_download,
+       .fsm_component_verify   = mlxsw_linecard_device_fw_fsm_component_verify,
+       .fsm_activate           = mlxsw_linecard_device_fw_fsm_activate,
+       .fsm_query_state        = mlxsw_linecard_device_fw_fsm_query_state,
+       .fsm_cancel             = mlxsw_linecard_device_fw_fsm_cancel,
+       .fsm_release            = mlxsw_linecard_device_fw_fsm_release,
+};
+
+int mlxsw_linecard_flash_update(struct devlink *linecard_devlink,
+                               struct mlxsw_linecard *linecard,
+                               const struct firmware *firmware,
+                               struct netlink_ext_ack *extack)
+{
+       struct mlxsw_core *mlxsw_core = linecard->linecards->mlxsw_core;
+       struct mlxsw_linecard_device_fw_info info = {
+               .mlxfw_dev = {
+                       .ops = &mlxsw_linecard_device_dev_ops,
+                       .psid = linecard->device.info.psid,
+                       .psid_size = strlen(linecard->device.info.psid),
+                       .devlink = linecard_devlink,
+               },
+               .mlxsw_core = mlxsw_core,
+               .linecard = linecard,
+       };
+       int err;
+
+       mutex_lock(&linecard->lock);
+       if (!linecard->active) {
+               NL_SET_ERR_MSG_MOD(extack, "Only active line cards can be flashed");
+               err = -EINVAL;
+               goto unlock;
+       }
+       err = mlxsw_core_fw_flash(mlxsw_core, &info.mlxfw_dev,
+                                 firmware, extack);
+unlock:
+       mutex_unlock(&linecard->lock);
+       return err;
+}
+
 static int mlxsw_linecard_device_psid_get(struct mlxsw_linecard *linecard,
                                          u8 device_index, char *psid)
 {
@@ -149,6 +426,7 @@ static int mlxsw_linecard_device_info_update(struct mlxsw_linecard *linecard)
                        return err;
 
                linecard->device.info = info;
+               linecard->device.index = device_index;
                flashable_found = true;
        } while (msg_seq);