Nothing lives outside the repo: the bench ixgbe driver source moves in tracked at kernel/ixgbe-sff/ (build artifacts gitignored, load-ixgbe repointed, module build verified from the new home — the reg_ops/ts_bench bench extras are finally versioned), the ice sff_i2c patch is vendored at docs/nics/e810/ice-sff-i2c.patch with its out-of-tree build recipe (TRACE_INCLUDE_PATH fixup, version-matched Makefile), the e810 NVM flash logistics land in the e810 README (exact package name, rollback -m B49691D578EC into the inactive bank, post-flash inventory failing until power cycle is the expected tell), and every ~/work/phydiag-work pointer becomes an inline statement; phydiag-work, ice-sff, e810nvm and the wiitekphy CLI (a duplicate of documented transport+protocol) are deleted

This commit is contained in:
flamingcow
2026-08-19 16:27:48 -07:00
parent c3569b5625
commit e3de27a251
54 changed files with 56360 additions and 19 deletions
+3 -3
View File
@@ -1,6 +1,6 @@
# Intel E810 (ice)
Out of the box. The measurement path was originally built against it — its datapath delivers per-packet MAC RX timestamps (`rx_filter=ALL`), which the committed rate design no longer needs ([../README.md](../README.md)). A patched ice + `sff_i2c` exists for read-side diagnostics work.
Out of the box. The measurement path was originally built against it — its datapath delivers per-packet MAC RX timestamps (`rx_filter=ALL`), which the committed rate design no longer needs ([../README.md](../README.md)). The `sff_i2c` ice patch for read-side diagnostics is vendored here as [ice-sff-i2c.patch](ice-sff-i2c.patch); to run it, apply to a copy of the kernel's ice source and build out of tree (`make -C /usr/lib/modules/$(uname -r)/build M=<dir> modules`, then `rmmod irdma ice; insmod`), with two out-of-tree fixups: `ice_trace.h` needs `TRACE_INCLUDE_PATH` set to the build dir's absolute path and `TRACE_INCLUDE_FILE ice_trace`, and the Makefile's object list must match the source snapshot's kernel version.
## Host notes (if it returns)
@@ -26,8 +26,8 @@ Read-side transport details (the patched ice exists for read-side work, so these
- No offset-less read mode exists, so the plain mdio-i2c clause-45 read phase (bcm84881-style at 0x56) cannot be formed. RollBall fits fully (≤4 B native multi-byte writes).
- Latency is the wire-truth judge on this transport: on 3.10 firmware, silently-dropped 1-byte writes completed in ~63 µs while real wire traffic ran ~103 µs/byte (reads) and multi-ms ack-polling (writes), and `ok` returns even for writes at an empty address — protocol-level readback is the only delivery confirmation.
Firmware on the card: NVM 5.01 (release 31.2.2, EETRACK 0x80021FA2). The update package and logs live in `~/work/e810nvm/`; `nvmupdate64e` needs `-AllowMissingCert` on this platform (harmless — nothing boots from the NIC), writes no file backup, and rollback lives in the card's inactive flash bank (`nvmupdate64e -r -m <MAC>`).
Firmware on the card: NVM 5.01 (release 31.2.2, EETRACK 0x80021FA2), flashed with `nvmupdate64e` 1.43.37.1a from Intel's `E810_NVMUpdatePackage_v5_01_Linux.tar.gz` (re-downloadable; nothing kept locally). Flash logistics: the tool needs `-AllowMissingCert` on this platform (the MS UEFI CA 2023 cert is absent — harmless, nothing boots from the NIC), writes no file backup even with `-b` ("Backup images created" means the card's inactive flash bank, where rollback lives: `nvmupdate64e -r -m B49691D578EC`), and the post-flash inventory fails until a power cycle activates the new bank — that failure is the expected tell, not an error.
## The bit-bang-below-the-AQ idea — retired with the timestamp requirement
The write block is at the **Admin Queue** (firmware) level — the `0x06E2/E3` topo-I2C commands are refused with EPERM. That is not the same as the module's SDA/SCL being unreachable: the 82599 reaches the module I2C **without firmware** by toggling SDA/SCL as GPIO bits in `I2CCTL` (the `~/work/ixgbe-sff/` patch bit-bangs arbitrary START…STOP from those primitives), and whether the E810 exposes an equivalent register-level / SDP-GPIO path below the AQ policy was never tested. The idea existed to marry all-packet stamps to raw I2C in one card; the smoothed-bucket rate plan ([../x520/README.md](../x520/README.md)) dissolves the stamps requirement, so the X520 covers everything and this investigation is not needed. It stays recorded as the E810's one open door if per-frame arrival times are ever wanted again — the investigation would start at the datasheet's SDP/GPIO and low-level I2C controller registers, asking whether the cage's SDA/SCL are host-addressable.
The write block is at the **Admin Queue** (firmware) level — the `0x06E2/E3` topo-I2C commands are refused with EPERM. That is not the same as the module's SDA/SCL being unreachable: the 82599 reaches the module I2C **without firmware** by toggling SDA/SCL as GPIO bits in `I2CCTL` (the `kernel/ixgbe-sff/` bench driver bit-bangs arbitrary START…STOP from those primitives), and whether the E810 exposes an equivalent register-level / SDP-GPIO path below the AQ policy was never tested. The idea existed to marry all-packet stamps to raw I2C in one card; the smoothed-bucket rate plan ([../x520/README.md](../x520/README.md)) dissolves the stamps requirement, so the X520 covers everything and this investigation is not needed. It stays recorded as the E810's one open door if per-frame arrival times are ever wanted again — the investigation would start at the datasheet's SDP/GPIO and low-level I2C controller registers, asking whether the cage's SDA/SCL are host-addressable.
+202
View File
@@ -0,0 +1,202 @@
diff --git a/drivers/net/ethernet/intel/ice/ice_debugfs.c b/drivers/net/ethernet/intel/ice/ice_debugfs.c
index f450250fc..f0205e58a 100644
--- a/drivers/net/ethernet/intel/ice/ice_debugfs.c
+++ b/drivers/net/ethernet/intel/ice/ice_debugfs.c
@@ -2,10 +2,187 @@
/* Copyright (c) 2022, Intel Corporation. */
#include <linux/debugfs.h>
+#include <linux/uaccess.h>
#include "ice.h"
static struct dentry *ice_debugfs_root;
+#define ICE_SFF_I2C_MAX_DATA 16
+
+struct ice_sff_i2c_ctx {
+ struct ice_pf *pf;
+ char resp[ICE_SFF_I2C_MAX_DATA * 3 + 32];
+ size_t resp_len;
+};
+
+static int ice_sff_i2c_open(struct inode *inode, struct file *filp)
+{
+ struct ice_sff_i2c_ctx *ctx;
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->pf = inode->i_private;
+ filp->private_data = ctx;
+
+ return 0;
+}
+
+static int ice_sff_i2c_release(struct inode *inode, struct file *filp)
+{
+ kfree(filp->private_data);
+
+ return 0;
+}
+
+static ssize_t ice_sff_i2c_write(struct file *filp, const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct ice_sff_i2c_ctx *ctx = filp->private_data;
+ struct ice_hw *hw = &ctx->pf->hw;
+ u8 data[ICE_SFF_I2C_MAX_DATA] = {};
+ u8 bus, off, arg;
+ char cmd[64];
+ char op;
+ int ret;
+
+ if (!count || count >= sizeof(cmd))
+ return -EINVAL;
+ if (copy_from_user(cmd, ubuf, count))
+ return -EFAULT;
+ cmd[count] = '\0';
+
+ switch (cmd[0]) {
+ case 'r':
+ if (sscanf(cmd, "%c %hhx %hhx %hhx", &op, &bus, &off, &arg) != 4)
+ return -EINVAL;
+ if (arg < 1 || arg > ICE_SFF_I2C_MAX_DATA)
+ return -EINVAL;
+ ret = ice_aq_sff_eeprom(hw, 0, bus, off, 0, 0, data, arg,
+ false, NULL);
+ break;
+ case 'w':
+ if (sscanf(cmd, "%c %hhx %hhx %hhx", &op, &bus, &off, &arg) != 4)
+ return -EINVAL;
+ data[0] = arg;
+ ret = ice_aq_sff_eeprom(hw, 0, bus, off, 0, 0, data, 1,
+ true, NULL);
+ break;
+ case 't': {
+ struct ice_aqc_get_link_topo tcmd = {};
+ u8 part;
+ u16 handle;
+
+ if (sscanf(cmd, "%c %hhx %hhx", &op,
+ &tcmd.addr.topo_params.node_type_ctx,
+ &tcmd.addr.topo_params.index) != 3)
+ return -EINVAL;
+ ret = ice_aq_get_netlist_node(hw, &tcmd, &part, &handle);
+ if (!ret) {
+ ctx->resp_len = scnprintf(ctx->resp, sizeof(ctx->resp),
+ "ok %02x %04x\n", part,
+ handle);
+ *ppos = 0;
+ return count;
+ }
+ break;
+ }
+ case 'i': case 'j': {
+ struct ice_aqc_link_topo_addr topo = {};
+ u16 bus16, addr16, handle;
+ u8 params, n;
+ int fields;
+
+ fields = sscanf(cmd,
+ "%c %hhx %hhx %hx %hx %hx %hhx %hhx %hhx %hhx %hhx",
+ &op, &topo.topo_params.node_type_ctx,
+ &topo.topo_params.index, &handle, &bus16,
+ &addr16, &params, &data[0], &data[1], &data[2],
+ &data[3]);
+ if (fields < 7)
+ return -EINVAL;
+ topo.handle = cpu_to_le16(handle);
+ n = params & ICE_AQC_I2C_DATA_SIZE_M;
+ arg = n;
+ if (op == 'i') {
+ if (fields != 7 || n < 1)
+ return -EINVAL;
+ ret = ice_aq_read_i2c(hw, topo, bus16,
+ cpu_to_le16(addr16), params,
+ data, NULL);
+ } else {
+ if (n < 1 || n > 4 || fields != 7 + n)
+ return -EINVAL;
+ ret = ice_aq_write_i2c(hw, topo, bus16,
+ cpu_to_le16(addr16), params,
+ data, NULL);
+ }
+ op = op == 'i' ? 'r' : 'w';
+ break;
+ }
+ case 'x': {
+ struct libie_aq_desc desc = {};
+ u16 opcode, flags;
+ u32 p[4] = {};
+
+ if (sscanf(cmd, "%c %hx %hx %x %x %x %x", &op, &flags, &opcode,
+ &p[0], &p[1], &p[2], &p[3]) != 7)
+ return -EINVAL;
+ desc.flags = cpu_to_le16(flags);
+ desc.opcode = cpu_to_le16(opcode);
+ desc.params.generic.param0 = cpu_to_le32(p[0]);
+ desc.params.generic.param1 = cpu_to_le32(p[1]);
+ desc.params.generic.addr_high = cpu_to_le32(p[2]);
+ desc.params.generic.addr_low = cpu_to_le32(p[3]);
+ ret = ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
+ ctx->resp_len = scnprintf(ctx->resp, sizeof(ctx->resp),
+ "%s %d aq %d wb %08x %08x %08x %08x\n",
+ ret ? "err" : "ok", ret,
+ hw->adminq.sq_last_status,
+ le32_to_cpu(desc.params.generic.param0),
+ le32_to_cpu(desc.params.generic.param1),
+ le32_to_cpu(desc.params.generic.addr_high),
+ le32_to_cpu(desc.params.generic.addr_low));
+ *ppos = 0;
+ return count;
+ }
+ default:
+ return -EINVAL;
+ }
+
+ if (ret)
+ ctx->resp_len = scnprintf(ctx->resp, sizeof(ctx->resp),
+ "err %d aq %d\n", ret,
+ hw->adminq.sq_last_status);
+ else if (op == 'r')
+ ctx->resp_len = scnprintf(ctx->resp, sizeof(ctx->resp),
+ "ok %*ph\n", (int)arg, data);
+ else
+ ctx->resp_len = scnprintf(ctx->resp, sizeof(ctx->resp), "ok\n");
+
+ *ppos = 0;
+
+ return count;
+}
+
+static ssize_t ice_sff_i2c_read(struct file *filp, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct ice_sff_i2c_ctx *ctx = filp->private_data;
+
+ return simple_read_from_buffer(ubuf, count, ppos, ctx->resp,
+ ctx->resp_len);
+}
+
+static const struct file_operations ice_sff_i2c_fops = {
+ .owner = THIS_MODULE,
+ .open = ice_sff_i2c_open,
+ .release = ice_sff_i2c_release,
+ .read = ice_sff_i2c_read,
+ .write = ice_sff_i2c_write,
+};
+
int ice_debugfs_pf_init(struct ice_pf *pf)
{
const char *name = pci_name(pf->pdev);
@@ -14,6 +191,9 @@ int ice_debugfs_pf_init(struct ice_pf *pf)
if (IS_ERR(pf->ice_debugfs_pf))
return PTR_ERR(pf->ice_debugfs_pf);
+ debugfs_create_file("sff_i2c", 0600, pf->ice_debugfs_pf, pf,
+ &ice_sff_i2c_fops);
+
return 0;
}