Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/net/ethtool/cmis_fw_update.c
26285 views
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
11
struct 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
*/
24
struct 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
40
enum 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
47
static int
48
cmis_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
*/
105
struct 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
114
static int
115
cmis_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
*/
153
struct 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
158
static int
159
cmis_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
212
struct cmis_cdb_write_fw_block_epl_pl {
213
u8 fw_block[ETHTOOL_CMIS_CDB_EPL_MAX_PL_LENGTH];
214
};
215
216
static int
217
cmis_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
273
static int
274
cmis_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
298
static int
299
cmis_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
327
enum {
328
CMIS_MODULE_LOW_PWR = 1,
329
CMIS_MODULE_READY = 3,
330
};
331
332
static 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
342
static int
343
cmis_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
*/
358
struct cmis_cdb_run_fw_image_pl {
359
u8 resv1;
360
u8 image_to_run;
361
u16 delay_to_reset;
362
};
363
364
static int
365
cmis_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
395
static int
396
cmis_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
419
static 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
431
void
432
ethtool_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
481
err_cdb_fini:
482
ethtool_cmis_cdb_fini(cdb);
483
err_send_ntf:
484
ethnl_module_fw_flash_ntf_err(dev, ntf_params, NULL, NULL);
485
}
486
487