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