xref: /linux/drivers/net/ethernet/amd/pds_core/devlink.c (revision bf5802238dc181b1f7375d358af1d01cd72d1c11)
1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright(c) 2023 Advanced Micro Devices, Inc */
3 
4 #include "core.h"
5 #include <linux/pds/pds_auxbus.h>
6 
7 static struct
8 pdsc_viftype *pdsc_dl_find_viftype_by_id(struct pdsc *pdsc,
9 					 enum devlink_param_type dl_id)
10 {
11 	int vt;
12 
13 	if (!pdsc->viftype_status)
14 		return NULL;
15 
16 	for (vt = 0; vt < PDS_DEV_TYPE_MAX; vt++) {
17 		if (pdsc->viftype_status[vt].dl_id == dl_id)
18 			return &pdsc->viftype_status[vt];
19 	}
20 
21 	return NULL;
22 }
23 
24 int pdsc_dl_enable_get(struct devlink *dl, u32 id,
25 		       struct devlink_param_gset_ctx *ctx)
26 {
27 	struct pdsc *pdsc = devlink_priv(dl);
28 	struct pdsc_viftype *vt_entry;
29 
30 	vt_entry = pdsc_dl_find_viftype_by_id(pdsc, id);
31 	if (!vt_entry)
32 		return -ENOENT;
33 
34 	ctx->val.vbool = vt_entry->enabled;
35 
36 	return 0;
37 }
38 
39 int pdsc_dl_enable_set(struct devlink *dl, u32 id,
40 		       struct devlink_param_gset_ctx *ctx)
41 {
42 	struct pdsc *pdsc = devlink_priv(dl);
43 	struct pdsc_viftype *vt_entry;
44 	int err = 0;
45 	int vf_id;
46 
47 	vt_entry = pdsc_dl_find_viftype_by_id(pdsc, id);
48 	if (!vt_entry || !vt_entry->supported)
49 		return -EOPNOTSUPP;
50 
51 	if (vt_entry->enabled == ctx->val.vbool)
52 		return 0;
53 
54 	vt_entry->enabled = ctx->val.vbool;
55 	for (vf_id = 0; vf_id < pdsc->num_vfs; vf_id++) {
56 		struct pdsc *vf = pdsc->vfs[vf_id].vf;
57 
58 		err = ctx->val.vbool ? pdsc_auxbus_dev_add(vf, pdsc) :
59 				       pdsc_auxbus_dev_del(vf, pdsc);
60 	}
61 
62 	return err;
63 }
64 
65 int pdsc_dl_enable_validate(struct devlink *dl, u32 id,
66 			    union devlink_param_value val,
67 			    struct netlink_ext_ack *extack)
68 {
69 	struct pdsc *pdsc = devlink_priv(dl);
70 	struct pdsc_viftype *vt_entry;
71 
72 	vt_entry = pdsc_dl_find_viftype_by_id(pdsc, id);
73 	if (!vt_entry || !vt_entry->supported)
74 		return -EOPNOTSUPP;
75 
76 	if (!pdsc->viftype_status[vt_entry->vif_id].supported)
77 		return -ENODEV;
78 
79 	return 0;
80 }
81 
82 int pdsc_dl_flash_update(struct devlink *dl,
83 			 struct devlink_flash_update_params *params,
84 			 struct netlink_ext_ack *extack)
85 {
86 	struct pdsc *pdsc = devlink_priv(dl);
87 
88 	return pdsc_firmware_update(pdsc, params->fw, extack);
89 }
90 
91 static char *fw_slotnames[] = {
92 	"fw.goldfw",
93 	"fw.mainfwa",
94 	"fw.mainfwb",
95 };
96 
97 int pdsc_dl_info_get(struct devlink *dl, struct devlink_info_req *req,
98 		     struct netlink_ext_ack *extack)
99 {
100 	union pds_core_dev_cmd cmd = {
101 		.fw_control.opcode = PDS_CORE_CMD_FW_CONTROL,
102 		.fw_control.oper = PDS_CORE_FW_GET_LIST,
103 	};
104 	struct pds_core_fw_list_info fw_list;
105 	struct pdsc *pdsc = devlink_priv(dl);
106 	union pds_core_dev_comp comp;
107 	char buf[32];
108 	int listlen;
109 	int err;
110 	int i;
111 
112 	mutex_lock(&pdsc->devcmd_lock);
113 	err = pdsc_devcmd_locked(pdsc, &cmd, &comp, pdsc->devcmd_timeout * 2);
114 	if (!err)
115 		memcpy_fromio(&fw_list, pdsc->cmd_regs->data, sizeof(fw_list));
116 	mutex_unlock(&pdsc->devcmd_lock);
117 	if (err && err != -EIO)
118 		return err;
119 
120 	listlen = fw_list.num_fw_slots;
121 	for (i = 0; i < listlen; i++) {
122 		if (i < ARRAY_SIZE(fw_slotnames))
123 			strscpy(buf, fw_slotnames[i], sizeof(buf));
124 		else
125 			snprintf(buf, sizeof(buf), "fw.slot_%d", i);
126 		err = devlink_info_version_stored_put(req, buf,
127 						      fw_list.fw_names[i].fw_version);
128 		if (err)
129 			return err;
130 	}
131 
132 	err = devlink_info_version_running_put(req,
133 					       DEVLINK_INFO_VERSION_GENERIC_FW,
134 					       pdsc->dev_info.fw_version);
135 	if (err)
136 		return err;
137 
138 	snprintf(buf, sizeof(buf), "0x%x", pdsc->dev_info.asic_type);
139 	err = devlink_info_version_fixed_put(req,
140 					     DEVLINK_INFO_VERSION_GENERIC_ASIC_ID,
141 					     buf);
142 	if (err)
143 		return err;
144 
145 	snprintf(buf, sizeof(buf), "0x%x", pdsc->dev_info.asic_rev);
146 	err = devlink_info_version_fixed_put(req,
147 					     DEVLINK_INFO_VERSION_GENERIC_ASIC_REV,
148 					     buf);
149 	if (err)
150 		return err;
151 
152 	return devlink_info_serial_number_put(req, pdsc->dev_info.serial_num);
153 }
154 
155 int pdsc_fw_reporter_diagnose(struct devlink_health_reporter *reporter,
156 			      struct devlink_fmsg *fmsg,
157 			      struct netlink_ext_ack *extack)
158 {
159 	struct pdsc *pdsc = devlink_health_reporter_priv(reporter);
160 
161 	mutex_lock(&pdsc->config_lock);
162 	if (test_bit(PDSC_S_FW_DEAD, &pdsc->state))
163 		devlink_fmsg_string_pair_put(fmsg, "Status", "dead");
164 	else if (!pdsc_is_fw_good(pdsc))
165 		devlink_fmsg_string_pair_put(fmsg, "Status", "unhealthy");
166 	else
167 		devlink_fmsg_string_pair_put(fmsg, "Status", "healthy");
168 	mutex_unlock(&pdsc->config_lock);
169 
170 	devlink_fmsg_u32_pair_put(fmsg, "State",
171 				  pdsc->fw_status & ~PDS_CORE_FW_STS_F_GENERATION);
172 	devlink_fmsg_u32_pair_put(fmsg, "Generation", pdsc->fw_generation >> 4);
173 	devlink_fmsg_u32_pair_put(fmsg, "Recoveries", pdsc->fw_recoveries);
174 
175 	return 0;
176 }
177