File Index
Files and directories
Sign in
FILE

cmis_fw_update.c

File preview with syntax highlighting.

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