203 lines
5.3 KiB
Diff
203 lines
5.3 KiB
Diff
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, ¶ms, &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;
|
|
}
|
|
|