~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/net/ethtool/cmis_fw_update.c

Version: ~ [ linux-6.11-rc3 ] ~ [ linux-6.10.4 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.45 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.104 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.164 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.223 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.281 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.319 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php