kernel/patches/0568-net-hns3-add-extend-interface-support-for-read-and-w.patch
2023-11-17 14:19:46 +08:00

777 lines
22 KiB
Diff

From 4a236e9c4f8e24770ebec17387366b5bf40f4902 Mon Sep 17 00:00:00 2001
From: Jian Shen <shenjian15@huawei.com>
Date: Sun, 23 Apr 2023 16:44:38 +0800
Subject: [PATCH 207/283] net: hns3: add extend interface support for read and
write phy register
driver inclusion
category: feature
bugzilla: https://gitee.com/src-openeuler/kernel/issues/I8EN3D
CVE: NA
----------------------------------------------------------------------
Add extend interface support for read and write
phy register with page. Sofar it supports only
Phy RTL8211 and YT8521.
Signed-off-by: Jian Shen <shenjian15@huawei.com>
Signed-off-by: Jiantao Xiao <xiaojiantao1@h-partners.com>
Signed-off-by: Xiaodong Li <lixiaodong67@huawei.com>
Conflicts:
drivers/net/ethernet/hisilicon/hns3/hns3_ext.h
---
.../net/ethernet/hisilicon/hns3/hnae3_ext.h | 9 +
.../net/ethernet/hisilicon/hns3/hns3_ext.c | 36 ++
.../net/ethernet/hisilicon/hns3/hns3_ext.h | 11 +
.../hisilicon/hns3/hns3pf/hclge_cmd.h | 8 +-
.../hisilicon/hns3/hns3pf/hclge_ext.c | 559 ++++++++++++++++++
.../hisilicon/hns3/hns3pf/hclge_ext.h | 32 +
6 files changed, 654 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/hisilicon/hns3/hnae3_ext.h b/drivers/net/ethernet/hisilicon/hns3/hnae3_ext.h
index 6b1b5c630260..a9503e89263d 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hnae3_ext.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hnae3_ext.h
@@ -50,6 +50,8 @@ enum hnae3_ext_opcode {
HNAE3_EXT_OPC_SET_MAC_STATE,
HNAE3_EXT_OPC_SET_LED,
HNAE3_EXT_OPC_GET_LED_SIGNAL,
+ HNAE3_EXT_OPC_GET_PHY_REG,
+ HNAE3_EXT_OPC_SET_PHY_REG,
};
struct hnae3_led_state_para {
@@ -57,6 +59,13 @@ struct hnae3_led_state_para {
u32 status;
};
+struct hnae3_phy_para {
+ u32 page_select_addr;
+ u32 reg_addr;
+ u16 page;
+ u16 data;
+};
+
struct hnae3_lamp_signal {
u8 error;
u8 locate;
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_ext.c b/drivers/net/ethernet/hisilicon/hns3/hns3_ext.c
index 64ad9be4ec4f..39bd51e93ddb 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3_ext.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3_ext.c
@@ -430,3 +430,39 @@ int nic_get_led_signal(struct net_device *ndev,
signal, sizeof(*signal));
}
EXPORT_SYMBOL(nic_get_led_signal);
+
+int nic_get_phy_reg(struct net_device *ndev, u32 page_select_addr,
+ u16 page, u32 reg_addr, u16 *data)
+{
+ struct hnae3_phy_para para;
+ int ret;
+
+ if (!data)
+ return -EINVAL;
+
+ para.page_select_addr = page_select_addr;
+ para.page = page;
+ para.reg_addr = reg_addr;
+ ret = nic_invoke_pri_ops(ndev, HNAE3_EXT_OPC_GET_PHY_REG,
+ &para, sizeof(para));
+ if (ret)
+ return ret;
+
+ *data = para.data;
+ return 0;
+}
+EXPORT_SYMBOL(nic_get_phy_reg);
+
+int nic_set_phy_reg(struct net_device *ndev, u32 page_select_addr,
+ u16 page, u32 reg_addr, u16 data)
+{
+ struct hnae3_phy_para para;
+
+ para.page_select_addr = page_select_addr;
+ para.page = page;
+ para.reg_addr = reg_addr;
+ para.data = data;
+ return nic_invoke_pri_ops(ndev, HNAE3_EXT_OPC_SET_PHY_REG,
+ &para, sizeof(para));
+}
+EXPORT_SYMBOL(nic_set_phy_reg);
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_ext.h b/drivers/net/ethernet/hisilicon/hns3/hns3_ext.h
index f47c05c2c660..d9c107f5e231 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3_ext.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3_ext.h
@@ -14,6 +14,13 @@
#define HNS3_PFC_STORM_PARA_PERIOD_MIN 5
#define HNS3_PFC_STORM_PARA_PERIOD_MAX 2000
+#define nic_set_8211_phy_reg nic_set_phy_reg
+#define nic_get_8211_phy_reg nic_get_phy_reg
+#define nic_set_8521_phy_reg(ndev, page_region, page, reg_addr, data) \
+ nic_set_phy_reg(ndev, 0, page_region, page, reg_addr, data)
+#define nic_get_8521_phy_reg(ndev, page_region, page, reg_addr, data) \
+ nic_get_phy_reg(ndev, 0, page_region, page, reg_addr, data)
+
#define nic_get_cdr_flash_status(ndev, status) \
nic_get_port_fault_status(ndev, HNAE3_FAULT_TYPE_CDR_FLASH, status)
#define nic_get_hilink_ref_los(ndev, status) \
@@ -58,4 +65,8 @@ int nic_set_mac_state(struct net_device *ndev, int enable);
int nic_set_led(struct net_device *ndev, int type, int status);
int nic_get_led_signal(struct net_device *ndev,
struct hnae3_lamp_signal *signal);
+int nic_get_phy_reg(struct net_device *ndev, u32 page_select_addr,
+ u16 page, u32 reg_addr, u16 *data);
+int nic_set_phy_reg(struct net_device *ndev, u32 page_select_addr,
+ u16 page, u32 reg_addr, u16 data);
#endif
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h
index 07e20ed0106c..a4599f717da5 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h
@@ -1250,11 +1250,17 @@ struct hclge_phy_link_ksetting_1_cmd {
u8 rsv[22];
};
+#define HCLGE_PHY_RW_DIRECTLY 0
+#define HCLGE_PHY_RW_WITH_PAGE 1
struct hclge_phy_reg_cmd {
__le16 reg_addr;
u8 rsv0[2];
__le16 reg_val;
- u8 rsv1[18];
+ u8 rsv1[2];
+ u8 type;
+ u8 dev_addr;
+ __le16 page;
+ u8 rsv2[12];
};
/* capabilities bits map between imp firmware and local driver */
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_ext.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_ext.c
index 4ef891d9613a..c1013b338650 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_ext.c
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_ext.c
@@ -688,6 +688,563 @@ static int hclge_get_led_signal(struct hclge_dev *hdev, void *data,
return 0;
}
+static int hclge_def_phy_opt(struct mii_bus *mdio_bus, u32 phy_addr,
+ u16 reg_addr, u16 *data,
+ enum hclge_phy_op_code opt_type)
+{
+ int ret;
+
+ if (opt_type == PHY_OP_READ) {
+ ret = mdio_bus->read(mdio_bus, phy_addr, reg_addr);
+ if (ret >= 0) {
+ *data = (u16)ret;
+ ret = 0;
+ }
+ } else {
+ ret = mdio_bus->write(mdio_bus, phy_addr, reg_addr, *data);
+ }
+ return ret;
+}
+
+static int hclge_phy_reg_opt(struct hclge_dev *hdev,
+ struct hnae3_phy_para *para,
+ enum hclge_phy_op_code opt_type)
+{
+ struct mii_bus *mdio_bus = hdev->hw.mac.mdio_bus;
+ u32 phy_addr = hdev->hw.mac.phy_addr;
+ bool need_page_select = false;
+ u16 cur_page;
+ int ret;
+
+ /* operate flow:
+ * 1 record current page addr
+ * 2 jump to operated page
+ * 3 operate register(read or write)
+ * 4 come back to the page recorded in the first step.
+ */
+ mutex_lock(&mdio_bus->mdio_lock);
+
+ /* check if page select is needed and record current page addr.
+ * no need to change page when read page 0
+ */
+ if (opt_type != PHY_OP_READ || para->page != 0) {
+ ret = mdio_bus->read(mdio_bus, phy_addr,
+ para->page_select_addr);
+ if (ret < 0) {
+ dev_err(&hdev->pdev->dev,
+ "failed to read current phy %u reg page\n",
+ phy_addr);
+ mutex_unlock(&mdio_bus->mdio_lock);
+ return ret;
+ }
+ cur_page = (u16)ret;
+ need_page_select = cur_page != para->page;
+ }
+
+ /* jump to operated page */
+ if (need_page_select) {
+ ret = mdio_bus->write(mdio_bus, phy_addr,
+ para->page_select_addr, para->page);
+ if (ret < 0) {
+ mutex_unlock(&mdio_bus->mdio_lock);
+ dev_err(&hdev->pdev->dev,
+ "failed to change phy %u page %u to page %u\n",
+ phy_addr, cur_page, para->page);
+ return ret;
+ }
+ }
+
+ /* operate register(read or write) */
+ ret = hclge_def_phy_opt(mdio_bus, phy_addr, para->reg_addr, &para->data,
+ opt_type);
+ if (ret < 0)
+ dev_err(&hdev->pdev->dev,
+ "failed to %s phy %u page %u reg %u\n, ret = %d",
+ opt_type == PHY_OP_READ ? "read" : "write",
+ phy_addr, para->page, para->reg_addr, ret);
+
+ /* come back to the page recorded in the first step. */
+ if (need_page_select) {
+ ret = mdio_bus->write(mdio_bus, phy_addr,
+ para->page_select_addr, cur_page);
+ if (ret < 0)
+ dev_err(&hdev->pdev->dev,
+ "failed to restore phy %u reg page %u\n",
+ phy_addr, cur_page);
+ }
+
+ mutex_unlock(&mdio_bus->mdio_lock);
+
+ return ret;
+}
+
+static int hclge_8521_phy_ext_opt(struct mii_bus *mdio_bus, u32 phy_addr,
+ u16 reg_addr, u16 *data,
+ enum hclge_phy_op_code opt_type)
+{
+#define EXT_REG_ADDR 0x1e
+#define EXT_DATA_ADDR 0x1f
+ int ret;
+
+ ret = mdio_bus->write(mdio_bus, phy_addr, EXT_REG_ADDR, reg_addr);
+ if (ret < 0)
+ return ret;
+
+ return hclge_def_phy_opt(mdio_bus, phy_addr, EXT_DATA_ADDR, data,
+ opt_type);
+}
+
+static int hclge_8521_phy_mmd_opt(struct mii_bus *mdio_bus, u32 phy_addr,
+ u32 reg_addr, u16 *data,
+ enum hclge_phy_op_code opt_type)
+{
+#define MMD_REG_ADDR 0xd
+#define MMD_DATA_ADDR 0xe
+ u16 mmd_index;
+ u16 mmd_reg;
+ int ret;
+
+ mmd_index = reg_addr >> 16U;
+ mmd_reg = reg_addr & 0xFFFF;
+
+ ret = mdio_bus->write(mdio_bus, phy_addr, MMD_REG_ADDR, mmd_index);
+ if (ret < 0)
+ return ret;
+ ret = mdio_bus->write(mdio_bus, phy_addr, MMD_DATA_ADDR, mmd_reg);
+ if (ret < 0)
+ return ret;
+ ret = mdio_bus->write(mdio_bus, phy_addr, MMD_REG_ADDR,
+ mmd_index | 0x4000);
+ if (ret < 0)
+ return ret;
+
+ return hclge_def_phy_opt(mdio_bus, phy_addr, MMD_DATA_ADDR, data,
+ opt_type);
+}
+
+static void hclge_8521_phy_restores_to_utp_mii(struct hclge_dev *hdev,
+ struct mii_bus *mdio_bus,
+ u32 phy_addr)
+{
+ u16 phy_mii_region_val = 0x6;
+ u16 utp_region_val = 0x0;
+ int ret;
+
+ ret = hclge_8521_phy_ext_opt(mdio_bus, phy_addr,
+ HCLGE_8521_PHY_SMI_SDS_ADDR,
+ &utp_region_val, PHY_OP_WRITE);
+ if (ret)
+ dev_err(&hdev->pdev->dev,
+ "failed to choose phy space, ret = %d\n", ret);
+
+ ret = hclge_8521_phy_ext_opt(mdio_bus, phy_addr,
+ HCLGE_8521_PHY_LDS_MII_ADDR,
+ &phy_mii_region_val, PHY_OP_WRITE);
+ if (ret)
+ dev_err(&hdev->pdev->dev,
+ "failed to choose phy MII, ret = %d\n", ret);
+}
+
+static int hclge_8521_phy_utp_mii_opt(struct hnae3_phy_para *para,
+ struct mii_bus *mdio_bus, u32 phy_addr,
+ enum hclge_phy_op_code opt_type)
+{
+ u16 phy_mii_region_val = 0x6;
+ u16 utp_region_val = 0x0;
+ int ret;
+
+ ret = hclge_8521_phy_ext_opt(mdio_bus, phy_addr,
+ HCLGE_8521_PHY_SMI_SDS_ADDR,
+ &utp_region_val, PHY_OP_WRITE);
+ if (ret)
+ return ret;
+
+ ret = hclge_8521_phy_ext_opt(mdio_bus, phy_addr,
+ HCLGE_8521_PHY_LDS_MII_ADDR,
+ &phy_mii_region_val, PHY_OP_WRITE);
+ if (ret)
+ return ret;
+
+ return hclge_def_phy_opt(mdio_bus, phy_addr, (u16)para->reg_addr,
+ &para->data, opt_type);
+}
+
+static int hclge_8521_phy_utp_mmd_opt(struct hnae3_phy_para *para,
+ struct mii_bus *mdio_bus, u32 phy_addr,
+ enum hclge_phy_op_code opt_type)
+{
+ u16 utp_region_val = 0x0;
+ int ret;
+
+ ret = hclge_8521_phy_ext_opt(mdio_bus, phy_addr,
+ HCLGE_8521_PHY_SMI_SDS_ADDR,
+ &utp_region_val, PHY_OP_WRITE);
+ if (ret)
+ return ret;
+
+ return hclge_8521_phy_mmd_opt(mdio_bus, phy_addr, para->reg_addr,
+ &para->data, opt_type);
+}
+
+static int hclge_8521_phy_utp_lds_opt(struct hnae3_phy_para *para,
+ struct mii_bus *mdio_bus, u32 phy_addr,
+ enum hclge_phy_op_code opt_type)
+{
+ u16 lds_mii_region_val = 0x4;
+ u16 utp_region_val = 0x0;
+ int ret;
+
+ ret = hclge_8521_phy_ext_opt(mdio_bus, phy_addr,
+ HCLGE_8521_PHY_SMI_SDS_ADDR,
+ &utp_region_val, PHY_OP_WRITE);
+ if (ret)
+ return ret;
+
+ ret = hclge_8521_phy_ext_opt(mdio_bus, phy_addr,
+ HCLGE_8521_PHY_LDS_MII_ADDR,
+ &lds_mii_region_val, PHY_OP_WRITE);
+ if (ret)
+ return ret;
+
+ return hclge_def_phy_opt(mdio_bus, phy_addr, (u16)para->reg_addr,
+ &para->data, opt_type);
+}
+
+static int hclge_8521_phy_utp_ext_opt(struct hnae3_phy_para *para,
+ struct mii_bus *mdio_bus, u32 phy_addr,
+ enum hclge_phy_op_code opt_type)
+{
+ u16 utp_region_val = 0x0;
+ int ret;
+
+ ret = hclge_8521_phy_ext_opt(mdio_bus, phy_addr,
+ HCLGE_8521_PHY_SMI_SDS_ADDR,
+ &utp_region_val, PHY_OP_WRITE);
+ if (ret)
+ return ret;
+
+ return hclge_8521_phy_ext_opt(mdio_bus, phy_addr, (u16)para->reg_addr,
+ &para->data, opt_type);
+}
+
+static int hclge_8521_phy_sds_mii_opt(struct hnae3_phy_para *para,
+ struct mii_bus *mdio_bus, u32 phy_addr,
+ enum hclge_phy_op_code opt_type)
+{
+ u16 sds_region_val = 0x2;
+ int ret;
+
+ ret = hclge_8521_phy_ext_opt(mdio_bus, phy_addr,
+ HCLGE_8521_PHY_SMI_SDS_ADDR,
+ &sds_region_val, PHY_OP_WRITE);
+ if (ret)
+ return ret;
+
+ return hclge_def_phy_opt(mdio_bus, phy_addr, (u16)para->reg_addr,
+ &para->data, opt_type);
+}
+
+static int hclge_8521_phy_sds_ext_opt(struct hnae3_phy_para *para,
+ struct mii_bus *mdio_bus, u32 phy_addr,
+ enum hclge_phy_op_code opt_type)
+{
+ u16 sds_region_val = 0x2;
+ int ret;
+
+ ret = hclge_8521_phy_ext_opt(mdio_bus, phy_addr,
+ HCLGE_8521_PHY_SMI_SDS_ADDR,
+ &sds_region_val, PHY_OP_WRITE);
+ if (ret)
+ return ret;
+
+ return hclge_8521_phy_ext_opt(mdio_bus, phy_addr, (u16)para->reg_addr,
+ &para->data, opt_type);
+}
+
+static int hclge_8521_phy_opt(struct hclge_dev *hdev,
+ struct hnae3_phy_para *para,
+ enum hclge_phy_op_code opt_type)
+{
+ struct mii_bus *mdio_bus = hdev->hw.mac.mdio_bus;
+ u32 phy_addr = hdev->hw.mac.phy_addr;
+ int ret;
+
+ mutex_lock(&mdio_bus->mdio_lock);
+ switch (para->page) {
+ case HCLGE_PHY_REGION_UTP_MII:
+ ret = hclge_8521_phy_utp_mii_opt(para, mdio_bus,
+ phy_addr, opt_type);
+ break;
+ case HCLGE_PHY_REGION_UTP_MMD:
+ ret = hclge_8521_phy_utp_mmd_opt(para, mdio_bus,
+ phy_addr, opt_type);
+ break;
+ case HCLGE_PHY_REGION_UTP_LDS:
+ ret = hclge_8521_phy_utp_lds_opt(para, mdio_bus,
+ phy_addr, opt_type);
+ break;
+ case HCLGE_PHY_REGION_UTP_EXT:
+ ret = hclge_8521_phy_utp_ext_opt(para, mdio_bus,
+ phy_addr, opt_type);
+ break;
+ case HCLGE_PHY_REGION_SDS_MII:
+ ret = hclge_8521_phy_sds_mii_opt(para, mdio_bus,
+ phy_addr, opt_type);
+ break;
+ case HCLGE_PHY_REGION_SDS_EXT:
+ ret = hclge_8521_phy_sds_ext_opt(para, mdio_bus,
+ phy_addr, opt_type);
+ break;
+ case HCLGE_PHY_REGION_COM_REG:
+ ret = hclge_8521_phy_ext_opt(mdio_bus, phy_addr,
+ (u16)para->reg_addr,
+ &para->data, opt_type);
+ break;
+ default:
+ dev_err(&hdev->pdev->dev, "invalid reg region: %d\n",
+ para->page);
+ mutex_unlock(&mdio_bus->mdio_lock);
+ return -EINVAL;
+ }
+
+ if (ret)
+ dev_err(&hdev->pdev->dev,
+ "phy operation failed %d, reg_region: %d, data: 0x%x\n",
+ ret, para->page, para->data);
+
+ /* Set the region to UTP MII after operating the 8521 phy register */
+ hclge_8521_phy_restores_to_utp_mii(hdev, mdio_bus, phy_addr);
+ mutex_unlock(&mdio_bus->mdio_lock);
+ return ret;
+}
+
+static int hclge_check_phy_opt_param(struct hclge_dev *hdev, void *data,
+ size_t length)
+{
+ struct hnae3_phy_para *para = (struct hnae3_phy_para *)data;
+ struct hclge_mac *mac = &hdev->hw.mac;
+
+ if (length != sizeof(*para))
+ return -EINVAL;
+
+ if (mac->media_type != HNAE3_MEDIA_TYPE_COPPER) {
+ dev_err(&hdev->pdev->dev, "this is not a copper port");
+ return -EOPNOTSUPP;
+ }
+
+ if (hnae3_dev_phy_imp_supported(hdev))
+ return 0;
+
+ if (!mac->phydev) {
+ dev_err(&hdev->pdev->dev, "this net device has no phy");
+ return -EINVAL;
+ }
+
+ if (!mac->mdio_bus) {
+ dev_err(&hdev->pdev->dev, "this net device has no mdio bus");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int hclge_8211_phy_indirect_opt(struct hclge_dev *hdev,
+ struct hnae3_phy_para *para,
+ struct mii_bus *mdio_bus, u32 phy_addr,
+ enum hclge_phy_op_code opt_type)
+{
+ u32 indirect_reg_data;
+ int ret;
+
+ /* select indirect page 0xa43 */
+ ret = mdio_bus->write(mdio_bus, phy_addr, para->page_select_addr,
+ HCLGE_8211_PHY_INDIRECT_PAGE);
+ if (ret < 0) {
+ dev_err(&hdev->pdev->dev,
+ "failed to change phy %u indirect page 0xa43\n",
+ phy_addr);
+ return ret;
+ }
+ /* indirect access addr = page_no*16 + 2*(reg_no%16) */
+ indirect_reg_data = (para->page << 4) + ((para->reg_addr % 16) << 1);
+ ret = mdio_bus->write(mdio_bus, phy_addr, HCLGE_8211_PHY_INDIRECT_REG,
+ indirect_reg_data);
+ if (ret < 0) {
+ dev_err(&hdev->pdev->dev,
+ "failed to write phy %u indirect reg\n", phy_addr);
+ return ret;
+ }
+
+ ret = hclge_def_phy_opt(mdio_bus, phy_addr,
+ HCLGE_8211_PHY_INDIRECT_DATA, &para->data,
+ opt_type);
+ if (ret < 0)
+ dev_err(&hdev->pdev->dev,
+ "failed to %s phy %u indirect data\n, ret = %d",
+ opt_type == PHY_OP_READ ? "read" : "write",
+ phy_addr, ret);
+
+ return ret;
+}
+
+static int hclge_8211_phy_need_indirect_access(u16 page)
+{
+ if (page >= HCLGE_8211_PHY_INDIRECT_RANGE1_S &&
+ page <= HCLGE_8211_PHY_INDIRECT_RANGE1_E)
+ return true;
+ else if (page >= HCLGE_8211_PHY_INDIRECT_RANGE2_S &&
+ page <= HCLGE_8211_PHY_INDIRECT_RANGE2_E)
+ return true;
+
+ return false;
+}
+
+static int hclge_8211_phy_reg_opt(struct hclge_dev *hdev,
+ struct hnae3_phy_para *para,
+ enum hclge_phy_op_code opt_type)
+{
+ struct mii_bus *mdio_bus = hdev->hw.mac.mdio_bus;
+ u32 phy_addr = hdev->hw.mac.phy_addr;
+ u16 save_page;
+ int ret;
+
+ mutex_lock(&mdio_bus->mdio_lock);
+ ret = mdio_bus->read(mdio_bus, phy_addr, para->page_select_addr);
+ if (ret < 0) {
+ dev_err(&hdev->pdev->dev,
+ "failed to record phy %u reg page\n", phy_addr);
+ mutex_unlock(&mdio_bus->mdio_lock);
+ return ret;
+ }
+ save_page = ret;
+ ret = hclge_8211_phy_indirect_opt(hdev, para, mdio_bus, phy_addr,
+ opt_type);
+ if (ret)
+ dev_err(&hdev->pdev->dev,
+ "failed to indirect access 8211 phy %u\n", phy_addr);
+ ret = mdio_bus->write(mdio_bus, phy_addr, para->page_select_addr,
+ save_page);
+ if (ret < 0)
+ dev_err(&hdev->pdev->dev,
+ "failed to restore phy %u reg page %u\n",
+ phy_addr, save_page);
+ mutex_unlock(&mdio_bus->mdio_lock);
+
+ return ret;
+}
+
+static int hclge_rw_8211_phy_reg(struct hclge_dev *hdev,
+ struct hnae3_phy_para *para,
+ enum hclge_phy_op_code opt_type)
+{
+ if (hclge_8211_phy_need_indirect_access(para->page))
+ return hclge_8211_phy_reg_opt(hdev, para, opt_type);
+
+ return hclge_phy_reg_opt(hdev, para, opt_type);
+}
+
+/* used when imp support phy drvier */
+static int hclge_read_phy_reg_with_page(struct hclge_dev *hdev, u16 page,
+ u16 reg_addr, u16 *val)
+{
+ struct hclge_phy_reg_cmd *req;
+ struct hclge_desc desc;
+ int ret;
+
+ hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_PHY_REG, true);
+
+ req = (struct hclge_phy_reg_cmd *)desc.data;
+ req->reg_addr = cpu_to_le16(reg_addr);
+ req->type = HCLGE_PHY_RW_WITH_PAGE;
+ req->page = cpu_to_le16(page);
+
+ ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+ if (ret) {
+ dev_err(&hdev->pdev->dev,
+ "failed to read phy page %u reg %u, ret = %d\n",
+ page, reg_addr, ret);
+ return ret;
+ }
+
+ *val = le16_to_cpu(req->reg_val);
+ return 0;
+}
+
+/* used when imp support phy drvier */
+static int hclge_write_phy_reg_with_page(struct hclge_dev *hdev, u16 page,
+ u16 reg_addr, u16 val)
+{
+ struct hclge_phy_reg_cmd *req;
+ struct hclge_desc desc;
+ int ret;
+
+ hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_PHY_REG, false);
+
+ req = (struct hclge_phy_reg_cmd *)desc.data;
+ req->reg_addr = cpu_to_le16(reg_addr);
+ req->type = HCLGE_PHY_RW_WITH_PAGE;
+ req->page = cpu_to_le16(page);
+ req->reg_val = cpu_to_le16(val);
+
+ ret = hclge_cmd_send(&hdev->hw, &desc, 1);
+ if (ret)
+ dev_err(&hdev->pdev->dev,
+ "failed to write phy page %u reg %u, ret = %d\n",
+ page, reg_addr, ret);
+
+ return ret;
+}
+
+static int hclge_rw_phy_reg_with_page(struct hclge_dev *hdev,
+ struct hnae3_phy_para *para,
+ enum hclge_phy_op_code opt_type)
+{
+ if (opt_type == PHY_OP_READ)
+ return hclge_read_phy_reg_with_page(hdev, para->page,
+ para->reg_addr,
+ &para->data);
+
+ return hclge_write_phy_reg_with_page(hdev, para->page, para->reg_addr,
+ para->data);
+}
+
+static int hclge_rw_phy_reg(struct hclge_dev *hdev, void *data,
+ size_t length, enum hclge_phy_op_code opt_type)
+{
+ struct hnae3_phy_para *para = (struct hnae3_phy_para *)data;
+ struct hclge_mac *mac = &hdev->hw.mac;
+ u32 phy_id;
+ int ret;
+
+ ret = hclge_check_phy_opt_param(hdev, data, length);
+ if (ret < 0)
+ return ret;
+
+ if (hnae3_dev_phy_imp_supported(hdev))
+ return hclge_rw_phy_reg_with_page(hdev, para, opt_type);
+
+ phy_id = mac->phydev->phy_id & HCLGE_PHY_ID_MASK;
+ switch (phy_id) {
+ case HCLGE_PHY_ID_FOR_RTL8211:
+ return hclge_rw_8211_phy_reg(hdev, para, opt_type);
+ case HCLGE_PHY_ID_FOR_YT8521:
+ return hclge_8521_phy_opt(hdev, para, opt_type);
+ case HCLGE_PHY_ID_FOR_MVL1512:
+ default:
+ return hclge_phy_reg_opt(hdev, para, opt_type);
+ }
+}
+
+static int hclge_get_phy_reg(struct hclge_dev *hdev, void *data, size_t length)
+{
+ return hclge_rw_phy_reg(hdev, data, length, PHY_OP_READ);
+}
+
+static int hclge_set_phy_reg(struct hclge_dev *hdev, void *data, size_t length)
+{
+ return hclge_rw_phy_reg(hdev, data, length, PHY_OP_WRITE);
+}
+
static void hclge_ext_resotre_config(struct hclge_dev *hdev)
{
if (hdev->reset_type != HNAE3_IMP_RESET &&
@@ -859,6 +1416,8 @@ static const hclge_priv_ops_fn hclge_ext_func_arr[] = {
[HNAE3_EXT_OPC_SET_MAC_STATE] = hclge_set_mac_state,
[HNAE3_EXT_OPC_SET_LED] = hclge_set_led,
[HNAE3_EXT_OPC_GET_LED_SIGNAL] = hclge_get_led_signal,
+ [HNAE3_EXT_OPC_GET_PHY_REG] = hclge_get_phy_reg,
+ [HNAE3_EXT_OPC_SET_PHY_REG] = hclge_set_phy_reg,
};
int hclge_ext_ops_handle(struct hnae3_handle *handle, int opcode,
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_ext.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_ext.h
index dab62a588e53..090152a87c60 100644
--- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_ext.h
+++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_ext.h
@@ -5,6 +5,38 @@
#define __HCLGE_EXT_H
#include <linux/types.h>
+#define HCLGE_PHY_ID_FOR_RTL8211 0x001cc910
+#define HCLGE_PHY_ID_FOR_MVL1512 0x01410dd0
+#define HCLGE_PHY_ID_FOR_YT8521 0x00000110
+#define HCLGE_PHY_ID_MASK 0xFFFFFFF0U
+
+enum hclge_phy_page_region {
+ HCLGE_PHY_REGION_UTP_MII,
+ HCLGE_PHY_REGION_UTP_MMD,
+ HCLGE_PHY_REGION_UTP_LDS,
+ HCLGE_PHY_REGION_UTP_EXT,
+ HCLGE_PHY_REGION_SDS_MII,
+ HCLGE_PHY_REGION_SDS_EXT,
+ HCLGE_PHY_REGION_COM_REG,
+ HCLGE_PHY_REGION_MAX
+};
+
+enum hclge_phy_op_code {
+ PHY_OP_READ,
+ PHY_OP_WRITE
+};
+
+#define HCLGE_8211_PHY_INDIRECT_PAGE 0xa43
+#define HCLGE_8211_PHY_INDIRECT_REG 0x1b
+#define HCLGE_8211_PHY_INDIRECT_DATA 0x1c
+#define HCLGE_8211_PHY_INDIRECT_RANGE1_S 0xDC0
+#define HCLGE_8211_PHY_INDIRECT_RANGE1_E 0xDCF
+#define HCLGE_8211_PHY_INDIRECT_RANGE2_S 0xDE0
+#define HCLGE_8211_PHY_INDIRECT_RANGE2_E 0xDF0
+
+#define HCLGE_8521_PHY_SMI_SDS_ADDR 0xA000
+#define HCLGE_8521_PHY_LDS_MII_ADDR 0x100
+
#define HCLGE_NOTIFY_PARA_CFG_PKT_EN BIT(0)
#define HCLGE_NOTIFY_PARA_CFG_START_EN BIT(1)
#define HCLGE_NOTIFY_PARA_CFG_PKT_NUM_M GENMASK(5, 2)
--
2.34.1