Skip to content

Commit 7dc85b5

Browse files
committed
Merge branch 'ethtool-eeprom'
Moshe Shemesh says: ==================== ethtool: Extend module EEPROM dump API Ethtool supports module EEPROM dumps via the `ethtool -m <dev>` command. But in current state its functionality is limited - offset and length parameters, which are used to specify a linear desired region of EEPROM data to dump, is not enough, considering emergence of complex module EEPROM layouts such as CMIS 4.0. Moreover, CMIS 4.0 extends the amount of pages that may be accessible by introducing another parameter for page addressing - banks. Besides, currently module EEPROM is represented as a chunk of concatenated pages, where lower 128 bytes of all pages, except page 00h, are omitted. Offset and length are used to address parts of this fake linear memory. But in practice drivers, which implement get_module_info() and get_module_eeprom() ethtool ops still calculate page number and set I2C address on their own. This series tackles these issues by adding ethtool op, which allows to pass page number, bank number and I2C address in addition to offset and length parameters to the driver, adds corresponding netlink infrastructure and implements the new interface in mlx5 driver. This allows to extend userspace 'ethtool -m' CLI by adding new parameters - page, bank and i2c. New command line format: ethtool -m <dev> [hex on|off] [raw on|off] [offset N] [length N] [page N] [bank N] [i2c N] The consequence of this series is a possibility to dump arbitrary EEPROM page at a time, in contrast to dumps of concatenated pages. Therefore, offset and length change their semantics and may be used only to specify a part of data within half page boundary, which size is currently limited to 128 bytes. As for drivers that support legacy get_module_info() and get_module_eeprom() pair, the series addresses it by implementing a fallback mechanism. As mentioned earlier, such drivers derive a page number from 'global' offset, so this can be done vice versa without their involvement thanks to standardization. If kernel netlink handler of 'ethtool -m' command detects that new ethtool op is not supported by the driver, it calculates offset from given page number and page offset and calls old ndos, if they are available. ==================== \Signed-off-by: David S. Miller <[email protected]>
2 parents cbd3125 + c97a31f commit 7dc85b5

File tree

16 files changed

+553
-39
lines changed

16 files changed

+553
-39
lines changed

Documentation/networking/ethtool-netlink.rst

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1338,6 +1338,38 @@ in an implementation specific way.
13381338
``ETHTOOL_A_FEC_AUTO`` requests the driver to choose FEC mode based on SFP
13391339
module parameters. This does not mean autonegotiation.
13401340

1341+
MODULE_EEPROM
1342+
=============
1343+
1344+
Fetch module EEPROM data dump.
1345+
This interface is designed to allow dumps of at most 1/2 page at once. This
1346+
means only dumps of 128 (or less) bytes are allowed, without crossing half page
1347+
boundary located at offset 128. For pages other than 0 only high 128 bytes are
1348+
accessible.
1349+
1350+
Request contents:
1351+
1352+
======================================= ====== ==========================
1353+
``ETHTOOL_A_MODULE_EEPROM_HEADER`` nested request header
1354+
``ETHTOOL_A_MODULE_EEPROM_OFFSET`` u32 offset within a page
1355+
``ETHTOOL_A_MODULE_EEPROM_LENGTH`` u32 amount of bytes to read
1356+
``ETHTOOL_A_MODULE_EEPROM_PAGE`` u8 page number
1357+
``ETHTOOL_A_MODULE_EEPROM_BANK`` u8 bank number
1358+
``ETHTOOL_A_MODULE_EEPROM_I2C_ADDRESS`` u8 page I2C address
1359+
======================================= ====== ==========================
1360+
1361+
Kernel response contents:
1362+
1363+
+---------------------------------------------+--------+---------------------+
1364+
| ``ETHTOOL_A_MODULE_EEPROM_HEADER`` | nested | reply header |
1365+
+---------------------------------------------+--------+---------------------+
1366+
| ``ETHTOOL_A_MODULE_EEPROM_DATA`` | nested | array of bytes from |
1367+
| | | module EEPROM |
1368+
+---------------------------------------------+--------+---------------------+
1369+
1370+
``ETHTOOL_A_MODULE_EEPROM_DATA`` has an attribute length equal to the amount of
1371+
bytes driver actually read.
1372+
13411373
Request translation
13421374
===================
13431375

@@ -1415,8 +1447,8 @@ are netlink only.
14151447
``ETHTOOL_GET_DUMP_FLAG`` n/a
14161448
``ETHTOOL_GET_DUMP_DATA`` n/a
14171449
``ETHTOOL_GET_TS_INFO`` ``ETHTOOL_MSG_TSINFO_GET``
1418-
``ETHTOOL_GMODULEINFO`` n/a
1419-
``ETHTOOL_GMODULEEEPROM`` n/a
1450+
``ETHTOOL_GMODULEINFO`` ``ETHTOOL_MSG_MODULE_EEPROM_GET``
1451+
``ETHTOOL_GMODULEEEPROM`` ``ETHTOOL_MSG_MODULE_EEPROM_GET``
14201452
``ETHTOOL_GEEE`` ``ETHTOOL_MSG_EEE_GET``
14211453
``ETHTOOL_SEEE`` ``ETHTOOL_MSG_EEE_SET``
14221454
``ETHTOOL_GRSSH`` n/a

drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1770,6 +1770,49 @@ static int mlx5e_get_module_eeprom(struct net_device *netdev,
17701770
return 0;
17711771
}
17721772

1773+
static int mlx5e_get_module_eeprom_by_page(struct net_device *netdev,
1774+
const struct ethtool_module_eeprom *page_data,
1775+
struct netlink_ext_ack *extack)
1776+
{
1777+
struct mlx5e_priv *priv = netdev_priv(netdev);
1778+
struct mlx5_module_eeprom_query_params query;
1779+
struct mlx5_core_dev *mdev = priv->mdev;
1780+
u8 *data = page_data->data;
1781+
int size_read;
1782+
int i = 0;
1783+
1784+
if (!page_data->length)
1785+
return -EINVAL;
1786+
1787+
memset(data, 0, page_data->length);
1788+
1789+
query.offset = page_data->offset;
1790+
query.i2c_address = page_data->i2c_address;
1791+
query.bank = page_data->bank;
1792+
query.page = page_data->page;
1793+
while (i < page_data->length) {
1794+
query.size = page_data->length - i;
1795+
size_read = mlx5_query_module_eeprom_by_page(mdev, &query, data + i);
1796+
1797+
/* Done reading, return how many bytes was read */
1798+
if (!size_read)
1799+
return i;
1800+
1801+
if (size_read == -EINVAL)
1802+
return -EINVAL;
1803+
if (size_read < 0) {
1804+
netdev_err(priv->netdev, "%s: mlx5_query_module_eeprom_by_page failed:0x%x\n",
1805+
__func__, size_read);
1806+
return i;
1807+
}
1808+
1809+
i += size_read;
1810+
query.offset += size_read;
1811+
}
1812+
1813+
return i;
1814+
}
1815+
17731816
int mlx5e_ethtool_flash_device(struct mlx5e_priv *priv,
17741817
struct ethtool_flash *flash)
17751818
{
@@ -2159,6 +2202,7 @@ const struct ethtool_ops mlx5e_ethtool_ops = {
21592202
.set_wol = mlx5e_set_wol,
21602203
.get_module_info = mlx5e_get_module_info,
21612204
.get_module_eeprom = mlx5e_get_module_eeprom,
2205+
.get_module_eeprom_by_page = mlx5e_get_module_eeprom_by_page,
21622206
.flash_device = mlx5e_flash_device,
21632207
.get_priv_flags = mlx5e_get_priv_flags,
21642208
.set_priv_flags = mlx5e_set_priv_flags,

drivers/net/ethernet/mellanox/mlx5/core/port.c

Lines changed: 82 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -353,69 +353,123 @@ static void mlx5_sfp_eeprom_params_set(u16 *i2c_addr, int *page_num, u16 *offset
353353
*offset -= MLX5_EEPROM_PAGE_LENGTH;
354354
}
355355

356-
int mlx5_query_module_eeprom(struct mlx5_core_dev *dev,
357-
u16 offset, u16 size, u8 *data)
356+
static int mlx5_query_mcia(struct mlx5_core_dev *dev,
357+
struct mlx5_module_eeprom_query_params *params, u8 *data)
358358
{
359-
int module_num, status, err, page_num = 0;
360359
u32 in[MLX5_ST_SZ_DW(mcia_reg)] = {};
361360
u32 out[MLX5_ST_SZ_DW(mcia_reg)];
362-
u16 i2c_addr = 0;
363-
u8 module_id;
361+
int status, err;
364362
void *ptr;
363+
u16 size;
365364

366-
err = mlx5_query_module_num(dev, &module_num);
365+
size = min_t(int, params->size, MLX5_EEPROM_MAX_BYTES);
366+
367+
MLX5_SET(mcia_reg, in, l, 0);
368+
MLX5_SET(mcia_reg, in, size, size);
369+
MLX5_SET(mcia_reg, in, module, params->module_number);
370+
MLX5_SET(mcia_reg, in, device_address, params->offset);
371+
MLX5_SET(mcia_reg, in, page_number, params->page);
372+
MLX5_SET(mcia_reg, in, i2c_device_address, params->i2c_address);
373+
374+
err = mlx5_core_access_reg(dev, in, sizeof(in), out,
375+
sizeof(out), MLX5_REG_MCIA, 0, 0);
367376
if (err)
368377
return err;
369378

370-
err = mlx5_query_module_id(dev, module_num, &module_id);
379+
status = MLX5_GET(mcia_reg, out, status);
380+
if (status) {
381+
mlx5_core_err(dev, "query_mcia_reg failed: status: 0x%x\n",
382+
status);
383+
return -EIO;
384+
}
385+
386+
ptr = MLX5_ADDR_OF(mcia_reg, out, dword_0);
387+
memcpy(data, ptr, size);
388+
389+
return size;
390+
}
391+
392+
int mlx5_query_module_eeprom(struct mlx5_core_dev *dev,
393+
u16 offset, u16 size, u8 *data)
394+
{
395+
struct mlx5_module_eeprom_query_params query = {0};
396+
u8 module_id;
397+
int err;
398+
399+
err = mlx5_query_module_num(dev, &query.module_number);
400+
if (err)
401+
return err;
402+
403+
err = mlx5_query_module_id(dev, query.module_number, &module_id);
371404
if (err)
372405
return err;
373406

374407
switch (module_id) {
375408
case MLX5_MODULE_ID_SFP:
376-
mlx5_sfp_eeprom_params_set(&i2c_addr, &page_num, &offset);
409+
mlx5_sfp_eeprom_params_set(&query.i2c_address, &query.page, &query.offset);
377410
break;
378411
case MLX5_MODULE_ID_QSFP:
379412
case MLX5_MODULE_ID_QSFP_PLUS:
380413
case MLX5_MODULE_ID_QSFP28:
381-
mlx5_qsfp_eeprom_params_set(&i2c_addr, &page_num, &offset);
414+
mlx5_qsfp_eeprom_params_set(&query.i2c_address, &query.page, &query.offset);
382415
break;
383416
default:
384417
mlx5_core_err(dev, "Module ID not recognized: 0x%x\n", module_id);
385418
return -EINVAL;
386419
}
387420

388-
if (offset + size > MLX5_EEPROM_PAGE_LENGTH)
421+
if (query.offset + size > MLX5_EEPROM_PAGE_LENGTH)
389422
/* Cross pages read, read until offset 256 in low page */
390423
size -= offset + size - MLX5_EEPROM_PAGE_LENGTH;
391424

392-
size = min_t(int, size, MLX5_EEPROM_MAX_BYTES);
425+
query.size = size;
393426

394-
MLX5_SET(mcia_reg, in, l, 0);
395-
MLX5_SET(mcia_reg, in, module, module_num);
396-
MLX5_SET(mcia_reg, in, i2c_device_address, i2c_addr);
397-
MLX5_SET(mcia_reg, in, page_number, page_num);
398-
MLX5_SET(mcia_reg, in, device_address, offset);
399-
MLX5_SET(mcia_reg, in, size, size);
427+
return mlx5_query_mcia(dev, &query, data);
428+
}
429+
EXPORT_SYMBOL_GPL(mlx5_query_module_eeprom);
400430

401-
err = mlx5_core_access_reg(dev, in, sizeof(in), out,
402-
sizeof(out), MLX5_REG_MCIA, 0, 0);
431+
int mlx5_query_module_eeprom_by_page(struct mlx5_core_dev *dev,
432+
struct mlx5_module_eeprom_query_params *params,
433+
u8 *data)
434+
{
435+
u8 module_id;
436+
int err;
437+
438+
err = mlx5_query_module_num(dev, &params->module_number);
403439
if (err)
404440
return err;
405441

406-
status = MLX5_GET(mcia_reg, out, status);
407-
if (status) {
408-
mlx5_core_err(dev, "query_mcia_reg failed: status: 0x%x\n",
409-
status);
410-
return -EIO;
442+
err = mlx5_query_module_id(dev, params->module_number, &module_id);
443+
if (err)
444+
return err;
445+
446+
switch (module_id) {
447+
case MLX5_MODULE_ID_SFP:
448+
if (params->page > 0)
449+
return -EINVAL;
450+
break;
451+
case MLX5_MODULE_ID_QSFP:
452+
case MLX5_MODULE_ID_QSFP28:
453+
case MLX5_MODULE_ID_QSFP_PLUS:
454+
if (params->page > 3)
455+
return -EINVAL;
456+
break;
457+
case MLX5_MODULE_ID_DSFP:
458+
break;
459+
default:
460+
mlx5_core_err(dev, "Module ID not recognized: 0x%x\n", module_id);
461+
return -EINVAL;
411462
}
412463

413-
ptr = MLX5_ADDR_OF(mcia_reg, out, dword_0);
414-
memcpy(data, ptr, size);
464+
if (params->i2c_address != MLX5_I2C_ADDR_HIGH &&
465+
params->i2c_address != MLX5_I2C_ADDR_LOW) {
466+
mlx5_core_err(dev, "I2C address not recognized: 0x%x\n", params->i2c_address);
467+
return -EINVAL;
468+
}
415469

416-
return size;
470+
return mlx5_query_mcia(dev, params, data);
417471
}
418-
EXPORT_SYMBOL_GPL(mlx5_query_module_eeprom);
472+
EXPORT_SYMBOL_GPL(mlx5_query_module_eeprom_by_page);
419473

420474
static int mlx5_query_port_pvlc(struct mlx5_core_dev *dev, u32 *pvlc,
421475
int pvlc_size, u8 local_port)

drivers/net/phy/sfp-bus.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,26 @@ int sfp_get_module_eeprom(struct sfp_bus *bus, struct ethtool_eeprom *ee,
555555
}
556556
EXPORT_SYMBOL_GPL(sfp_get_module_eeprom);
557557

558+
/**
559+
* sfp_get_module_eeprom_by_page() - Read a page from the SFP module EEPROM
560+
* @bus: a pointer to the &struct sfp_bus structure for the sfp module
561+
* @page: a &struct ethtool_module_eeprom
562+
* @extack: extack for reporting problems
563+
*
564+
* Read an EEPROM page as specified by the supplied @page. See the
565+
* documentation for &struct ethtool_module_eeprom for the page to be read.
566+
*
567+
* Returns 0 on success or a negative errno number. More error
568+
* information might be provided via extack
569+
*/
570+
int sfp_get_module_eeprom_by_page(struct sfp_bus *bus,
571+
const struct ethtool_module_eeprom *page,
572+
struct netlink_ext_ack *extack)
573+
{
574+
return bus->socket_ops->module_eeprom_by_page(bus->sfp, page, extack);
575+
}
576+
EXPORT_SYMBOL_GPL(sfp_get_module_eeprom_by_page);
577+
558578
/**
559579
* sfp_upstream_start() - Inform the SFP that the network device is up
560580
* @bus: a pointer to the &struct sfp_bus structure for the sfp module

drivers/net/phy/sfp.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2330,13 +2330,38 @@ static int sfp_module_eeprom(struct sfp *sfp, struct ethtool_eeprom *ee,
23302330
return 0;
23312331
}
23322332

2333+
static int sfp_module_eeprom_by_page(struct sfp *sfp,
2334+
const struct ethtool_module_eeprom *page,
2335+
struct netlink_ext_ack *extack)
2336+
{
2337+
if (page->bank) {
2338+
NL_SET_ERR_MSG(extack, "Banks not supported");
2339+
return -EOPNOTSUPP;
2340+
}
2341+
2342+
if (page->page) {
2343+
NL_SET_ERR_MSG(extack, "Only page 0 supported");
2344+
return -EOPNOTSUPP;
2345+
}
2346+
2347+
if (page->i2c_address != 0x50 &&
2348+
page->i2c_address != 0x51) {
2349+
NL_SET_ERR_MSG(extack, "Only address 0x50 and 0x51 supported");
2350+
return -EOPNOTSUPP;
2351+
}
2352+
2353+
return sfp_read(sfp, page->i2c_address == 0x51, page->offset,
2354+
page->data, page->length);
2355+
};
2356+
23332357
static const struct sfp_socket_ops sfp_module_ops = {
23342358
.attach = sfp_attach,
23352359
.detach = sfp_detach,
23362360
.start = sfp_start,
23372361
.stop = sfp_stop,
23382362
.module_info = sfp_module_info,
23392363
.module_eeprom = sfp_module_eeprom,
2364+
.module_eeprom_by_page = sfp_module_eeprom_by_page,
23402365
};
23412366

23422367
static void sfp_timeout(struct work_struct *work)

drivers/net/phy/sfp.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ struct sfp_socket_ops {
1414
int (*module_info)(struct sfp *sfp, struct ethtool_modinfo *modinfo);
1515
int (*module_eeprom)(struct sfp *sfp, struct ethtool_eeprom *ee,
1616
u8 *data);
17+
int (*module_eeprom_by_page)(struct sfp *sfp,
18+
const struct ethtool_module_eeprom *page,
19+
struct netlink_ext_ack *extack);
1720
};
1821

1922
int sfp_add_phy(struct sfp_bus *bus, struct phy_device *phydev);

0 commit comments

Comments
 (0)