xref: /linux/net/ethtool/cmis_fw_update.c (revision a4a35f6cbebbf9466b6c412506ab89299d567f51)
1 // SPDX-License-Identifier: GPL-2.0-only
2 
3 #include <linux/ethtool.h>
4 #include <linux/firmware.h>
5 
6 #include "common.h"
7 #include "module_fw.h"
8 #include "cmis.h"
9 
10 struct cmis_fw_update_fw_mng_features {
11 	u8	start_cmd_payload_size;
12 	u16	max_duration_start;
13 	u16	max_duration_write;
14 	u16	max_duration_complete;
15 };
16 
17 /* See section 9.4.2 "CMD 0041h: Firmware Management Features" in CMIS standard
18  * revision 5.2.
19  * struct cmis_cdb_fw_mng_features_rpl is a structured layout of the flat
20  * array, ethtool_cmis_cdb_rpl::payload.
21  */
22 struct cmis_cdb_fw_mng_features_rpl {
23 	u8	resv1;
24 	u8	resv2;
25 	u8	start_cmd_payload_size;
26 	u8	resv3;
27 	u8	read_write_len_ext;
28 	u8	write_mechanism;
29 	u8	resv4;
30 	u8	resv5;
31 	__be16	max_duration_start;
32 	__be16	resv6;
33 	__be16	max_duration_write;
34 	__be16	max_duration_complete;
35 	__be16	resv7;
36 };
37 
38 enum cmis_cdb_fw_write_mechanism {
39 	CMIS_CDB_FW_WRITE_MECHANISM_LPL		= 0x01,
40 	CMIS_CDB_FW_WRITE_MECHANISM_BOTH	= 0x11,
41 };
42 
43 static int
cmis_fw_update_fw_mng_features_get(struct ethtool_cmis_cdb * cdb,struct net_device * dev,struct cmis_fw_update_fw_mng_features * fw_mng,struct ethnl_module_fw_flash_ntf_params * ntf_params)44 cmis_fw_update_fw_mng_features_get(struct ethtool_cmis_cdb *cdb,
45 				   struct net_device *dev,
46 				   struct cmis_fw_update_fw_mng_features *fw_mng,
47 				   struct ethnl_module_fw_flash_ntf_params *ntf_params)
48 {
49 	struct ethtool_cmis_cdb_cmd_args args = {};
50 	struct cmis_cdb_fw_mng_features_rpl *rpl;
51 	u8 flags = CDB_F_STATUS_VALID;
52 	int err;
53 
54 	ethtool_cmis_cdb_check_completion_flag(cdb->cmis_rev, &flags);
55 	ethtool_cmis_cdb_compose_args(&args,
56 				      ETHTOOL_CMIS_CDB_CMD_FW_MANAGMENT_FEATURES,
57 				      NULL, 0, cdb->max_completion_time,
58 				      cdb->read_write_len_ext, 1000,
59 				      sizeof(*rpl), flags);
60 
61 	err = ethtool_cmis_cdb_execute_cmd(dev, &args);
62 	if (err < 0) {
63 		ethnl_module_fw_flash_ntf_err(dev, ntf_params,
64 					      "FW Management Features command failed",
65 					      args.err_msg);
66 		return err;
67 	}
68 
69 	rpl = (struct cmis_cdb_fw_mng_features_rpl *)args.req.payload;
70 	if (!(rpl->write_mechanism == CMIS_CDB_FW_WRITE_MECHANISM_LPL ||
71 	      rpl->write_mechanism == CMIS_CDB_FW_WRITE_MECHANISM_BOTH)) {
72 		ethnl_module_fw_flash_ntf_err(dev, ntf_params,
73 					      "Write LPL is not supported",
74 					      NULL);
75 		return  -EOPNOTSUPP;
76 	}
77 
78 	/* Above, we used read_write_len_ext that we got from CDB
79 	 * advertisement. Update it with the value that we got from module
80 	 * features query, which is specific for Firmware Management Commands
81 	 * (IDs 0100h-01FFh).
82 	 */
83 	cdb->read_write_len_ext = rpl->read_write_len_ext;
84 	fw_mng->start_cmd_payload_size = rpl->start_cmd_payload_size;
85 	fw_mng->max_duration_start = be16_to_cpu(rpl->max_duration_start);
86 	fw_mng->max_duration_write = be16_to_cpu(rpl->max_duration_write);
87 	fw_mng->max_duration_complete = be16_to_cpu(rpl->max_duration_complete);
88 
89 	return 0;
90 }
91 
92 /* See section 9.7.2 "CMD 0101h: Start Firmware Download" in CMIS standard
93  * revision 5.2.
94  * struct cmis_cdb_start_fw_download_pl is a structured layout of the
95  * flat array, ethtool_cmis_cdb_request::payload.
96  */
97 struct cmis_cdb_start_fw_download_pl {
98 	__struct_group(cmis_cdb_start_fw_download_pl_h, head, /* no attrs */,
99 			__be32	image_size;
100 			__be32	resv1;
101 	);
102 	u8 vendor_data[ETHTOOL_CMIS_CDB_LPL_MAX_PL_LENGTH -
103 		sizeof(struct cmis_cdb_start_fw_download_pl_h)];
104 };
105 
106 static int
cmis_fw_update_start_download(struct ethtool_cmis_cdb * cdb,struct ethtool_cmis_fw_update_params * fw_update,struct cmis_fw_update_fw_mng_features * fw_mng)107 cmis_fw_update_start_download(struct ethtool_cmis_cdb *cdb,
108 			      struct ethtool_cmis_fw_update_params *fw_update,
109 			      struct cmis_fw_update_fw_mng_features *fw_mng)
110 {
111 	u8 vendor_data_size = fw_mng->start_cmd_payload_size;
112 	struct cmis_cdb_start_fw_download_pl pl = {};
113 	struct ethtool_cmis_cdb_cmd_args args = {};
114 	u8 lpl_len;
115 	int err;
116 
117 	pl.image_size = cpu_to_be32(fw_update->fw->size);
118 	memcpy(pl.vendor_data, fw_update->fw->data, vendor_data_size);
119 
120 	lpl_len = offsetof(struct cmis_cdb_start_fw_download_pl,
121 			   vendor_data[vendor_data_size]);
122 
123 	ethtool_cmis_cdb_compose_args(&args,
124 				      ETHTOOL_CMIS_CDB_CMD_START_FW_DOWNLOAD,
125 				      (u8 *)&pl, lpl_len,
126 				      fw_mng->max_duration_start,
127 				      cdb->read_write_len_ext, 1000, 0,
128 				      CDB_F_COMPLETION_VALID | CDB_F_STATUS_VALID);
129 
130 	err = ethtool_cmis_cdb_execute_cmd(fw_update->dev, &args);
131 	if (err < 0)
132 		ethnl_module_fw_flash_ntf_err(fw_update->dev,
133 					      &fw_update->ntf_params,
134 					      "Start FW download command failed",
135 					      args.err_msg);
136 
137 	return err;
138 }
139 
140 /* See section 9.7.4 "CMD 0103h: Write Firmware Block LPL" in CMIS standard
141  * revision 5.2.
142  * struct cmis_cdb_write_fw_block_lpl_pl is a structured layout of the
143  * flat array, ethtool_cmis_cdb_request::payload.
144  */
145 struct cmis_cdb_write_fw_block_lpl_pl {
146 	__be32	block_address;
147 	u8 fw_block[ETHTOOL_CMIS_CDB_LPL_MAX_PL_LENGTH - sizeof(__be32)];
148 };
149 
150 static int
cmis_fw_update_write_image(struct ethtool_cmis_cdb * cdb,struct ethtool_cmis_fw_update_params * fw_update,struct cmis_fw_update_fw_mng_features * fw_mng)151 cmis_fw_update_write_image(struct ethtool_cmis_cdb *cdb,
152 			   struct ethtool_cmis_fw_update_params *fw_update,
153 			   struct cmis_fw_update_fw_mng_features *fw_mng)
154 {
155 	u8 start = fw_mng->start_cmd_payload_size;
156 	u32 offset, max_block_size, max_lpl_len;
157 	u32 image_size = fw_update->fw->size;
158 	int err;
159 
160 	max_lpl_len = min_t(u32,
161 			    ethtool_cmis_get_max_payload_size(cdb->read_write_len_ext),
162 			    ETHTOOL_CMIS_CDB_LPL_MAX_PL_LENGTH);
163 	max_block_size =
164 		max_lpl_len - sizeof_field(struct cmis_cdb_write_fw_block_lpl_pl,
165 					   block_address);
166 
167 	for (offset = start; offset < image_size; offset += max_block_size) {
168 		struct cmis_cdb_write_fw_block_lpl_pl pl = {
169 			.block_address = cpu_to_be32(offset - start),
170 		};
171 		struct ethtool_cmis_cdb_cmd_args args = {};
172 		u32 block_size, lpl_len;
173 
174 		ethnl_module_fw_flash_ntf_in_progress(fw_update->dev,
175 						      &fw_update->ntf_params,
176 						      offset - start,
177 						      image_size);
178 		block_size = min_t(u32, max_block_size, image_size - offset);
179 		memcpy(pl.fw_block, &fw_update->fw->data[offset], block_size);
180 		lpl_len = block_size +
181 			sizeof_field(struct cmis_cdb_write_fw_block_lpl_pl,
182 				     block_address);
183 
184 		ethtool_cmis_cdb_compose_args(&args,
185 					      ETHTOOL_CMIS_CDB_CMD_WRITE_FW_BLOCK_LPL,
186 					      (u8 *)&pl, lpl_len,
187 					      fw_mng->max_duration_write,
188 					      cdb->read_write_len_ext, 1, 0,
189 					      CDB_F_COMPLETION_VALID | CDB_F_STATUS_VALID);
190 
191 		err = ethtool_cmis_cdb_execute_cmd(fw_update->dev, &args);
192 		if (err < 0) {
193 			ethnl_module_fw_flash_ntf_err(fw_update->dev,
194 						      &fw_update->ntf_params,
195 						      "Write FW block LPL command failed",
196 						      args.err_msg);
197 			return err;
198 		}
199 	}
200 
201 	return 0;
202 }
203 
204 static int
cmis_fw_update_complete_download(struct ethtool_cmis_cdb * cdb,struct net_device * dev,struct cmis_fw_update_fw_mng_features * fw_mng,struct ethnl_module_fw_flash_ntf_params * ntf_params)205 cmis_fw_update_complete_download(struct ethtool_cmis_cdb *cdb,
206 				 struct net_device *dev,
207 				 struct cmis_fw_update_fw_mng_features *fw_mng,
208 				 struct ethnl_module_fw_flash_ntf_params *ntf_params)
209 {
210 	struct ethtool_cmis_cdb_cmd_args args = {};
211 	int err;
212 
213 	ethtool_cmis_cdb_compose_args(&args,
214 				      ETHTOOL_CMIS_CDB_CMD_COMPLETE_FW_DOWNLOAD,
215 				      NULL, 0, fw_mng->max_duration_complete,
216 				      cdb->read_write_len_ext, 1000, 0,
217 				      CDB_F_COMPLETION_VALID | CDB_F_STATUS_VALID);
218 
219 	err = ethtool_cmis_cdb_execute_cmd(dev, &args);
220 	if (err < 0)
221 		ethnl_module_fw_flash_ntf_err(dev, ntf_params,
222 					      "Complete FW download command failed",
223 					      args.err_msg);
224 
225 	return err;
226 }
227 
228 static int
cmis_fw_update_download_image(struct ethtool_cmis_cdb * cdb,struct ethtool_cmis_fw_update_params * fw_update,struct cmis_fw_update_fw_mng_features * fw_mng)229 cmis_fw_update_download_image(struct ethtool_cmis_cdb *cdb,
230 			      struct ethtool_cmis_fw_update_params *fw_update,
231 			      struct cmis_fw_update_fw_mng_features *fw_mng)
232 {
233 	int err;
234 
235 	err = cmis_fw_update_start_download(cdb, fw_update, fw_mng);
236 	if (err < 0)
237 		return err;
238 
239 	err = cmis_fw_update_write_image(cdb, fw_update, fw_mng);
240 	if (err < 0)
241 		return err;
242 
243 	err = cmis_fw_update_complete_download(cdb, fw_update->dev, fw_mng,
244 					       &fw_update->ntf_params);
245 	if (err < 0)
246 		return err;
247 
248 	return 0;
249 }
250 
251 enum {
252 	CMIS_MODULE_LOW_PWR	= 1,
253 	CMIS_MODULE_READY	= 3,
254 };
255 
module_is_ready(u8 data)256 static bool module_is_ready(u8 data)
257 {
258 	u8 state = (data >> 1) & 7;
259 
260 	return state == CMIS_MODULE_READY || state == CMIS_MODULE_LOW_PWR;
261 }
262 
263 #define CMIS_MODULE_READY_MAX_DURATION_MSEC	1000
264 #define CMIS_MODULE_STATE_OFFSET		3
265 
266 static int
cmis_fw_update_wait_for_module_state(struct net_device * dev,u8 flags)267 cmis_fw_update_wait_for_module_state(struct net_device *dev, u8 flags)
268 {
269 	u8 state;
270 
271 	return ethtool_cmis_wait_for_cond(dev, flags, CDB_F_MODULE_STATE_VALID,
272 					  CMIS_MODULE_READY_MAX_DURATION_MSEC,
273 					  CMIS_MODULE_STATE_OFFSET,
274 					  module_is_ready, NULL, &state);
275 }
276 
277 /* See section 9.7.10 "CMD 0109h: Run Firmware Image" in CMIS standard
278  * revision 5.2.
279  * struct cmis_cdb_run_fw_image_pl is a structured layout of the flat
280  * array, ethtool_cmis_cdb_request::payload.
281  */
282 struct cmis_cdb_run_fw_image_pl {
283 	u8 resv1;
284 	u8 image_to_run;
285 	u16 delay_to_reset;
286 };
287 
288 static int
cmis_fw_update_run_image(struct ethtool_cmis_cdb * cdb,struct net_device * dev,struct ethnl_module_fw_flash_ntf_params * ntf_params)289 cmis_fw_update_run_image(struct ethtool_cmis_cdb *cdb, struct net_device *dev,
290 			 struct ethnl_module_fw_flash_ntf_params *ntf_params)
291 {
292 	struct ethtool_cmis_cdb_cmd_args args = {};
293 	struct cmis_cdb_run_fw_image_pl pl = {0};
294 	int err;
295 
296 	ethtool_cmis_cdb_compose_args(&args, ETHTOOL_CMIS_CDB_CMD_RUN_FW_IMAGE,
297 				      (u8 *)&pl, sizeof(pl),
298 				      cdb->max_completion_time,
299 				      cdb->read_write_len_ext, 1000, 0,
300 				      CDB_F_MODULE_STATE_VALID);
301 
302 	err = ethtool_cmis_cdb_execute_cmd(dev, &args);
303 	if (err < 0) {
304 		ethnl_module_fw_flash_ntf_err(dev, ntf_params,
305 					      "Run image command failed",
306 					      args.err_msg);
307 		return err;
308 	}
309 
310 	err = cmis_fw_update_wait_for_module_state(dev, args.flags);
311 	if (err < 0)
312 		ethnl_module_fw_flash_ntf_err(dev, ntf_params,
313 					      "Module is not ready on time after reset",
314 					      NULL);
315 
316 	return err;
317 }
318 
319 static int
cmis_fw_update_commit_image(struct ethtool_cmis_cdb * cdb,struct net_device * dev,struct ethnl_module_fw_flash_ntf_params * ntf_params)320 cmis_fw_update_commit_image(struct ethtool_cmis_cdb *cdb,
321 			    struct net_device *dev,
322 			    struct ethnl_module_fw_flash_ntf_params *ntf_params)
323 {
324 	struct ethtool_cmis_cdb_cmd_args args = {};
325 	int err;
326 
327 	ethtool_cmis_cdb_compose_args(&args,
328 				      ETHTOOL_CMIS_CDB_CMD_COMMIT_FW_IMAGE,
329 				      NULL, 0, cdb->max_completion_time,
330 				      cdb->read_write_len_ext, 1000, 0,
331 				      CDB_F_COMPLETION_VALID | CDB_F_STATUS_VALID);
332 
333 	err = ethtool_cmis_cdb_execute_cmd(dev, &args);
334 	if (err < 0)
335 		ethnl_module_fw_flash_ntf_err(dev, ntf_params,
336 					      "Commit image command failed",
337 					      args.err_msg);
338 
339 	return err;
340 }
341 
cmis_fw_update_reset(struct net_device * dev)342 static int cmis_fw_update_reset(struct net_device *dev)
343 {
344 	__u32 reset_data = ETH_RESET_PHY;
345 
346 	return dev->ethtool_ops->reset(dev, &reset_data);
347 }
348 
349 void
ethtool_cmis_fw_update(struct ethtool_cmis_fw_update_params * fw_update)350 ethtool_cmis_fw_update(struct ethtool_cmis_fw_update_params *fw_update)
351 {
352 	struct ethnl_module_fw_flash_ntf_params *ntf_params =
353 						&fw_update->ntf_params;
354 	struct cmis_fw_update_fw_mng_features fw_mng = {0};
355 	struct net_device *dev = fw_update->dev;
356 	struct ethtool_cmis_cdb *cdb;
357 	int err;
358 
359 	cdb = ethtool_cmis_cdb_init(dev, &fw_update->params, ntf_params);
360 	if (IS_ERR(cdb))
361 		goto err_send_ntf;
362 
363 	ethnl_module_fw_flash_ntf_start(dev, ntf_params);
364 
365 	err = cmis_fw_update_fw_mng_features_get(cdb, dev, &fw_mng, ntf_params);
366 	if (err < 0)
367 		goto err_cdb_fini;
368 
369 	err = cmis_fw_update_download_image(cdb, fw_update, &fw_mng);
370 	if (err < 0)
371 		goto err_cdb_fini;
372 
373 	err = cmis_fw_update_run_image(cdb, dev, ntf_params);
374 	if (err < 0)
375 		goto err_cdb_fini;
376 
377 	/* The CDB command "Run Firmware Image" resets the firmware, so the new
378 	 * one might have different settings.
379 	 * Free the old CDB instance, and init a new one.
380 	 */
381 	ethtool_cmis_cdb_fini(cdb);
382 
383 	cdb = ethtool_cmis_cdb_init(dev, &fw_update->params, ntf_params);
384 	if (IS_ERR(cdb))
385 		goto err_send_ntf;
386 
387 	err = cmis_fw_update_commit_image(cdb, dev, ntf_params);
388 	if (err < 0)
389 		goto err_cdb_fini;
390 
391 	err = cmis_fw_update_reset(dev);
392 	if (err < 0)
393 		goto err_cdb_fini;
394 
395 	ethnl_module_fw_flash_ntf_complete(dev, ntf_params);
396 	ethtool_cmis_cdb_fini(cdb);
397 	return;
398 
399 err_cdb_fini:
400 	ethtool_cmis_cdb_fini(cdb);
401 err_send_ntf:
402 	ethnl_module_fw_flash_ntf_err(dev, ntf_params, NULL, NULL);
403 }
404