Add additional debugfs support for reading / writing registers of any
attached external phy devices as well as the SFP eeprom data.

These debugfs files will only be created if the phy implementation
supports them.

Signed-off-by: Tom Lendacky <thomas.lenda...@amd.com>
---
 drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c |  151 ++++++++++++++++++++++++++
 drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c  |   87 +++++++++++++++
 drivers/net/ethernet/amd/xgbe/xgbe.h         |   12 ++
 3 files changed, 250 insertions(+)

diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c 
b/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c
index 7546b66..7409705 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c
@@ -436,6 +436,126 @@ static ssize_t xi2c_reg_value_write(struct file *filp,
        .write = xi2c_reg_value_write,
 };
 
+static ssize_t sfp_eeprom_read(struct file *filp, char __user *buffer,
+                              size_t count, loff_t *ppos)
+{
+       struct xgbe_prv_data *pdata = filp->private_data;
+       struct xgbe_phy_impl_if *phy_impl;
+       unsigned char *eeprom;
+       ssize_t len;
+
+       phy_impl = &pdata->phy_if.phy_impl;
+       eeprom = phy_impl->sfp_eeprom(pdata);
+       if (!eeprom)
+               return 0;
+
+       len = simple_read_from_buffer(buffer, count, ppos,
+                                     eeprom, strlen(eeprom));
+
+       kfree(eeprom);
+
+       return len;
+}
+
+static const struct file_operations sfp_eeprom_fops = {
+       .owner = THIS_MODULE,
+       .open = simple_open,
+       .read = sfp_eeprom_read,
+};
+
+static ssize_t phydev_mmd_read(struct file *filp, char __user *buffer,
+                              size_t count, loff_t *ppos)
+{
+       struct xgbe_prv_data *pdata = filp->private_data;
+
+       return xgbe_common_read(buffer, count, ppos, pdata->debugfs_phydev_mmd);
+}
+
+static ssize_t phydev_mmd_write(struct file *filp, const char __user *buffer,
+                               size_t count, loff_t *ppos)
+{
+       struct xgbe_prv_data *pdata = filp->private_data;
+
+       return xgbe_common_write(buffer, count, ppos,
+                                &pdata->debugfs_phydev_mmd);
+}
+
+static ssize_t phydev_reg_addr_read(struct file *filp, char __user *buffer,
+                                   size_t count, loff_t *ppos)
+{
+       struct xgbe_prv_data *pdata = filp->private_data;
+
+       return xgbe_common_read(buffer, count, ppos, pdata->debugfs_phydev_reg);
+}
+
+static ssize_t phydev_reg_addr_write(struct file *filp,
+                                    const char __user *buffer,
+                                    size_t count, loff_t *ppos)
+{
+       struct xgbe_prv_data *pdata = filp->private_data;
+
+       return xgbe_common_write(buffer, count, ppos,
+                                &pdata->debugfs_phydev_reg);
+}
+
+static ssize_t phydev_reg_value_read(struct file *filp, char __user *buffer,
+                                    size_t count, loff_t *ppos)
+{
+       struct xgbe_prv_data *pdata = filp->private_data;
+       struct xgbe_phy_impl_if *phy_impl;
+       unsigned int value;
+
+       phy_impl = &pdata->phy_if.phy_impl;
+       value = phy_impl->phydev_read(pdata,
+                                     pdata->debugfs_phydev_mmd,
+                                     pdata->debugfs_phydev_reg);
+
+       return xgbe_common_read(buffer, count, ppos, value);
+}
+
+static ssize_t phydev_reg_value_write(struct file *filp,
+                                     const char __user *buffer,
+                                     size_t count, loff_t *ppos)
+{
+       struct xgbe_prv_data *pdata = filp->private_data;
+       struct xgbe_phy_impl_if *phy_impl;
+       unsigned int value;
+       ssize_t len;
+
+       len = xgbe_common_write(buffer, count, ppos, &value);
+       if (len < 0)
+               return len;
+
+       phy_impl = &pdata->phy_if.phy_impl;
+       phy_impl->phydev_write(pdata,
+                              pdata->debugfs_phydev_mmd,
+                              pdata->debugfs_phydev_reg,
+                              value);
+
+       return len;
+}
+
+static const struct file_operations phydev_mmd_fops = {
+       .owner = THIS_MODULE,
+       .open = simple_open,
+       .read = phydev_mmd_read,
+       .write = phydev_mmd_write,
+};
+
+static const struct file_operations phydev_reg_addr_fops = {
+       .owner = THIS_MODULE,
+       .open = simple_open,
+       .read = phydev_reg_addr_read,
+       .write = phydev_reg_addr_write,
+};
+
+static const struct file_operations phydev_reg_value_fops = {
+       .owner = THIS_MODULE,
+       .open = simple_open,
+       .read = phydev_reg_value_read,
+       .write = phydev_reg_value_write,
+};
+
 void xgbe_debugfs_init(struct xgbe_prv_data *pdata)
 {
        struct dentry *pfile;
@@ -445,6 +565,8 @@ void xgbe_debugfs_init(struct xgbe_prv_data *pdata)
        pdata->debugfs_xgmac_reg = 0;
        pdata->debugfs_xpcs_mmd = 1;
        pdata->debugfs_xpcs_reg = 0;
+       pdata->debugfs_phydev_mmd = 1;
+       pdata->debugfs_phydev_reg = 0;
 
        buf = kasprintf(GFP_KERNEL, "amd-xgbe-%s", pdata->netdev->name);
        if (!buf)
@@ -519,6 +641,35 @@ void xgbe_debugfs_init(struct xgbe_prv_data *pdata)
                                   "debugfs_create_file failed\n");
        }
 
+       if (pdata->phy_if.phy_impl.sfp_eeprom) {
+               pfile = debugfs_create_file("sfp_eeprom", 0400,
+                                           pdata->xgbe_debugfs, pdata,
+                                           &sfp_eeprom_fops);
+               if (!pfile)
+                       netdev_err(pdata->netdev, "debugfs_create_file 
failed\n");
+       }
+
+       if (pdata->phy_if.phy_impl.phydev_read &&
+           pdata->phy_if.phy_impl.phydev_write) {
+               pfile = debugfs_create_file("phydev_mmd", 0600,
+                                           pdata->xgbe_debugfs, pdata,
+                                           &phydev_mmd_fops);
+               if (!pfile)
+                       netdev_err(pdata->netdev, "debugfs_create_file 
failed\n");
+
+               pfile = debugfs_create_file("phydev_register", 0600,
+                                           pdata->xgbe_debugfs, pdata,
+                                           &phydev_reg_addr_fops);
+               if (!pfile)
+                       netdev_err(pdata->netdev, "debugfs_create_file 
failed\n");
+
+               pfile = debugfs_create_file("phydev_register_value", 0600,
+                                           pdata->xgbe_debugfs, pdata,
+                                           &phydev_reg_value_fops);
+               if (!pfile)
+                       netdev_err(pdata->netdev, "debugfs_create_file 
failed\n");
+       }
+
        kfree(buf);
 }
 
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c 
b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c
index 81c45fa..4463487 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c
@@ -258,6 +258,8 @@ struct xgbe_sfp_eeprom {
        u8 vendor[32];
 };
 
+#define XGBE_SFP_EEPROM_LINE                   16
+
 #define XGBE_BEL_FUSE_VENDOR   "BEL-FUSE        "
 #define XGBE_BEL_FUSE_PARTNO   "1GBT-SFP06      "
 
@@ -1275,6 +1277,79 @@ static void xgbe_phy_sfp_detect(struct xgbe_prv_data 
*pdata)
        xgbe_phy_put_comm_ownership(pdata);
 }
 
+static int xgbe_phy_phydev_write(struct xgbe_prv_data *pdata, unsigned int mmd,
+                                unsigned int reg, unsigned int val)
+{
+       struct xgbe_phy_data *phy_data = pdata->phy_data;
+       unsigned int ret;
+
+       if (!phy_data->phydev)
+               return -ENOTSUPP;
+
+       if (phy_data->phydev_mode == XGBE_MDIO_MODE_CL45)
+               reg = MII_ADDR_C45 | (mmd << 16) | (reg & 0xffff);
+
+       ret = xgbe_phy_mii_write(phy_data->mii, phy_data->mdio_addr, reg, val);
+
+       return ret;
+}
+
+static int xgbe_phy_phydev_read(struct xgbe_prv_data *pdata, unsigned int mmd,
+                               unsigned int reg)
+{
+       struct xgbe_phy_data *phy_data = pdata->phy_data;
+       unsigned int ret;
+
+       if (!phy_data->phydev)
+               return -ENOTSUPP;
+
+       if (phy_data->phydev_mode == XGBE_MDIO_MODE_CL45)
+               reg = MII_ADDR_C45 | (mmd << 16) | (reg & 0xffff);
+
+       ret = xgbe_phy_mii_read(phy_data->mii, phy_data->mdio_addr, reg);
+
+       return ret;
+}
+
+static unsigned char *xgbe_phy_sfp_eeprom(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_phy_data *phy_data = pdata->phy_data;
+       unsigned char *eeprom, *eeprom_end;
+       char *buffer, *cur;
+       size_t size;
+
+       /* Calculate the buffer needed for hex_dump_to_buffer()
+        * assuming XGBE_SFP_EEPROM_LINE bytes per line and ASCII data:
+        *   sizeof(struct xgbe_sfp_eeprom) * 3  for the hex output
+        *   sizeof(struct xgbe_sfp_eeprom)      for the ASCII output
+        *   sizeof(struct xgbe_sfp_eeprom)
+        *       / XGBE_SFP_EEPROM_LINE * 4      for extra spaces and newlines
+        */
+       size = (sizeof(struct xgbe_sfp_eeprom) * 4) +
+              (sizeof(struct xgbe_sfp_eeprom) / XGBE_SFP_EEPROM_LINE * 4);
+       buffer = kzalloc(size, GFP_ATOMIC);
+       if (!buffer)
+               return NULL;
+
+       cur = buffer;
+       eeprom = (unsigned char *)&phy_data->sfp_eeprom;
+       eeprom_end = eeprom + sizeof(struct xgbe_sfp_eeprom);
+       for (; eeprom < eeprom_end; eeprom += XGBE_SFP_EEPROM_LINE) {
+               hex_dump_to_buffer(eeprom, XGBE_SFP_EEPROM_LINE,
+                                  XGBE_SFP_EEPROM_LINE, 1, cur, size, true);
+
+               /* Reduce size by new string length (including newline) */
+               size -= strlen(cur);
+               size--;
+
+               /* Adjust buffer and add a new line */
+               cur += strlen(cur);
+               *cur++ = '\n';
+       }
+
+       return buffer;
+}
+
 static void xgbe_phy_phydev_flowctrl(struct xgbe_prv_data *pdata)
 {
        struct xgbe_phy_data *phy_data = pdata->phy_data;
@@ -2744,6 +2819,7 @@ static void xgbe_phy_exit(struct xgbe_prv_data *pdata)
 
 static int xgbe_phy_init(struct xgbe_prv_data *pdata)
 {
+       struct xgbe_phy_impl_if *phy_impl = &pdata->phy_if.phy_impl;
        struct xgbe_phy_data *phy_data;
        struct mii_bus *mii;
        unsigned int reg;
@@ -2869,6 +2945,8 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata)
                }
 
                phy_data->phydev_mode = XGBE_MDIO_MODE_CL22;
+               phy_impl->phydev_read = xgbe_phy_phydev_read;
+               phy_impl->phydev_write = xgbe_phy_phydev_write;
                break;
 
        /* MDIO Base-X support */
@@ -2880,6 +2958,8 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata)
                phy_data->start_mode = XGBE_MODE_X;
 
                phy_data->phydev_mode = XGBE_MDIO_MODE_CL22;
+               phy_impl->phydev_read = xgbe_phy_phydev_read;
+               phy_impl->phydev_write = xgbe_phy_phydev_write;
                break;
 
        /* MDIO NBase-T support */
@@ -2901,6 +2981,8 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata)
                }
 
                phy_data->phydev_mode = XGBE_MDIO_MODE_CL45;
+               phy_impl->phydev_read = xgbe_phy_phydev_read;
+               phy_impl->phydev_write = xgbe_phy_phydev_write;
                break;
 
        /* 10GBase-T support */
@@ -2922,6 +3004,8 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata)
                }
 
                phy_data->phydev_mode = XGBE_MDIO_MODE_CL45;
+               phy_impl->phydev_read = xgbe_phy_phydev_read;
+               phy_impl->phydev_write = xgbe_phy_phydev_write;
                break;
 
        /* 10GBase-R support */
@@ -2957,8 +3041,11 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata)
                }
 
                phy_data->phydev_mode = XGBE_MDIO_MODE_CL22;
+               phy_impl->phydev_read = xgbe_phy_phydev_read;
+               phy_impl->phydev_write = xgbe_phy_phydev_write;
 
                xgbe_phy_sfp_setup(pdata);
+               phy_impl->sfp_eeprom = xgbe_phy_sfp_eeprom;
                break;
        default:
                return -EINVAL;
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h 
b/drivers/net/ethernet/amd/xgbe/xgbe.h
index e9282c9..8a3fb9d 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe.h
+++ b/drivers/net/ethernet/amd/xgbe/xgbe.h
@@ -776,6 +776,7 @@ struct xgbe_hw_if {
  * implementation of a PHY. All routines are required unless noted below.
  *   Optional routines:
  *     kr_training_pre, kr_training_post
+ *     eeprom_data, phydev_read, phydev_write
  */
 struct xgbe_phy_impl_if {
        /* Perform Setup/teardown actions */
@@ -819,6 +820,14 @@ struct xgbe_phy_impl_if {
        /* Pre/Post KR training enablement support */
        void (*kr_training_pre)(struct xgbe_prv_data *);
        void (*kr_training_post)(struct xgbe_prv_data *);
+
+       /* Return the eeprom data for an SFP */
+       unsigned char *(*sfp_eeprom)(struct xgbe_prv_data *);
+
+       /* Read the register of an attached PHY device */
+       int (*phydev_read)(struct xgbe_prv_data *, unsigned int, unsigned int);
+       int (*phydev_write)(struct xgbe_prv_data *, unsigned int, unsigned int,
+                           unsigned int);
 };
 
 struct xgbe_phy_if {
@@ -1183,6 +1192,9 @@ struct xgbe_prv_data {
        unsigned int debugfs_xprop_reg;
 
        unsigned int debugfs_xi2c_reg;
+
+       unsigned int debugfs_phydev_mmd;
+       unsigned int debugfs_phydev_reg;
 #endif
 };
 

Reply via email to