xref: /linux/drivers/net/ethernet/amd/pds_core/devlink.c (revision 91ec2035134982b98fab0609a9fd8480e8217dc1)
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
pdsc_dl_find_viftype_by_id(struct pdsc * pdsc,enum devlink_param_type dl_id)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 
pdsc_dl_enable_get(struct devlink * dl,u32 id,struct devlink_param_gset_ctx * ctx,struct netlink_ext_ack * extack)24 int pdsc_dl_enable_get(struct devlink *dl, u32 id,
25 		       struct devlink_param_gset_ctx *ctx,
26 		       struct netlink_ext_ack *extack)
27 {
28 	struct pdsc *pdsc = devlink_priv(dl);
29 	struct pdsc_viftype *vt_entry;
30 
31 	vt_entry = pdsc_dl_find_viftype_by_id(pdsc, id);
32 	if (!vt_entry)
33 		return -ENOENT;
34 
35 	ctx->val.vbool = vt_entry->enabled;
36 
37 	return 0;
38 }
39 
pdsc_dl_enable_set(struct devlink * dl,u32 id,struct devlink_param_gset_ctx * ctx,struct netlink_ext_ack * extack)40 int pdsc_dl_enable_set(struct devlink *dl, u32 id,
41 		       struct devlink_param_gset_ctx *ctx,
42 		       struct netlink_ext_ack *extack)
43 {
44 	struct pdsc *pdsc = devlink_priv(dl);
45 	struct pdsc_viftype *vt_entry;
46 	int err = 0;
47 	int vf_id;
48 
49 	vt_entry = pdsc_dl_find_viftype_by_id(pdsc, id);
50 	if (!vt_entry || !vt_entry->supported)
51 		return -EOPNOTSUPP;
52 
53 	if (vt_entry->enabled == ctx->val.vbool)
54 		return 0;
55 
56 	vt_entry->enabled = ctx->val.vbool;
57 	for (vf_id = 0; vf_id < pdsc->num_vfs; vf_id++) {
58 		struct pdsc *vf = pdsc->vfs[vf_id].vf;
59 
60 		if (ctx->val.vbool)
61 			err = pdsc_auxbus_dev_add(vf, pdsc, vt_entry->vif_id,
62 						  &pdsc->vfs[vf_id].padev);
63 		else
64 			pdsc_auxbus_dev_del(vf, pdsc, &pdsc->vfs[vf_id].padev);
65 	}
66 
67 	return err;
68 }
69 
pdsc_dl_enable_validate(struct devlink * dl,u32 id,union devlink_param_value * val,struct netlink_ext_ack * extack)70 int pdsc_dl_enable_validate(struct devlink *dl, u32 id,
71 			    union devlink_param_value *val,
72 			    struct netlink_ext_ack *extack)
73 {
74 	struct pdsc *pdsc = devlink_priv(dl);
75 	struct pdsc_viftype *vt_entry;
76 
77 	vt_entry = pdsc_dl_find_viftype_by_id(pdsc, id);
78 	if (!vt_entry || !vt_entry->supported)
79 		return -EOPNOTSUPP;
80 
81 	if (!pdsc->viftype_status[vt_entry->vif_id].supported)
82 		return -ENODEV;
83 
84 	return 0;
85 }
86 
pdsc_dl_flash_update(struct devlink * dl,struct devlink_flash_update_params * params,struct netlink_ext_ack * extack)87 int pdsc_dl_flash_update(struct devlink *dl,
88 			 struct devlink_flash_update_params *params,
89 			 struct netlink_ext_ack *extack)
90 {
91 	struct pdsc *pdsc = devlink_priv(dl);
92 
93 	return pdsc_firmware_update(pdsc, params, extack);
94 }
95 
pdsc_dl_report_component(struct devlink_info_req * req,struct pds_core_fw_component_info * info)96 static int pdsc_dl_report_component(struct devlink_info_req *req,
97 				    struct pds_core_fw_component_info *info)
98 {
99 	enum devlink_info_version_type ver_type;
100 	u16 flags = le16_to_cpu(info->flags);
101 	char *ver = info->version;
102 	const char *name;
103 	char buf[32];
104 
105 	/* Main firmware is reported as generic "fw" */
106 	if (info->component_type == PDS_CORE_FW_TYPE_MAIN) {
107 		if (info->slot_id == PDS_CORE_FW_SLOT_GOLD)
108 			snprintf(buf, sizeof(buf), "fw.gold");
109 		else
110 			snprintf(buf, sizeof(buf), "fw");
111 	} else {
112 		name = pdsc_fw_type_to_name(info->component_type);
113 		if (!name)
114 			return 0;
115 
116 		if (info->slot_id == PDS_CORE_FW_SLOT_GOLD)
117 			snprintf(buf, sizeof(buf), "fw.%s.gold", name);
118 		else
119 			snprintf(buf, sizeof(buf), "fw.%s", name);
120 	}
121 
122 	ver_type = DEVLINK_INFO_VERSION_TYPE_NONE;
123 	if (flags & PDS_CORE_FW_COMPONENT_INFO_F_UPDATE_BY_NAME)
124 		ver_type = DEVLINK_INFO_VERSION_TYPE_COMPONENT;
125 
126 	if (flags & PDS_CORE_FW_COMPONENT_INFO_F_FIXED) {
127 		int err;
128 
129 		err = devlink_info_version_fixed_put(req, buf, ver);
130 		if (err)
131 			return err;
132 	}
133 
134 	if (flags & PDS_CORE_FW_COMPONENT_INFO_F_RUNNING) {
135 		int err;
136 
137 		err = devlink_info_version_running_put_ext(req, buf,
138 							   ver, ver_type);
139 		if (err)
140 			return err;
141 	}
142 
143 	if (flags & PDS_CORE_FW_COMPONENT_INFO_F_STARTUP) {
144 		int err;
145 
146 		err = devlink_info_version_stored_put_ext(req, buf,
147 							  ver, ver_type);
148 		if (err)
149 			return err;
150 	}
151 
152 	return 0;
153 }
154 
pdsc_dl_report_fw_ver(struct devlink_info_req * req,char * fw_ver)155 static int pdsc_dl_report_fw_ver(struct devlink_info_req *req, char *fw_ver)
156 {
157 	return devlink_info_version_running_put(req,
158 						DEVLINK_INFO_VERSION_GENERIC_FW,
159 						fw_ver);
160 }
161 
pdsc_dl_component_info_get(struct devlink * dl,struct devlink_info_req * req,struct netlink_ext_ack * extack)162 static int pdsc_dl_component_info_get(struct devlink *dl,
163 				      struct devlink_info_req *req,
164 				      struct netlink_ext_ack *extack)
165 {
166 	struct pdsc *pdsc = devlink_priv(dl);
167 	u8 num_components;
168 	int err;
169 	int i;
170 
171 	/* Pairs with WRITE_ONCE in pdsc_fw_components_invalidate().
172 	 * Use READ_ONCE to get a consistent snapshot of num_components.
173 	 * pdsc_fw_components_invalidate() can zero it concurrently during
174 	 * firmware recovery; using the local copy avoids iterating zero
175 	 * times when we already decided the cache was valid.
176 	 */
177 	num_components = READ_ONCE(pdsc->fw_components.num_components);
178 	if (!num_components) {
179 		err = pdsc_get_component_info(pdsc);
180 		if (err)
181 			return pdsc_dl_report_fw_ver(req,
182 						    pdsc->dev_info.fw_version);
183 		num_components = READ_ONCE(pdsc->fw_components.num_components);
184 		if (!num_components)
185 			return pdsc_dl_report_fw_ver(req,
186 						    pdsc->dev_info.fw_version);
187 	}
188 
189 	num_components = min_t(u16, num_components,
190 			       le16_to_cpu(pdsc->dev_ident.max_fw_slots));
191 	for (i = 0; i < num_components; i++) {
192 		err = pdsc_dl_report_component(req,
193 					       &pdsc->fw_components.info[i]);
194 		if (err)
195 			return err;
196 	}
197 
198 	return 0;
199 }
200 
201 static char *fw_slotnames[] = {
202 	"fw.goldfw",
203 	"fw.mainfwa",
204 	"fw.mainfwb",
205 };
206 
pdsc_dl_fw_list_info_get(struct devlink * dl,struct devlink_info_req * req,struct netlink_ext_ack * extack)207 static int pdsc_dl_fw_list_info_get(struct devlink *dl,
208 				    struct devlink_info_req *req,
209 				    struct netlink_ext_ack *extack)
210 {
211 	union pds_core_dev_cmd cmd = {
212 		.fw_control.opcode = PDS_CORE_CMD_FW_CONTROL,
213 		.fw_control.oper = PDS_CORE_FW_GET_LIST,
214 	};
215 	struct pds_core_fw_list_info fw_list = {};
216 	struct pdsc *pdsc = devlink_priv(dl);
217 	union pds_core_dev_comp comp;
218 	char buf[32];
219 	int listlen;
220 	int err;
221 	int i;
222 
223 	mutex_lock(&pdsc->devcmd_lock);
224 	err = pdsc_devcmd_locked(pdsc, &cmd, &comp, pdsc->devcmd_timeout * 2);
225 	if (!err)
226 		memcpy_fromio(&fw_list, pdsc->cmd_regs->data, sizeof(fw_list));
227 	mutex_unlock(&pdsc->devcmd_lock);
228 
229 	listlen = min(fw_list.num_fw_slots, ARRAY_SIZE(fw_list.fw_names));
230 	for (i = 0; i < listlen; i++) {
231 		char *fw_ver = fw_list.fw_names[i].fw_version;
232 
233 		if (i < ARRAY_SIZE(fw_slotnames))
234 			strscpy(buf, fw_slotnames[i], sizeof(buf));
235 		else
236 			snprintf(buf, sizeof(buf), "fw.slot_%d", i);
237 		fw_ver[sizeof(fw_list.fw_names[i].fw_version) - 1] = '\0';
238 		err = devlink_info_version_stored_put(req, buf, fw_ver);
239 		if (err)
240 			return err;
241 	}
242 
243 	return 0;
244 }
245 
pdsc_dl_info_get_v1(struct devlink * dl,struct devlink_info_req * req,struct netlink_ext_ack * extack)246 static int pdsc_dl_info_get_v1(struct devlink *dl,
247 			       struct devlink_info_req *req,
248 			       struct netlink_ext_ack *extack)
249 {
250 	struct pdsc *pdsc = devlink_priv(dl);
251 	int err;
252 
253 	err = pdsc_dl_fw_list_info_get(dl, req, extack);
254 	if (err)
255 		return err;
256 
257 	/* Version 1: report fw from dev_info (running only) */
258 	return pdsc_dl_report_fw_ver(req, pdsc->dev_info.fw_version);
259 }
260 
pdsc_dl_info_get(struct devlink * dl,struct devlink_info_req * req,struct netlink_ext_ack * extack)261 int pdsc_dl_info_get(struct devlink *dl, struct devlink_info_req *req,
262 		     struct netlink_ext_ack *extack)
263 {
264 	struct pdsc *pdsc = devlink_priv(dl);
265 	char buf[32];
266 	int err;
267 
268 	if (pdsc->dev_ident.version >= PDS_CORE_IDENTITY_VERSION_2) {
269 		err = pdsc_dl_component_info_get(dl, req, extack);
270 		if (err)
271 			return err;
272 	} else {
273 		err = pdsc_dl_info_get_v1(dl, req, extack);
274 		if (err)
275 			return err;
276 	}
277 
278 	snprintf(buf, sizeof(buf), "0x%x", pdsc->dev_info.asic_type);
279 	err = devlink_info_version_fixed_put(req,
280 					     DEVLINK_INFO_VERSION_GENERIC_ASIC_ID,
281 					     buf);
282 	if (err)
283 		return err;
284 
285 	snprintf(buf, sizeof(buf), "0x%x", pdsc->dev_info.asic_rev);
286 	err = devlink_info_version_fixed_put(req,
287 					     DEVLINK_INFO_VERSION_GENERIC_ASIC_REV,
288 					     buf);
289 	if (err)
290 		return err;
291 
292 	return devlink_info_serial_number_put(req, pdsc->dev_info.serial_num);
293 }
294 
pdsc_fw_reporter_diagnose(struct devlink_health_reporter * reporter,struct devlink_fmsg * fmsg,struct netlink_ext_ack * extack)295 int pdsc_fw_reporter_diagnose(struct devlink_health_reporter *reporter,
296 			      struct devlink_fmsg *fmsg,
297 			      struct netlink_ext_ack *extack)
298 {
299 	struct pdsc *pdsc = devlink_health_reporter_priv(reporter);
300 
301 	mutex_lock(&pdsc->config_lock);
302 	if (test_bit(PDSC_S_FW_DEAD, &pdsc->state))
303 		devlink_fmsg_string_pair_put(fmsg, "Status", "dead");
304 	else if (!pdsc_is_fw_good(pdsc))
305 		devlink_fmsg_string_pair_put(fmsg, "Status", "unhealthy");
306 	else
307 		devlink_fmsg_string_pair_put(fmsg, "Status", "healthy");
308 	mutex_unlock(&pdsc->config_lock);
309 
310 	devlink_fmsg_u32_pair_put(fmsg, "State",
311 				  pdsc->fw_status & ~PDS_CORE_FW_STS_F_GENERATION);
312 	devlink_fmsg_u32_pair_put(fmsg, "Generation", pdsc->fw_generation >> 4);
313 	devlink_fmsg_u32_pair_put(fmsg, "Recoveries", pdsc->fw_recoveries);
314 
315 	return 0;
316 }
317