mlxsw: core_linecards: Implement line card device flashing

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>
This commit is contained in:
Jiri Pirko 2022-07-25 10:29:23 +02:00 committed by Jakub Kicinski
parent 3fc0c51905
commit 9ca6a7a5f4
4 changed files with 323 additions and 10 deletions

View File

@ -951,6 +951,20 @@ static struct mlxsw_driver *mlxsw_core_driver_get(const char *kind)
return mlxsw_driver; 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 mlxsw_core_fw_info {
struct mlxfw_dev mlxfw_dev; struct mlxfw_dev mlxfw_dev;
struct mlxsw_core *mlxsw_core; 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, .fsm_release = mlxsw_core_fw_fsm_release,
}; };
static int mlxsw_core_fw_flash(struct mlxsw_core *mlxsw_core, const struct firmware *firmware, static int mlxsw_core_dev_fw_flash(struct mlxsw_core *mlxsw_core,
struct netlink_ext_ack *extack) const struct firmware *firmware,
struct netlink_ext_ack *extack)
{ {
struct mlxsw_core_fw_info mlxsw_core_fw_info = { struct mlxsw_core_fw_info mlxsw_core_fw_info = {
.mlxfw_dev = { .mlxfw_dev = {
@ -1117,13 +1132,9 @@ static int mlxsw_core_fw_flash(struct mlxsw_core *mlxsw_core, const struct firmw
}, },
.mlxsw_core = mlxsw_core .mlxsw_core = mlxsw_core
}; };
int err;
mlxsw_core->fw_flash_in_progress = true; return mlxsw_core_fw_flash(mlxsw_core, &mlxsw_core_fw_info.mlxfw_dev,
err = mlxfw_firmware_flash(&mlxsw_core_fw_info.mlxfw_dev, firmware, extack); firmware, extack);
mlxsw_core->fw_flash_in_progress = false;
return err;
} }
static int mlxsw_core_fw_rev_validate(struct mlxsw_core *mlxsw_core, 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; return err;
} }
err = mlxsw_core_fw_flash(mlxsw_core, firmware, NULL); err = mlxsw_core_dev_fw_flash(mlxsw_core, firmware, NULL);
release_firmware(firmware); release_firmware(firmware);
if (err) if (err)
dev_err(mlxsw_bus_info->dev, "Could not upgrade firmware\n"); 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 devlink_flash_update_params *params,
struct netlink_ext_ack *extack) 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, static int mlxsw_core_devlink_param_fw_load_policy_validate(struct devlink *devlink, u32 id,

View File

@ -19,6 +19,7 @@
#include "reg.h" #include "reg.h"
#include "cmd.h" #include "cmd.h"
#include "resources.h" #include "resources.h"
#include "../mlxfw/mlxfw.h"
enum mlxsw_core_resource_id { enum mlxsw_core_resource_id {
MLXSW_CORE_RESOURCE_PORTS = 1, 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); int mlxsw_core_driver_register(struct mlxsw_driver *mlxsw_driver);
void mlxsw_core_driver_unregister(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, int mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info,
const struct mlxsw_bus *mlxsw_bus, const struct mlxsw_bus *mlxsw_bus,
void *bus_priv, bool reload, void *bus_priv, bool reload,
@ -590,6 +596,7 @@ struct mlxsw_linecard {
struct mlxsw_linecard_bdev *bdev; struct mlxsw_linecard_bdev *bdev;
struct { struct {
struct mlxsw_linecard_device_info info; struct mlxsw_linecard_device_info info;
u8 index;
} device; } 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, int mlxsw_linecard_devlink_info_get(struct mlxsw_linecard *linecard,
struct devlink_info_req *req, struct devlink_info_req *req,
struct netlink_ext_ack *extack); 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, int mlxsw_linecards_init(struct mlxsw_core *mlxsw_core,
const struct mlxsw_bus_info *bus_info); const struct mlxsw_bus_info *bus_info);

View 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); 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 = { static const struct devlink_ops mlxsw_linecard_dev_devlink_ops = {
.info_get = mlxsw_linecard_dev_devlink_info_get, .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, static int mlxsw_linecard_bdev_probe(struct auxiliary_device *adev,

View File

@ -13,6 +13,7 @@
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include "core.h" #include "core.h"
#include "../mlxfw/mlxfw.h"
struct mlxsw_linecard_ini_file { struct mlxsw_linecard_ini_file {
__le16 size; __le16 size;
@ -87,6 +88,282 @@ static const char *mlxsw_linecard_type_name(struct mlxsw_linecard *linecard)
return linecard->name; 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, static int mlxsw_linecard_device_psid_get(struct mlxsw_linecard *linecard,
u8 device_index, char *psid) u8 device_index, char *psid)
{ {
@ -149,6 +426,7 @@ static int mlxsw_linecard_device_info_update(struct mlxsw_linecard *linecard)
return err; return err;
linecard->device.info = info; linecard->device.info = info;
linecard->device.index = device_index;
flashable_found = true; flashable_found = true;
} while (msg_seq); } while (msg_seq);