Skip to content

Commit

Permalink
mlxsw: Move fw flashing code into core.c
Browse files Browse the repository at this point in the history
As the firmware flashing is not specific to Spectrum, move the code to
core.c and avoid one op call and 2 exported symbols. Also, this allows
to do flash before call of driver->init function and possibly do other
core calls in between.

Do some small renaming here and there on the way to be consistent with
the rest of core.c code.

Signed-off-by: Jiri Pirko <jiri@nvidia.com>
Signed-off-by: Ido Schimmel <idosch@nvidia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Jiri Pirko authored and davem330 committed Sep 15, 2020
1 parent eab1924 commit b79cb78
Show file tree
Hide file tree
Showing 4 changed files with 268 additions and 307 deletions.
277 changes: 260 additions & 17 deletions drivers/net/ethernet/mellanox/mlxsw/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <linux/rcupdate.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/firmware.h>
#include <asm/byteorder.h>
#include <net/devlink.h>
#include <trace/events/devlink.h>
Expand All @@ -32,6 +33,7 @@
#include "emad.h"
#include "reg.h"
#include "resources.h"
#include "../mlxfw/mlxfw.h"

static LIST_HEAD(mlxsw_core_driver_list);
static DEFINE_SPINLOCK(mlxsw_core_driver_list_lock);
Expand Down Expand Up @@ -864,6 +866,257 @@ static struct mlxsw_driver *mlxsw_core_driver_get(const char *kind)
return mlxsw_driver;
}

struct mlxsw_core_fw_info {
struct mlxfw_dev mlxfw_dev;
struct mlxsw_core *mlxsw_core;
};

static int mlxsw_core_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_core_fw_info *mlxsw_core_fw_info =
container_of(mlxfw_dev, struct mlxsw_core_fw_info, mlxfw_dev);
struct mlxsw_core *mlxsw_core = mlxsw_core_fw_info->mlxsw_core;
char mcqi_pl[MLXSW_REG_MCQI_LEN];
int err;

mlxsw_reg_mcqi_pack(mcqi_pl, component_index);
err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mcqi), mcqi_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_core_fw_fsm_lock(struct mlxfw_dev *mlxfw_dev, u32 *fwhandle)
{
struct mlxsw_core_fw_info *mlxsw_core_fw_info =
container_of(mlxfw_dev, struct mlxsw_core_fw_info, mlxfw_dev);
struct mlxsw_core *mlxsw_core = mlxsw_core_fw_info->mlxsw_core;
char mcc_pl[MLXSW_REG_MCC_LEN];
u8 control_state;
int err;

mlxsw_reg_mcc_pack(mcc_pl, 0, 0, 0, 0);
err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mcc), mcc_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_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_LOCK_UPDATE_HANDLE, 0, *fwhandle, 0);
return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mcc), mcc_pl);
}

static int mlxsw_core_fw_fsm_component_update(struct mlxfw_dev *mlxfw_dev, u32 fwhandle,
u16 component_index, u32 component_size)
{
struct mlxsw_core_fw_info *mlxsw_core_fw_info =
container_of(mlxfw_dev, struct mlxsw_core_fw_info, mlxfw_dev);
struct mlxsw_core *mlxsw_core = mlxsw_core_fw_info->mlxsw_core;
char mcc_pl[MLXSW_REG_MCC_LEN];

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(mcc), mcc_pl);
}

static int mlxsw_core_fw_fsm_block_download(struct mlxfw_dev *mlxfw_dev, u32 fwhandle,
u8 *data, u16 size, u32 offset)
{
struct mlxsw_core_fw_info *mlxsw_core_fw_info =
container_of(mlxfw_dev, struct mlxsw_core_fw_info, mlxfw_dev);
struct mlxsw_core *mlxsw_core = mlxsw_core_fw_info->mlxsw_core;
char mcda_pl[MLXSW_REG_MCDA_LEN];

mlxsw_reg_mcda_pack(mcda_pl, fwhandle, offset, size, data);
return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mcda), mcda_pl);
}

static int mlxsw_core_fw_fsm_component_verify(struct mlxfw_dev *mlxfw_dev, u32 fwhandle,
u16 component_index)
{
struct mlxsw_core_fw_info *mlxsw_core_fw_info =
container_of(mlxfw_dev, struct mlxsw_core_fw_info, mlxfw_dev);
struct mlxsw_core *mlxsw_core = mlxsw_core_fw_info->mlxsw_core;
char mcc_pl[MLXSW_REG_MCC_LEN];

mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_VERIFY_COMPONENT,
component_index, fwhandle, 0);
return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mcc), mcc_pl);
}

static int mlxsw_core_fw_fsm_activate(struct mlxfw_dev *mlxfw_dev, u32 fwhandle)
{
struct mlxsw_core_fw_info *mlxsw_core_fw_info =
container_of(mlxfw_dev, struct mlxsw_core_fw_info, mlxfw_dev);
struct mlxsw_core *mlxsw_core = mlxsw_core_fw_info->mlxsw_core;
char mcc_pl[MLXSW_REG_MCC_LEN];

mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_ACTIVATE, 0, fwhandle, 0);
return mlxsw_reg_write(mlxsw_core, MLXSW_REG(mcc), mcc_pl);
}

static int mlxsw_core_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_core_fw_info *mlxsw_core_fw_info =
container_of(mlxfw_dev, struct mlxsw_core_fw_info, mlxfw_dev);
struct mlxsw_core *mlxsw_core = mlxsw_core_fw_info->mlxsw_core;
char mcc_pl[MLXSW_REG_MCC_LEN];
u8 control_state;
u8 error_code;
int err;

mlxsw_reg_mcc_pack(mcc_pl, 0, 0, fwhandle, 0);
err = mlxsw_reg_query(mlxsw_core, MLXSW_REG(mcc), mcc_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_core_fw_fsm_cancel(struct mlxfw_dev *mlxfw_dev, u32 fwhandle)
{
struct mlxsw_core_fw_info *mlxsw_core_fw_info =
container_of(mlxfw_dev, struct mlxsw_core_fw_info, mlxfw_dev);
struct mlxsw_core *mlxsw_core = mlxsw_core_fw_info->mlxsw_core;
char mcc_pl[MLXSW_REG_MCC_LEN];

mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_CANCEL, 0, fwhandle, 0);
mlxsw_reg_write(mlxsw_core, MLXSW_REG(mcc), mcc_pl);
}

static void mlxsw_core_fw_fsm_release(struct mlxfw_dev *mlxfw_dev, u32 fwhandle)
{
struct mlxsw_core_fw_info *mlxsw_core_fw_info =
container_of(mlxfw_dev, struct mlxsw_core_fw_info, mlxfw_dev);
struct mlxsw_core *mlxsw_core = mlxsw_core_fw_info->mlxsw_core;
char mcc_pl[MLXSW_REG_MCC_LEN];

mlxsw_reg_mcc_pack(mcc_pl, MLXSW_REG_MCC_INSTRUCTION_RELEASE_UPDATE_HANDLE, 0, fwhandle, 0);
mlxsw_reg_write(mlxsw_core, MLXSW_REG(mcc), mcc_pl);
}

static const struct mlxfw_dev_ops mlxsw_core_fw_mlxsw_dev_ops = {
.component_query = mlxsw_core_fw_component_query,
.fsm_lock = mlxsw_core_fw_fsm_lock,
.fsm_component_update = mlxsw_core_fw_fsm_component_update,
.fsm_block_download = mlxsw_core_fw_fsm_block_download,
.fsm_component_verify = mlxsw_core_fw_fsm_component_verify,
.fsm_activate = mlxsw_core_fw_fsm_activate,
.fsm_query_state = mlxsw_core_fw_fsm_query_state,
.fsm_cancel = mlxsw_core_fw_fsm_cancel,
.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)
{
struct mlxsw_core_fw_info mlxsw_core_fw_info = {
.mlxfw_dev = {
.ops = &mlxsw_core_fw_mlxsw_dev_ops,
.psid = mlxsw_core->bus_info->psid,
.psid_size = strlen(mlxsw_core->bus_info->psid),
.devlink = priv_to_devlink(mlxsw_core),
},
.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;
}

static int mlxsw_core_fw_rev_validate(struct mlxsw_core *mlxsw_core,
const struct mlxsw_bus_info *mlxsw_bus_info,
const struct mlxsw_fw_rev *req_rev,
const char *filename)
{
const struct mlxsw_fw_rev *rev = &mlxsw_bus_info->fw_rev;
union devlink_param_value value;
const struct firmware *firmware;
int err;

/* Don't check if driver does not require it */
if (!req_rev || !filename)
return 0;

/* Don't check if devlink 'fw_load_policy' param is 'flash' */
err = devlink_param_driverinit_value_get(priv_to_devlink(mlxsw_core),
DEVLINK_PARAM_GENERIC_ID_FW_LOAD_POLICY,
&value);
if (err)
return err;
if (value.vu8 == DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_FLASH)
return 0;

/* Validate driver & FW are compatible */
if (rev->major != req_rev->major) {
WARN(1, "Mismatch in major FW version [%d:%d] is never expected; Please contact support\n",
rev->major, req_rev->major);
return -EINVAL;
}
if (mlxsw_core_fw_rev_minor_subminor_validate(rev, req_rev))
return 0;

dev_err(mlxsw_bus_info->dev, "The firmware version %d.%d.%d is incompatible with the driver (required >= %d.%d.%d)\n",
rev->major, rev->minor, rev->subminor, req_rev->major,
req_rev->minor, req_rev->subminor);
dev_info(mlxsw_bus_info->dev, "Flashing firmware using file %s\n", filename);

err = request_firmware_direct(&firmware, filename, mlxsw_bus_info->dev);
if (err) {
dev_err(mlxsw_bus_info->dev, "Could not request firmware file %s\n", filename);
return err;
}

err = mlxsw_core_fw_flash(mlxsw_core, firmware, NULL);
release_firmware(firmware);
if (err)
dev_err(mlxsw_bus_info->dev, "Could not upgrade firmware\n");

/* On FW flash success, tell the caller FW reset is needed
* if current FW supports it.
*/
if (rev->minor >= req_rev->can_reset_minor)
return err ? err : -EAGAIN;
else
return 0;
}

static int mlxsw_core_fw_flash_update(struct mlxsw_core *mlxsw_core,
const char *file_name, const char *component,
struct netlink_ext_ack *extack)
{
const struct firmware *firmware;
int err;

if (component)
return -EOPNOTSUPP;

err = request_firmware_direct(&firmware, file_name, mlxsw_core->bus_info->dev);
if (err)
return err;
err = mlxsw_core_fw_flash(mlxsw_core, firmware, extack);
release_firmware(firmware);

return err;
}

static int mlxsw_devlink_port_split(struct devlink *devlink,
unsigned int port_index,
unsigned int count,
Expand Down Expand Up @@ -1143,12 +1396,8 @@ static int mlxsw_devlink_flash_update(struct devlink *devlink,
struct netlink_ext_ack *extack)
{
struct mlxsw_core *mlxsw_core = devlink_priv(devlink);
struct mlxsw_driver *mlxsw_driver = mlxsw_core->driver;

if (!mlxsw_driver->flash_update)
return -EOPNOTSUPP;
return mlxsw_driver->flash_update(mlxsw_core, file_name,
component, extack);
return mlxsw_core_fw_flash_update(mlxsw_core, file_name, component, extack);
}

static int mlxsw_devlink_trap_init(struct devlink *devlink,
Expand Down Expand Up @@ -1374,6 +1623,11 @@ __mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info,
goto err_register_params;
}

err = mlxsw_core_fw_rev_validate(mlxsw_core, mlxsw_bus_info, mlxsw_driver->fw_req_rev,
mlxsw_driver->fw_filename);
if (err)
goto err_fw_rev_validate;

if (mlxsw_driver->init) {
err = mlxsw_driver->init(mlxsw_core, mlxsw_bus_info, extack);
if (err)
Expand Down Expand Up @@ -1403,6 +1657,7 @@ __mlxsw_core_bus_device_register(const struct mlxsw_bus_info *mlxsw_bus_info,
if (mlxsw_core->driver->fini)
mlxsw_core->driver->fini(mlxsw_core);
err_driver_init:
err_fw_rev_validate:
if (mlxsw_driver->params_unregister && !reload)
mlxsw_driver->params_unregister(mlxsw_core);
err_register_params:
Expand Down Expand Up @@ -2410,18 +2665,6 @@ int mlxsw_core_kvd_sizes_get(struct mlxsw_core *mlxsw_core,
}
EXPORT_SYMBOL(mlxsw_core_kvd_sizes_get);

void mlxsw_core_fw_flash_start(struct mlxsw_core *mlxsw_core)
{
mlxsw_core->fw_flash_in_progress = true;
}
EXPORT_SYMBOL(mlxsw_core_fw_flash_start);

void mlxsw_core_fw_flash_end(struct mlxsw_core *mlxsw_core)
{
mlxsw_core->fw_flash_in_progress = false;
}
EXPORT_SYMBOL(mlxsw_core_fw_flash_end);

int mlxsw_core_resources_query(struct mlxsw_core *mlxsw_core, char *mbox,
struct mlxsw_res *res)
{
Expand Down
8 changes: 2 additions & 6 deletions drivers/net/ethernet/mellanox/mlxsw/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,8 @@ struct mlxsw_driver {
struct list_head list;
const char *kind;
size_t priv_size;
const struct mlxsw_fw_rev *fw_req_rev;
const char *fw_filename;
int (*init)(struct mlxsw_core *mlxsw_core,
const struct mlxsw_bus_info *mlxsw_bus_info,
struct netlink_ext_ack *extack);
Expand Down Expand Up @@ -324,9 +326,6 @@ struct mlxsw_driver {
unsigned int sb_index, u16 tc_index,
enum devlink_sb_pool_type pool_type,
u32 *p_cur, u32 *p_max);
int (*flash_update)(struct mlxsw_core *mlxsw_core,
const char *file_name, const char *component,
struct netlink_ext_ack *extack);
int (*trap_init)(struct mlxsw_core *mlxsw_core,
const struct devlink_trap *trap, void *trap_ctx);
void (*trap_fini)(struct mlxsw_core *mlxsw_core,
Expand Down Expand Up @@ -378,9 +377,6 @@ int mlxsw_core_kvd_sizes_get(struct mlxsw_core *mlxsw_core,
u64 *p_single_size, u64 *p_double_size,
u64 *p_linear_size);

void mlxsw_core_fw_flash_start(struct mlxsw_core *mlxsw_core);
void mlxsw_core_fw_flash_end(struct mlxsw_core *mlxsw_core);

u32 mlxsw_core_read_frc_h(struct mlxsw_core *mlxsw_core);
u32 mlxsw_core_read_frc_l(struct mlxsw_core *mlxsw_core);

Expand Down
Loading

0 comments on commit b79cb78

Please sign in to comment.