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(cmis_rev: cdb->cmis_rev, flags: &flags);
59 ethtool_cmis_cdb_compose_args(args: &args,
60 cmd: ETHTOOL_CMIS_CDB_CMD_FW_MANAGMENT_FEATURES,
61 NULL, lpl_len: 0, NULL, epl_len: 0,
62 max_duration: cdb->max_completion_time,
63 read_write_len_ext: cdb->read_write_len_ext, msleep_pre_rpl: 1000,
64 rpl_exp_len: sizeof(*rpl), flags);
65
66 err = ethtool_cmis_cdb_execute_cmd(dev, args: &args);
67 if (err < 0) {
68 ethnl_module_fw_flash_ntf_err(dev, params: ntf_params,
69 err_msg: "FW Management Features command failed",
70 sub_err_msg: 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, params: ntf_params,
77 err_msg: "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(to: pl.vendor_data, from: fw_update->fw->data, len: 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: &args,
132 cmd: ETHTOOL_CMIS_CDB_CMD_START_FW_DOWNLOAD,
133 lpl: (u8 *)&pl, lpl_len, NULL, epl_len: 0,
134 max_duration: fw_mng->max_duration_start,
135 read_write_len_ext: cdb->read_write_len_ext, msleep_pre_rpl: 1000, rpl_exp_len: 0,
136 CDB_F_COMPLETION_VALID | CDB_F_STATUS_VALID);
137
138 err = ethtool_cmis_cdb_execute_cmd(dev: fw_update->dev, args: &args);
139 if (err < 0)
140 ethnl_module_fw_flash_ntf_err(dev: fw_update->dev,
141 params: &fw_update->ntf_params,
142 err_msg: "Start FW download command failed",
143 sub_err_msg: 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(dev: fw_update->dev,
183 params: &fw_update->ntf_params,
184 done: offset - start,
185 total: image_size);
186 block_size = min_t(u32, max_block_size, image_size - offset);
187 memcpy(to: pl.fw_block, from: &fw_update->fw->data[offset], len: 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: &args,
193 cmd: ETHTOOL_CMIS_CDB_CMD_WRITE_FW_BLOCK_LPL,
194 lpl: (u8 *)&pl, lpl_len, NULL, epl_len: 0,
195 max_duration: fw_mng->max_duration_write,
196 read_write_len_ext: cdb->read_write_len_ext, msleep_pre_rpl: 1, rpl_exp_len: 0,
197 CDB_F_COMPLETION_VALID | CDB_F_STATUS_VALID);
198
199 err = ethtool_cmis_cdb_execute_cmd(dev: fw_update->dev, args: &args);
200 if (err < 0) {
201 ethnl_module_fw_flash_ntf_err(dev: fw_update->dev,
202 params: &fw_update->ntf_params,
203 err_msg: "Write FW block LPL command failed",
204 sub_err_msg: 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(dev: fw_update->dev,
239 params: &fw_update->ntf_params,
240 done: offset - start,
241 total: 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(to: epl->fw_block, from: &fw_update->fw->data[offset], len: epl_len);
250
251 ethtool_cmis_cdb_compose_args(args: &args,
252 cmd: ETHTOOL_CMIS_CDB_CMD_WRITE_FW_BLOCK_EPL,
253 lpl: (u8 *)&lpl, lpl_len, epl: (u8 *)epl,
254 epl_len,
255 max_duration: fw_mng->max_duration_write,
256 read_write_len_ext: cdb->read_write_len_ext, msleep_pre_rpl: 1, rpl_exp_len: 0,
257 CDB_F_COMPLETION_VALID | CDB_F_STATUS_VALID);
258
259 err = ethtool_cmis_cdb_execute_cmd(dev: fw_update->dev, args: &args);
260 kfree(objp: epl);
261 if (err < 0) {
262 ethnl_module_fw_flash_ntf_err(dev: fw_update->dev,
263 params: &fw_update->ntf_params,
264 err_msg: "Write FW block EPL command failed",
265 sub_err_msg: 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: &args,
283 cmd: ETHTOOL_CMIS_CDB_CMD_COMPLETE_FW_DOWNLOAD,
284 NULL, lpl_len: 0, NULL, epl_len: 0,
285 max_duration: fw_mng->max_duration_complete,
286 read_write_len_ext: cdb->read_write_len_ext, msleep_pre_rpl: 1000, rpl_exp_len: 0,
287 CDB_F_COMPLETION_VALID | CDB_F_STATUS_VALID);
288
289 err = ethtool_cmis_cdb_execute_cmd(dev, args: &args);
290 if (err < 0)
291 ethnl_module_fw_flash_ntf_err(dev, params: ntf_params,
292 err_msg: "Complete FW download command failed",
293 sub_err_msg: 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, dev: fw_update->dev, fw_mng,
320 ntf_params: &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 cond_success: module_is_ready, NULL, state: &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: &args, cmd: ETHTOOL_CMIS_CDB_CMD_RUN_FW_IMAGE,
373 lpl: (u8 *)&pl, lpl_len: sizeof(pl), NULL, epl_len: 0,
374 max_duration: cdb->max_completion_time,
375 read_write_len_ext: cdb->read_write_len_ext, msleep_pre_rpl: 1000, rpl_exp_len: 0,
376 CDB_F_MODULE_STATE_VALID);
377
378 err = ethtool_cmis_cdb_execute_cmd(dev, args: &args);
379 if (err < 0) {
380 ethnl_module_fw_flash_ntf_err(dev, params: ntf_params,
381 err_msg: "Run image command failed",
382 sub_err_msg: args.err_msg);
383 return err;
384 }
385
386 err = cmis_fw_update_wait_for_module_state(dev, flags: args.flags);
387 if (err < 0)
388 ethnl_module_fw_flash_ntf_err(dev, params: ntf_params,
389 err_msg: "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: &args,
404 cmd: ETHTOOL_CMIS_CDB_CMD_COMMIT_FW_IMAGE,
405 NULL, lpl_len: 0, NULL, epl_len: 0,
406 max_duration: cdb->max_completion_time,
407 read_write_len_ext: cdb->read_write_len_ext, msleep_pre_rpl: 1000, rpl_exp_len: 0,
408 CDB_F_COMPLETION_VALID | CDB_F_STATUS_VALID);
409
410 err = ethtool_cmis_cdb_execute_cmd(dev, args: &args);
411 if (err < 0)
412 ethnl_module_fw_flash_ntf_err(dev, params: ntf_params,
413 err_msg: "Commit image command failed",
414 sub_err_msg: 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, params: &fw_update->params, ntf_params);
442 if (IS_ERR(ptr: cdb))
443 goto err_send_ntf;
444
445 ethnl_module_fw_flash_ntf_start(dev, params: ntf_params);
446
447 err = cmis_fw_update_fw_mng_features_get(cdb, dev, fw_mng: &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: &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, params: &fw_update->params, ntf_params);
466 if (IS_ERR(ptr: 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, params: 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, params: ntf_params, NULL, NULL);
485}
486