316 lines · 8.0 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* Copyright(c) 2023 Advanced Micro Devices, Inc */ #include "core.h"#include <linux/pds/pds_auxbus.h> static structpdsc_viftype *pdsc_dl_find_viftype_by_id(struct pdsc *pdsc,					 enum devlink_param_type dl_id){	int vt; 	if (!pdsc->viftype_status)		return NULL; 	for (vt = 0; vt < PDS_DEV_TYPE_MAX; vt++) {		if (pdsc->viftype_status[vt].dl_id == dl_id)			return &pdsc->viftype_status[vt];	} 	return NULL;} int pdsc_dl_enable_get(struct devlink *dl, u32 id,		       struct devlink_param_gset_ctx *ctx,		       struct netlink_ext_ack *extack){	struct pdsc *pdsc = devlink_priv(dl);	struct pdsc_viftype *vt_entry; 	vt_entry = pdsc_dl_find_viftype_by_id(pdsc, id);	if (!vt_entry)		return -ENOENT; 	ctx->val.vbool = vt_entry->enabled; 	return 0;} int pdsc_dl_enable_set(struct devlink *dl, u32 id,		       struct devlink_param_gset_ctx *ctx,		       struct netlink_ext_ack *extack){	struct pdsc *pdsc = devlink_priv(dl);	struct pdsc_viftype *vt_entry;	int err = 0;	int vf_id; 	vt_entry = pdsc_dl_find_viftype_by_id(pdsc, id);	if (!vt_entry || !vt_entry->supported)		return -EOPNOTSUPP; 	if (vt_entry->enabled == ctx->val.vbool)		return 0; 	vt_entry->enabled = ctx->val.vbool;	for (vf_id = 0; vf_id < pdsc->num_vfs; vf_id++) {		struct pdsc *vf = pdsc->vfs[vf_id].vf; 		if (ctx->val.vbool)			err = pdsc_auxbus_dev_add(vf, pdsc, vt_entry->vif_id,						  &pdsc->vfs[vf_id].padev);		else			pdsc_auxbus_dev_del(vf, pdsc, &pdsc->vfs[vf_id].padev);	} 	return err;} int pdsc_dl_enable_validate(struct devlink *dl, u32 id,			    union devlink_param_value *val,			    struct netlink_ext_ack *extack){	struct pdsc *pdsc = devlink_priv(dl);	struct pdsc_viftype *vt_entry; 	vt_entry = pdsc_dl_find_viftype_by_id(pdsc, id);	if (!vt_entry || !vt_entry->supported)		return -EOPNOTSUPP; 	if (!pdsc->viftype_status[vt_entry->vif_id].supported)		return -ENODEV; 	return 0;} int pdsc_dl_flash_update(struct devlink *dl,			 struct devlink_flash_update_params *params,			 struct netlink_ext_ack *extack){	struct pdsc *pdsc = devlink_priv(dl); 	return pdsc_firmware_update(pdsc, params, extack);} static int pdsc_dl_report_component(struct devlink_info_req *req,				    struct pds_core_fw_component_info *info){	enum devlink_info_version_type ver_type;	u16 flags = le16_to_cpu(info->flags);	char *ver = info->version;	const char *name;	char buf[32]; 	/* Main firmware is reported as generic "fw" */	if (info->component_type == PDS_CORE_FW_TYPE_MAIN) {		if (info->slot_id == PDS_CORE_FW_SLOT_GOLD)			snprintf(buf, sizeof(buf), "fw.gold");		else			snprintf(buf, sizeof(buf), "fw");	} else {		name = pdsc_fw_type_to_name(info->component_type);		if (!name)			return 0; 		if (info->slot_id == PDS_CORE_FW_SLOT_GOLD)			snprintf(buf, sizeof(buf), "fw.%s.gold", name);		else			snprintf(buf, sizeof(buf), "fw.%s", name);	} 	ver_type = DEVLINK_INFO_VERSION_TYPE_NONE;	if (flags & PDS_CORE_FW_COMPONENT_INFO_F_UPDATE_BY_NAME)		ver_type = DEVLINK_INFO_VERSION_TYPE_COMPONENT; 	if (flags & PDS_CORE_FW_COMPONENT_INFO_F_FIXED) {		int err; 		err = devlink_info_version_fixed_put(req, buf, ver);		if (err)			return err;	} 	if (flags & PDS_CORE_FW_COMPONENT_INFO_F_RUNNING) {		int err; 		err = devlink_info_version_running_put_ext(req, buf,							   ver, ver_type);		if (err)			return err;	} 	if (flags & PDS_CORE_FW_COMPONENT_INFO_F_STARTUP) {		int err; 		err = devlink_info_version_stored_put_ext(req, buf,							  ver, ver_type);		if (err)			return err;	} 	return 0;} static int pdsc_dl_report_fw_ver(struct devlink_info_req *req, char *fw_ver){	return devlink_info_version_running_put(req,						DEVLINK_INFO_VERSION_GENERIC_FW,						fw_ver);} static int pdsc_dl_component_info_get(struct devlink *dl,				      struct devlink_info_req *req,				      struct netlink_ext_ack *extack){	struct pdsc *pdsc = devlink_priv(dl);	u8 num_components;	int err;	int i; 	/* Pairs with WRITE_ONCE in pdsc_fw_components_invalidate().	 * Use READ_ONCE to get a consistent snapshot of num_components.	 * pdsc_fw_components_invalidate() can zero it concurrently during	 * firmware recovery; using the local copy avoids iterating zero	 * times when we already decided the cache was valid.	 */	num_components = READ_ONCE(pdsc->fw_components.num_components);	if (!num_components) {		err = pdsc_get_component_info(pdsc);		if (err)			return pdsc_dl_report_fw_ver(req,						    pdsc->dev_info.fw_version);		num_components = READ_ONCE(pdsc->fw_components.num_components);		if (!num_components)			return pdsc_dl_report_fw_ver(req,						    pdsc->dev_info.fw_version);	} 	num_components = min_t(u16, num_components,			       le16_to_cpu(pdsc->dev_ident.max_fw_slots));	for (i = 0; i < num_components; i++) {		err = pdsc_dl_report_component(req,					       &pdsc->fw_components.info[i]);		if (err)			return err;	} 	return 0;} static char *fw_slotnames[] = {	"fw.goldfw",	"fw.mainfwa",	"fw.mainfwb",}; static int pdsc_dl_fw_list_info_get(struct devlink *dl,				    struct devlink_info_req *req,				    struct netlink_ext_ack *extack){	union pds_core_dev_cmd cmd = {		.fw_control.opcode = PDS_CORE_CMD_FW_CONTROL,		.fw_control.oper = PDS_CORE_FW_GET_LIST,	};	struct pds_core_fw_list_info fw_list = {};	struct pdsc *pdsc = devlink_priv(dl);	union pds_core_dev_comp comp;	char buf[32];	int listlen;	int err;	int i; 	mutex_lock(&pdsc->devcmd_lock);	err = pdsc_devcmd_locked(pdsc, &cmd, &comp, pdsc->devcmd_timeout * 2);	if (!err)		memcpy_fromio(&fw_list, pdsc->cmd_regs->data, sizeof(fw_list));	mutex_unlock(&pdsc->devcmd_lock); 	listlen = min(fw_list.num_fw_slots, ARRAY_SIZE(fw_list.fw_names));	for (i = 0; i < listlen; i++) {		char *fw_ver = fw_list.fw_names[i].fw_version; 		if (i < ARRAY_SIZE(fw_slotnames))			strscpy(buf, fw_slotnames[i], sizeof(buf));		else			snprintf(buf, sizeof(buf), "fw.slot_%d", i);		fw_ver[sizeof(fw_list.fw_names[i].fw_version) - 1] = '\0';		err = devlink_info_version_stored_put(req, buf, fw_ver);		if (err)			return err;	} 	return 0;} static int pdsc_dl_info_get_v1(struct devlink *dl,			       struct devlink_info_req *req,			       struct netlink_ext_ack *extack){	struct pdsc *pdsc = devlink_priv(dl);	int err; 	err = pdsc_dl_fw_list_info_get(dl, req, extack);	if (err)		return err; 	/* Version 1: report fw from dev_info (running only) */	return pdsc_dl_report_fw_ver(req, pdsc->dev_info.fw_version);} int pdsc_dl_info_get(struct devlink *dl, struct devlink_info_req *req,		     struct netlink_ext_ack *extack){	struct pdsc *pdsc = devlink_priv(dl);	char buf[32];	int err; 	if (pdsc->dev_ident.version >= PDS_CORE_IDENTITY_VERSION_2) {		err = pdsc_dl_component_info_get(dl, req, extack);		if (err)			return err;	} else {		err = pdsc_dl_info_get_v1(dl, req, extack);		if (err)			return err;	} 	snprintf(buf, sizeof(buf), "0x%x", pdsc->dev_info.asic_type);	err = devlink_info_version_fixed_put(req,					     DEVLINK_INFO_VERSION_GENERIC_ASIC_ID,					     buf);	if (err)		return err; 	snprintf(buf, sizeof(buf), "0x%x", pdsc->dev_info.asic_rev);	err = devlink_info_version_fixed_put(req,					     DEVLINK_INFO_VERSION_GENERIC_ASIC_REV,					     buf);	if (err)		return err; 	return devlink_info_serial_number_put(req, pdsc->dev_info.serial_num);} int pdsc_fw_reporter_diagnose(struct devlink_health_reporter *reporter,			      struct devlink_fmsg *fmsg,			      struct netlink_ext_ack *extack){	struct pdsc *pdsc = devlink_health_reporter_priv(reporter); 	mutex_lock(&pdsc->config_lock);	if (test_bit(PDSC_S_FW_DEAD, &pdsc->state))		devlink_fmsg_string_pair_put(fmsg, "Status", "dead");	else if (!pdsc_is_fw_good(pdsc))		devlink_fmsg_string_pair_put(fmsg, "Status", "unhealthy");	else		devlink_fmsg_string_pair_put(fmsg, "Status", "healthy");	mutex_unlock(&pdsc->config_lock); 	devlink_fmsg_u32_pair_put(fmsg, "State",				  pdsc->fw_status & ~PDS_CORE_FW_STS_F_GENERATION);	devlink_fmsg_u32_pair_put(fmsg, "Generation", pdsc->fw_generation >> 4);	devlink_fmsg_u32_pair_put(fmsg, "Recoveries", pdsc->fw_recoveries); 	return 0;}