From 56063f598383f8b5d70d392352b1e3b039946b7e Mon Sep 17 00:00:00 2001 From: Narayan Reddy Date: Wed, 7 Jul 2021 01:17:19 +0530 Subject: [PATCH] nvethernet: add PHY read/write support Add a private ioctl for phy register read and write. This support is needed for accessing the PHY registers from user space when fixed link is enabled in SW even though PHY is directly connected to MAC. Bug 200733774 Change-Id: Ifd9b5dc7764363d897605c2370da419a863ddea4 Signed-off-by: Narayan Reddy Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2554892 Tested-by: mobile promotions Reviewed-by: mobile promotions --- .../ethernet/nvidia/nvethernet/ether_linux.c | 107 ++++++++++++++++++ .../net/ethernet/nvidia/nvethernet/ioctl.h | 2 + 2 files changed, 109 insertions(+) diff --git a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c index a85ed7e5..9ee30467 100644 --- a/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c +++ b/drivers/net/ethernet/nvidia/nvethernet/ether_linux.c @@ -3386,6 +3386,105 @@ void ether_set_rx_mode(struct net_device *dev) schedule_work(&pdata->set_rx_mode_work); } +/** + * @brief Function to handle PHY read private IOCTL + * + * Algorithm: This function is used to write the data + * into the specified register. + * + * @param [in] pdata: Pointer to private data structure. + * @param [in] ifr: Interface request structure used for socket ioctl + * + * @retval 0 on success. + * @retval "negative value" on failure. + */ + +static int ether_handle_priv_rmdio_ioctl(struct ether_priv_data *pdata, + struct ifreq *ifr) +{ +#if (LINUX_VERSION_CODE > KERNEL_VERSION(5, 9, 0)) + struct mii_ioctl_data *mii_data = if_mii(ifr); + unsigned int prtad, devad; + int ret = 0; + + if (!ifr->ifr_data) { + dev_err(pdata->dev, "%s: Invalid data for priv ioctl\n", + __func__); + return -EFAULT; + } + + if (mdio_phy_id_is_c45(mii_data->phy_id)) { + prtad = mdio_phy_id_prtad(mii_data->phy_id); + devad = mdio_phy_id_devad(mii_data->phy_id); + devad = mdiobus_c45_addr(devad, mii_data->reg_num); + } else { + prtad = mii_data->phy_id; + devad = mii_data->reg_num; + } + + dev_dbg(pdata->dev, "%s: phy_id:%d regadd: %d devaddr:%d\n", + __func__, mii_data->phy_id, prtad, devad); + + ret = osi_read_phy_reg(pdata->osi_core, prtad, devad); + if (ret < 0) { + dev_err(pdata->dev, "%s: Data read failed\n", __func__); + return -EFAULT; + } + + mii_data->val_out = ret; + + return 0; +#else + dev_err(pdata->dev, "Not supported for kernel versions less than 5.10"); + return -ENOTSUPP; +#endif +} + +/** + * @brief Function to handle PHY write private IOCTL + * + * Algorithm: This function is used to write the data + * into the specified register. + * + * @param [in] pdata: Pointer to private data structure. + * @param [in] ifr: Interface request structure used for socket ioctl + * + * @retval 0 on success. + * @retval "negative value" on failure. + */ +static int ether_handle_priv_wmdio_ioctl(struct ether_priv_data *pdata, + struct ifreq *ifr) +{ +#if (LINUX_VERSION_CODE > KERNEL_VERSION(5, 9, 0)) + struct mii_ioctl_data *mii_data = if_mii(ifr); + unsigned int prtad, devad; + + if (!ifr->ifr_data) { + dev_err(pdata->dev, "%s: Invalid data for priv ioctl\n", + __func__); + return -EFAULT; + } + + if (mdio_phy_id_is_c45(mii_data->phy_id)) { + prtad = mdio_phy_id_prtad(mii_data->phy_id); + devad = mdio_phy_id_devad(mii_data->phy_id); + devad = mdiobus_c45_addr(devad, mii_data->reg_num); + } else { + prtad = mii_data->phy_id; + devad = mii_data->reg_num; + } + + dev_dbg(pdata->dev, "%s: phy_id:%d regadd: %d devaddr:%d val:%d\n", + __func__, mii_data->phy_id, prtad, devad, mii_data->val_in); + + return osi_write_phy_reg(pdata->osi_core, prtad, devad, + mii_data->val_in); +#else + dev_err(pdata->dev, "Not supported for kernel versions less than 5.10"); + return -ENOTSUPP; +#endif +} + /** * @brief Network stack IOCTL hook to driver * @@ -3433,6 +3532,14 @@ static int ether_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) ret = ether_handle_priv_ioctl(dev, rq); break; + case ETHER_PRV_RMDIO_IOCTL: + ret = ether_handle_priv_rmdio_ioctl(pdata, rq); + break; + + case ETHER_PRV_WMDIO_IOCTL: + ret = ether_handle_priv_wmdio_ioctl(pdata, rq); + break; + case ETHER_PRV_TS_IOCTL: ret = ether_handle_priv_ts_ioctl(pdata, rq); break; diff --git a/drivers/net/ethernet/nvidia/nvethernet/ioctl.h b/drivers/net/ethernet/nvidia/nvethernet/ioctl.h index dd57110f..540f52e8 100644 --- a/drivers/net/ethernet/nvidia/nvethernet/ioctl.h +++ b/drivers/net/ethernet/nvidia/nvethernet/ioctl.h @@ -39,6 +39,8 @@ /* Remote wakeup filter */ #define EQOS_RWK_FILTER_LENGTH 8 #define ETHER_PRV_TS_IOCTL (SIOCDEVPRIVATE + 1) +#define ETHER_PRV_RMDIO_IOCTL (SIOCDEVPRIVATE + 2) +#define ETHER_PRV_WMDIO_IOCTL (SIOCDEVPRIVATE + 3) /* TX/RX channel/queue count */ #define EQOS_GET_TX_QCNT 23 #define EQOS_GET_RX_QCNT 24