diff --git a/drivers/net/ethernet/hisilicon/hns3/hnae3_ext.h b/drivers/net/ethernet/hisilicon/hns3/hnae3_ext.h index 9803764531273acc2ff08091869f92830db3340c..87add3ee453b3eaa6e143192576f0c4ef9bf3365 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hnae3_ext.h +++ b/drivers/net/ethernet/hisilicon/hns3/hnae3_ext.h @@ -44,6 +44,32 @@ enum hnae3_ext_opcode { HNAE3_EXT_OPC_GET_LANE_STATUS, HNAE3_EXT_OPC_DISABLE_CLOCK, HNAE3_EXT_OPC_SET_PFC_TIME, + HNAE3_EXT_OPC_GET_HILINK_REF_LOS, + HNAE3_EXT_OPC_GET_PORT_FAULT_STATUS, + HNAE3_EXT_OPC_GET_PORT_TYPE, + 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 { + u32 type; + 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; + u8 activity; }; struct hnae3_pfc_storm_para { @@ -54,6 +80,19 @@ struct hnae3_pfc_storm_para { u32 recovery_period_ms; }; +enum hnae3_port_fault_type { + HNAE3_FAULT_TYPE_CDR_FLASH, + HNAE3_FAULT_TYPE_9545_ERR, + HNAE3_FAULT_TYPE_CDR_CORE, + HNAE3_FAULT_TYPE_HILINK_REF_LOS, + HNAE3_FAULT_TYPE_INVALID +}; + +struct hnae3_port_fault { + u32 fault_type; + u32 fault_status; +}; + struct hnae3_notify_pkt_param { u32 ipg; /* inter-packet gap of sending, the unit is one cycle of clock */ u16 num; /* packet number of sending */ diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_ext.c b/drivers/net/ethernet/hisilicon/hns3/hns3_ext.c index 81f59a842209b5a9451de4b2f371e3713bc1ed61..76fb40c27639bf43a864150dc09aa01daabe9c77 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_ext.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_ext.c @@ -439,3 +439,94 @@ int nic_set_pfc_time_cfg(struct net_device *ndev, u16 time) &time, sizeof(time)); } EXPORT_SYMBOL(nic_set_pfc_time_cfg); + +int nic_get_port_fault_status(struct net_device *ndev, u32 fault_type, u32 *status) +{ + int opcode = HNAE3_EXT_OPC_GET_PORT_FAULT_STATUS; + struct hnae3_port_fault fault_para; + int ret; + + if (!status) + return -EINVAL; + + if (fault_type == HNAE3_FAULT_TYPE_HILINK_REF_LOS) + opcode = HNAE3_EXT_OPC_GET_HILINK_REF_LOS; + + fault_para.fault_type = fault_type; + ret = nic_invoke_pri_ops(ndev, opcode, &fault_para, sizeof(fault_para)); + if (ret) + return ret; + + *status = fault_para.fault_status; + return 0; +} +EXPORT_SYMBOL(nic_get_port_fault_status); + +int nic_get_port_wire_type(struct net_device *ndev, u32 *wire_type) +{ + return nic_invoke_pri_ops(ndev, HNAE3_EXT_OPC_GET_PORT_TYPE, + wire_type, sizeof(*wire_type)); +} +EXPORT_SYMBOL(nic_get_port_wire_type); + +int nic_set_mac_state(struct net_device *ndev, int enable) +{ + return nic_invoke_pri_ops(ndev, HNAE3_EXT_OPC_SET_MAC_STATE, + &enable, sizeof(enable)); +} +EXPORT_SYMBOL(nic_set_mac_state); + +int nic_set_led(struct net_device *ndev, int type, int status) +{ + struct hnae3_led_state_para para; + + para.status = status; + para.type = type; + + return nic_invoke_pri_ops(ndev, HNAE3_EXT_OPC_SET_LED, + ¶, sizeof(para)); +} +EXPORT_SYMBOL(nic_set_led); + +int nic_get_led_signal(struct net_device *ndev, struct hnae3_lamp_signal *signal) +{ + return nic_invoke_pri_ops(ndev, HNAE3_EXT_OPC_GET_LED_SIGNAL, + 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 3ac4c4f69b0bc486abbfc9d742454c83087ca032..b5273635909a603819f126c7201a2e465aac1143 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_ext.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_ext.h @@ -14,6 +14,18 @@ #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) \ + nic_get_port_fault_status(ndev, HNAE3_FAULT_TYPE_HILINK_REF_LOS, status) + int nic_netdev_match_check(struct net_device *netdev); void nic_chip_recover_handler(struct net_device *ndev, enum hnae3_event_type_custom event_t); @@ -42,4 +54,13 @@ int nic_disable_net_lane(struct net_device *ndev); int nic_get_net_lane_status(struct net_device *ndev, u32 *status); int nic_disable_clock(struct net_device *ndev); int nic_set_pfc_time_cfg(struct net_device *ndev, u16 time); +int nic_get_port_fault_status(struct net_device *ndev, u32 fault_type, u32 *status); +int nic_get_port_wire_type(struct net_device *ndev, u32 *wire_type); +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 c077b911e31d6441505a5d7e5153c632e198e65b..cc28d9590ef7c34a414adbdded8c5b8daa7dbb53 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h @@ -355,6 +355,12 @@ struct hclge_sfp_info_cmd { u8 rsv[6]; }; +struct hclge_port_fault_cmd { + __le32 fault_status; + __le32 port_type; + u8 rsv[16]; +}; + #define HCLGE_MAC_CFG_FEC_AUTO_EN_B 0 #define HCLGE_MAC_CFG_FEC_MODE_S 1 #define HCLGE_MAC_CFG_FEC_MODE_M GENMASK(3, 1) @@ -879,11 +885,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]; }; enum HCLGE_WOL_MODE { diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_ext.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_ext.c index 3dc13c922be7c8da8d7e8773af2c42f5a74c265f..0cb657cb6c0c66b130a242d9a9ec05169bc3dd7b 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_ext.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_ext.c @@ -582,6 +582,707 @@ static int hclge_set_pause_trans_time(struct hclge_dev *hdev, void *data, return 0; } +static int hclge_get_hilink_ref_los(struct hclge_dev *hdev, void *data, + size_t length) +{ + struct hclge_port_fault_cmd *fault_cmd; + struct hclge_desc desc; + int ret; + + if (length != sizeof(struct hnae3_port_fault)) + return -EINVAL; + + fault_cmd = (struct hclge_port_fault_cmd *)desc.data; + ret = hclge_get_info_from_cmd(hdev, &desc, 1, HCLGE_OPC_CFG_GET_HILINK_REF_LOS); + if (ret) { + dev_err(&hdev->pdev->dev, + "failed to get hilink ref los, ret = %d\n", ret); + return ret; + } + + *(u32 *)data = le32_to_cpu(fault_cmd->fault_status); + return 0; +} + +static int hclge_get_port_fault_status(struct hclge_dev *hdev, void *data, + size_t length) +{ + struct hclge_port_fault_cmd *fault_cmd; + struct hnae3_port_fault *para; + struct hclge_desc desc; + int ret; + + if (length != sizeof(struct hnae3_port_fault)) + return -EINVAL; + + para = (struct hnae3_port_fault *)data; + fault_cmd = (struct hclge_port_fault_cmd *)desc.data; + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_GET_PORT_FAULT_STATUS, true); + fault_cmd->port_type = cpu_to_le32(para->fault_type); + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "failed to get port fault status, type = %u, ret = %d\n", + para->fault_type, ret); + return ret; + } + + para->fault_status = le32_to_cpu(fault_cmd->fault_status); + + return 0; +} + +static int hclge_get_port_wire_type(struct hclge_dev *hdev, void *data, + size_t length) +{ + u8 module_type; + + if (length != sizeof(u32)) + return -EINVAL; + + hclge_get_media_type(&hdev->vport[0].nic, NULL, &module_type); + *(u32 *)data = module_type; + return 0; +} + +static void hclge_set_phy_state(struct hclge_dev *hdev, bool enable) +{ + struct phy_device *phydev = hdev->hw.mac.phydev; + + if (!phydev) + return; + + if (enable && (phydev->state == PHY_READY || phydev->state == PHY_HALTED)) + phy_start(phydev); + else if (!enable && (phy_is_started(phydev) || phydev->state == PHY_DOWN)) + phy_stop(phydev); +} + +static int hclge_set_mac_state(struct hclge_dev *hdev, void *data, + size_t length) +{ + bool enable; + int ret; + + if (length != sizeof(int)) + return -EINVAL; + + enable = !!*(int *)data; + ret = hclge_cfg_mac_mode(hdev, enable); + + if (!ret && !hclge_comm_dev_phy_imp_supported(hdev->ae_dev)) + hclge_set_phy_state(hdev, enable); + + return ret; +} + +static int hclge_set_led(struct hclge_dev *hdev, void *data, + size_t length) +{ + struct hclge_lamp_signal_cmd *para_cmd; + struct hnae3_led_state_para *para; + struct hclge_desc desc; + int ret; + + if (length != sizeof(struct hnae3_led_state_para)) + return -EINVAL; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_SET_LED, false); + para = (struct hnae3_led_state_para *)data; + para_cmd = (struct hclge_lamp_signal_cmd *)desc.data; + para_cmd->type = cpu_to_le32(para->type); + para_cmd->status = cpu_to_le32(para->status); + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) + dev_err(&hdev->pdev->dev, "failed to set led, ret = %d\n", ret); + + return ret; +} + +static int hclge_get_led_signal(struct hclge_dev *hdev, void *data, + size_t length) +{ + struct hclge_lamp_signal_cmd *signal_cmd; + struct hnae3_lamp_signal *signal; + struct hclge_desc desc; + int ret; + + if (length != sizeof(struct hnae3_lamp_signal)) + return -EINVAL; + + ret = hclge_get_info_from_cmd(hdev, &desc, 1, HCLGE_OPC_SET_LED); + if (ret) { + dev_err(&hdev->pdev->dev, + "failed to get led signal, ret = %d\n", ret); + return ret; + } + + signal = (struct hnae3_lamp_signal *)data; + signal_cmd = (struct hclge_lamp_signal_cmd *)desc.data; + signal->error = signal_cmd->error; + signal->locate = signal_cmd->locate; + signal->activity = signal_cmd->activity; + 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 && @@ -749,6 +1450,14 @@ static const hclge_priv_ops_fn hclge_ext_func_arr[] = { [HNAE3_EXT_OPC_GET_LANE_STATUS] = hclge_get_net_lane_status, [HNAE3_EXT_OPC_DISABLE_CLOCK] = hclge_disable_nic_clock, [HNAE3_EXT_OPC_SET_PFC_TIME] = hclge_set_pause_trans_time, + [HNAE3_EXT_OPC_GET_HILINK_REF_LOS] = hclge_get_hilink_ref_los, + [HNAE3_EXT_OPC_GET_PORT_FAULT_STATUS] = hclge_get_port_fault_status, + [HNAE3_EXT_OPC_GET_PORT_TYPE] = hclge_get_port_wire_type, + [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 04f9ab5261e880c8f06acce9fd90db550b7792aa..090152a87c608773bab6c0769064eb2b7b479fb1 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 +#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) @@ -83,6 +115,15 @@ struct hclge_sfp_enable_cmd { __le32 rsv[5]; }; +struct hclge_lamp_signal_cmd { + __le32 type; + __le32 status; + u8 error; + u8 locate; + u8 activity; + u8 rsv[13]; +}; + enum hclge_ext_opcode_type { HCLGE_OPC_CONFIG_NIC_CLOCK = 0x0060, HCLGE_OPC_CONFIG_SWITCH_PARAM = 0x1033, @@ -92,8 +133,11 @@ enum hclge_ext_opcode_type { HCLGE_OPC_CHIP_ID_GET = 0x7003, HCLGE_OPC_GET_CHIP_NUM = 0x7005, HCLGE_OPC_GET_PORT_NUM = 0x7006, + HCLGE_OPC_SET_LED = 0x7007, HCLGE_OPC_DISABLE_NET_LANE = 0x7008, HCLGE_OPC_CFG_PAUSE_STORM_PARA = 0x7019, + HCLGE_OPC_CFG_GET_HILINK_REF_LOS = 0x701B, + HCLGE_OPC_GET_PORT_FAULT_STATUS = 0x7023, HCLGE_OPC_SFP_GET_PRESENT = 0x7101, HCLGE_OPC_SFP_SET_STATUS = 0x7102, }; diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index 6563c27fa7512e95d6b9d513f41eedf83a034e57..5e542e74f7ebbf5466920358e22bd7c89bd8da22 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -7977,7 +7977,7 @@ static void hclge_enable_fd(struct hnae3_handle *handle, bool enable) hclge_task_schedule(hdev, 0); } -static void hclge_cfg_mac_mode(struct hclge_dev *hdev, bool enable) +int hclge_cfg_mac_mode(struct hclge_dev *hdev, bool enable) { struct hclge_desc desc; struct hclge_config_mac_mode_cmd *req = @@ -8004,8 +8004,10 @@ static void hclge_cfg_mac_mode(struct hclge_dev *hdev, bool enable) ret = hclge_cmd_send(&hdev->hw, &desc, 1); if (ret) - dev_err(&hdev->pdev->dev, - "mac enable fail, ret =%d.\n", ret); + dev_err(&hdev->pdev->dev, "failed to %s mac, ret = %d.\n", + enable ? "enable" : "disable", ret); + + return ret; } static int hclge_config_switch_param(struct hclge_dev *hdev, int vfid, @@ -11493,8 +11495,8 @@ static void hclge_get_ksettings_an_result(struct hnae3_handle *handle, *auto_neg = hdev->hw.mac.autoneg; } -static void hclge_get_media_type(struct hnae3_handle *handle, u8 *media_type, - u8 *module_type) +void hclge_get_media_type(struct hnae3_handle *handle, u8 *media_type, + u8 *module_type) { struct hclge_vport *vport = hclge_get_vport(handle); struct hclge_dev *hdev = vport->back; diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h index 66c567fad3d46630690b88d7904e12e77a2b29a3..d75b6c5eec5953855613477b6efcad2899301806 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h @@ -1167,4 +1167,7 @@ struct hclge_vport *hclge_get_vf_vport(struct hclge_dev *hdev, int vf); int hclge_inform_vf_reset(struct hclge_vport *vport, u16 reset_type); void hclge_reset_task_schedule(struct hclge_dev *hdev); void hclge_reset_event(struct pci_dev *pdev, struct hnae3_handle *handle); +void hclge_get_media_type(struct hnae3_handle *handle, u8 *media_type, + u8 *module_type); +int hclge_cfg_mac_mode(struct hclge_dev *hdev, bool enable); #endif