777 lines
22 KiB
Diff
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,
|
|
+ ¶, 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,
|
|
+ ¶, 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, ¶->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,
|
|
+ ¶->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,
|
|
+ ¶->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,
|
|
+ ¶->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,
|
|
+ ¶->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,
|
|
+ ¶->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,
|
|
+ ¶->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,
|
|
+ ¶->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, ¶->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,
|
|
+ ¶->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
|
|
|