Linux Audio

Check our new training course

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