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 +#include #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; }