| // SPDX-License-Identifier: GPL-2.0-only |
| /* |
| * Huawei HiNIC PCI Express Linux driver |
| * Copyright(c) 2017 Huawei Technologies Co., Ltd |
| */ |
| |
| #include <linux/types.h> |
| #include <linux/netdevice.h> |
| #include <linux/etherdevice.h> |
| #include <linux/if_vlan.h> |
| #include <linux/pci.h> |
| #include <linux/device.h> |
| #include <linux/errno.h> |
| |
| #include "hinic_hw_if.h" |
| #include "hinic_hw_dev.h" |
| #include "hinic_port.h" |
| #include "hinic_dev.h" |
| |
| enum mac_op { |
| MAC_DEL, |
| MAC_SET, |
| }; |
| |
| /** |
| * change_mac - change(add or delete) mac address |
| * @nic_dev: nic device |
| * @addr: mac address |
| * @vlan_id: vlan number to set with the mac |
| * @op: add or delete the mac |
| * |
| * Return 0 - Success, negative - Failure |
| **/ |
| static int change_mac(struct hinic_dev *nic_dev, const u8 *addr, |
| u16 vlan_id, enum mac_op op) |
| { |
| struct hinic_hwdev *hwdev = nic_dev->hwdev; |
| struct hinic_port_mac_cmd port_mac_cmd; |
| struct hinic_hwif *hwif = hwdev->hwif; |
| u16 out_size = sizeof(port_mac_cmd); |
| struct pci_dev *pdev = hwif->pdev; |
| enum hinic_port_cmd cmd; |
| int err; |
| |
| if (op == MAC_SET) |
| cmd = HINIC_PORT_CMD_SET_MAC; |
| else |
| cmd = HINIC_PORT_CMD_DEL_MAC; |
| |
| port_mac_cmd.func_idx = HINIC_HWIF_FUNC_IDX(hwif); |
| port_mac_cmd.vlan_id = vlan_id; |
| memcpy(port_mac_cmd.mac, addr, ETH_ALEN); |
| |
| err = hinic_port_msg_cmd(hwdev, cmd, &port_mac_cmd, |
| sizeof(port_mac_cmd), |
| &port_mac_cmd, &out_size); |
| if (err || out_size != sizeof(port_mac_cmd) || |
| (port_mac_cmd.status && |
| (port_mac_cmd.status != HINIC_PF_SET_VF_ALREADY || !HINIC_IS_VF(hwif)) && |
| port_mac_cmd.status != HINIC_MGMT_STATUS_EXIST)) { |
| dev_err(&pdev->dev, "Failed to change MAC, err: %d, status: 0x%x, out size: 0x%x\n", |
| err, port_mac_cmd.status, out_size); |
| return -EFAULT; |
| } |
| |
| if (port_mac_cmd.status == HINIC_PF_SET_VF_ALREADY) { |
| dev_warn(&pdev->dev, "PF has already set VF mac, ignore %s operation\n", |
| (op == MAC_SET) ? "set" : "del"); |
| return HINIC_PF_SET_VF_ALREADY; |
| } |
| |
| if (cmd == HINIC_PORT_CMD_SET_MAC && port_mac_cmd.status == |
| HINIC_MGMT_STATUS_EXIST) |
| dev_warn(&pdev->dev, "MAC is repeated, ignore set operation\n"); |
| |
| return 0; |
| } |
| |
| /** |
| * hinic_port_add_mac - add mac address |
| * @nic_dev: nic device |
| * @addr: mac address |
| * @vlan_id: vlan number to set with the mac |
| * |
| * Return 0 - Success, negative - Failure |
| **/ |
| int hinic_port_add_mac(struct hinic_dev *nic_dev, |
| const u8 *addr, u16 vlan_id) |
| { |
| return change_mac(nic_dev, addr, vlan_id, MAC_SET); |
| } |
| |
| /** |
| * hinic_port_del_mac - remove mac address |
| * @nic_dev: nic device |
| * @addr: mac address |
| * @vlan_id: vlan number that is connected to the mac |
| * |
| * Return 0 - Success, negative - Failure |
| **/ |
| int hinic_port_del_mac(struct hinic_dev *nic_dev, const u8 *addr, |
| u16 vlan_id) |
| { |
| return change_mac(nic_dev, addr, vlan_id, MAC_DEL); |
| } |
| |
| /** |
| * hinic_port_get_mac - get the mac address of the nic device |
| * @nic_dev: nic device |
| * @addr: returned mac address |
| * |
| * Return 0 - Success, negative - Failure |
| **/ |
| int hinic_port_get_mac(struct hinic_dev *nic_dev, u8 *addr) |
| { |
| struct hinic_hwdev *hwdev = nic_dev->hwdev; |
| struct hinic_port_mac_cmd port_mac_cmd; |
| struct hinic_hwif *hwif = hwdev->hwif; |
| u16 out_size = sizeof(port_mac_cmd); |
| struct pci_dev *pdev = hwif->pdev; |
| int err; |
| |
| port_mac_cmd.func_idx = HINIC_HWIF_FUNC_IDX(hwif); |
| |
| err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_MAC, |
| &port_mac_cmd, sizeof(port_mac_cmd), |
| &port_mac_cmd, &out_size); |
| if (err || out_size != sizeof(port_mac_cmd) || port_mac_cmd.status) { |
| dev_err(&pdev->dev, "Failed to get mac, err: %d, status: 0x%x, out size: 0x%x\n", |
| err, port_mac_cmd.status, out_size); |
| return -EFAULT; |
| } |
| |
| memcpy(addr, port_mac_cmd.mac, ETH_ALEN); |
| return 0; |
| } |
| |
| /** |
| * hinic_port_set_mtu - set mtu |
| * @nic_dev: nic device |
| * @new_mtu: new mtu |
| * |
| * Return 0 - Success, negative - Failure |
| **/ |
| int hinic_port_set_mtu(struct hinic_dev *nic_dev, int new_mtu) |
| { |
| struct hinic_hwdev *hwdev = nic_dev->hwdev; |
| struct hinic_port_mtu_cmd port_mtu_cmd; |
| struct hinic_hwif *hwif = hwdev->hwif; |
| u16 out_size = sizeof(port_mtu_cmd); |
| struct pci_dev *pdev = hwif->pdev; |
| int err; |
| |
| port_mtu_cmd.func_idx = HINIC_HWIF_FUNC_IDX(hwif); |
| port_mtu_cmd.mtu = new_mtu; |
| |
| err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_CHANGE_MTU, |
| &port_mtu_cmd, sizeof(port_mtu_cmd), |
| &port_mtu_cmd, &out_size); |
| if (err || out_size != sizeof(port_mtu_cmd) || port_mtu_cmd.status) { |
| dev_err(&pdev->dev, "Failed to set mtu, err: %d, status: 0x%x, out size: 0x%x\n", |
| err, port_mtu_cmd.status, out_size); |
| return -EFAULT; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * hinic_port_add_vlan - add vlan to the nic device |
| * @nic_dev: nic device |
| * @vlan_id: the vlan number to add |
| * |
| * Return 0 - Success, negative - Failure |
| **/ |
| int hinic_port_add_vlan(struct hinic_dev *nic_dev, u16 vlan_id) |
| { |
| struct hinic_hwdev *hwdev = nic_dev->hwdev; |
| struct hinic_port_vlan_cmd port_vlan_cmd; |
| |
| port_vlan_cmd.func_idx = HINIC_HWIF_FUNC_IDX(hwdev->hwif); |
| port_vlan_cmd.vlan_id = vlan_id; |
| |
| return hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_ADD_VLAN, |
| &port_vlan_cmd, sizeof(port_vlan_cmd), |
| NULL, NULL); |
| } |
| |
| /** |
| * hinic_port_del_vlan - delete vlan from the nic device |
| * @nic_dev: nic device |
| * @vlan_id: the vlan number to delete |
| * |
| * Return 0 - Success, negative - Failure |
| **/ |
| int hinic_port_del_vlan(struct hinic_dev *nic_dev, u16 vlan_id) |
| { |
| struct hinic_hwdev *hwdev = nic_dev->hwdev; |
| struct hinic_port_vlan_cmd port_vlan_cmd; |
| |
| port_vlan_cmd.func_idx = HINIC_HWIF_FUNC_IDX(hwdev->hwif); |
| port_vlan_cmd.vlan_id = vlan_id; |
| |
| return hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_DEL_VLAN, |
| &port_vlan_cmd, sizeof(port_vlan_cmd), |
| NULL, NULL); |
| } |
| |
| /** |
| * hinic_port_set_rx_mode - set rx mode in the nic device |
| * @nic_dev: nic device |
| * @rx_mode: the rx mode to set |
| * |
| * Return 0 - Success, negative - Failure |
| **/ |
| int hinic_port_set_rx_mode(struct hinic_dev *nic_dev, u32 rx_mode) |
| { |
| struct hinic_hwdev *hwdev = nic_dev->hwdev; |
| struct hinic_port_rx_mode_cmd rx_mode_cmd; |
| |
| rx_mode_cmd.func_idx = HINIC_HWIF_FUNC_IDX(hwdev->hwif); |
| rx_mode_cmd.rx_mode = rx_mode; |
| |
| return hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_RX_MODE, |
| &rx_mode_cmd, sizeof(rx_mode_cmd), |
| NULL, NULL); |
| } |
| |
| /** |
| * hinic_port_link_state - get the link state |
| * @nic_dev: nic device |
| * @link_state: the returned link state |
| * |
| * Return 0 - Success, negative - Failure |
| **/ |
| int hinic_port_link_state(struct hinic_dev *nic_dev, |
| enum hinic_port_link_state *link_state) |
| { |
| struct hinic_hwdev *hwdev = nic_dev->hwdev; |
| struct hinic_hwif *hwif = hwdev->hwif; |
| struct hinic_port_link_cmd link_cmd; |
| struct pci_dev *pdev = hwif->pdev; |
| u16 out_size = sizeof(link_cmd); |
| int err; |
| |
| link_cmd.func_idx = HINIC_HWIF_FUNC_IDX(hwif); |
| |
| err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_LINK_STATE, |
| &link_cmd, sizeof(link_cmd), |
| &link_cmd, &out_size); |
| if (err || out_size != sizeof(link_cmd) || link_cmd.status) { |
| dev_err(&pdev->dev, "Failed to get link state, err: %d, status: 0x%x, out size: 0x%x\n", |
| err, link_cmd.status, out_size); |
| return -EINVAL; |
| } |
| |
| *link_state = link_cmd.state; |
| return 0; |
| } |
| |
| /** |
| * hinic_port_set_state - set port state |
| * @nic_dev: nic device |
| * @state: the state to set |
| * |
| * Return 0 - Success, negative - Failure |
| **/ |
| int hinic_port_set_state(struct hinic_dev *nic_dev, enum hinic_port_state state) |
| { |
| struct hinic_hwdev *hwdev = nic_dev->hwdev; |
| struct hinic_port_state_cmd port_state; |
| struct hinic_hwif *hwif = hwdev->hwif; |
| struct pci_dev *pdev = hwif->pdev; |
| u16 out_size = sizeof(port_state); |
| int err; |
| |
| if (HINIC_IS_VF(hwdev->hwif)) |
| return 0; |
| |
| port_state.state = state; |
| |
| err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_PORT_STATE, |
| &port_state, sizeof(port_state), |
| &port_state, &out_size); |
| if (err || out_size != sizeof(port_state) || port_state.status) { |
| dev_err(&pdev->dev, "Failed to set port state, err: %d, status: 0x%x, out size: 0x%x\n", |
| err, port_state.status, out_size); |
| return -EFAULT; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * hinic_port_set_func_state- set func device state |
| * @nic_dev: nic device |
| * @state: the state to set |
| * |
| * Return 0 - Success, negative - Failure |
| **/ |
| int hinic_port_set_func_state(struct hinic_dev *nic_dev, |
| enum hinic_func_port_state state) |
| { |
| struct hinic_port_func_state_cmd func_state; |
| struct hinic_hwdev *hwdev = nic_dev->hwdev; |
| struct hinic_hwif *hwif = hwdev->hwif; |
| struct pci_dev *pdev = hwif->pdev; |
| u16 out_size = sizeof(func_state); |
| int err; |
| |
| func_state.func_idx = HINIC_HWIF_FUNC_IDX(hwif); |
| func_state.state = state; |
| |
| err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_FUNC_STATE, |
| &func_state, sizeof(func_state), |
| &func_state, &out_size); |
| if (err || out_size != sizeof(func_state) || func_state.status) { |
| dev_err(&pdev->dev, "Failed to set port func state, err: %d, status: 0x%x, out size: 0x%x\n", |
| err, func_state.status, out_size); |
| return -EFAULT; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * hinic_port_get_cap - get port capabilities |
| * @nic_dev: nic device |
| * @port_cap: returned port capabilities |
| * |
| * Return 0 - Success, negative - Failure |
| **/ |
| int hinic_port_get_cap(struct hinic_dev *nic_dev, |
| struct hinic_port_cap *port_cap) |
| { |
| struct hinic_hwdev *hwdev = nic_dev->hwdev; |
| struct hinic_hwif *hwif = hwdev->hwif; |
| struct pci_dev *pdev = hwif->pdev; |
| u16 out_size = sizeof(*port_cap); |
| int err; |
| |
| port_cap->func_idx = HINIC_HWIF_FUNC_IDX(hwif); |
| |
| err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_CAP, |
| port_cap, sizeof(*port_cap), |
| port_cap, &out_size); |
| if (err || out_size != sizeof(*port_cap) || port_cap->status) { |
| dev_err(&pdev->dev, |
| "Failed to get port capabilities, err: %d, status: 0x%x, out size: 0x%x\n", |
| err, port_cap->status, out_size); |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| /** |
| * hinic_port_set_tso - set port tso configuration |
| * @nic_dev: nic device |
| * @state: the tso state to set |
| * |
| * Return 0 - Success, negative - Failure |
| **/ |
| int hinic_port_set_tso(struct hinic_dev *nic_dev, enum hinic_tso_state state) |
| { |
| struct hinic_hwdev *hwdev = nic_dev->hwdev; |
| struct hinic_hwif *hwif = hwdev->hwif; |
| struct hinic_tso_config tso_cfg = {0}; |
| struct pci_dev *pdev = hwif->pdev; |
| u16 out_size = sizeof(tso_cfg); |
| int err; |
| |
| tso_cfg.func_id = HINIC_HWIF_FUNC_IDX(hwif); |
| tso_cfg.tso_en = state; |
| |
| err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_TSO, |
| &tso_cfg, sizeof(tso_cfg), |
| &tso_cfg, &out_size); |
| if (err || out_size != sizeof(tso_cfg) || tso_cfg.status) { |
| dev_err(&pdev->dev, |
| "Failed to set port tso, err: %d, status: 0x%x, out size: 0x%x\n", |
| err, tso_cfg.status, out_size); |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| int hinic_set_rx_csum_offload(struct hinic_dev *nic_dev, u32 en) |
| { |
| struct hinic_checksum_offload rx_csum_cfg = {0}; |
| struct hinic_hwdev *hwdev = nic_dev->hwdev; |
| u16 out_size = sizeof(rx_csum_cfg); |
| struct hinic_hwif *hwif; |
| struct pci_dev *pdev; |
| int err; |
| |
| if (!hwdev) |
| return -EINVAL; |
| |
| hwif = hwdev->hwif; |
| pdev = hwif->pdev; |
| rx_csum_cfg.func_id = HINIC_HWIF_FUNC_IDX(hwif); |
| rx_csum_cfg.rx_csum_offload = en; |
| |
| err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_RX_CSUM, |
| &rx_csum_cfg, sizeof(rx_csum_cfg), |
| &rx_csum_cfg, &out_size); |
| if (err || !out_size || rx_csum_cfg.status) { |
| dev_err(&pdev->dev, |
| "Failed to set rx csum offload, err: %d, status: 0x%x, out size: 0x%x\n", |
| err, rx_csum_cfg.status, out_size); |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| int hinic_set_rx_vlan_offload(struct hinic_dev *nic_dev, u8 en) |
| { |
| struct hinic_hwdev *hwdev = nic_dev->hwdev; |
| struct hinic_vlan_cfg vlan_cfg; |
| struct hinic_hwif *hwif; |
| struct pci_dev *pdev; |
| u16 out_size; |
| int err; |
| |
| if (!hwdev) |
| return -EINVAL; |
| |
| out_size = sizeof(vlan_cfg); |
| hwif = hwdev->hwif; |
| pdev = hwif->pdev; |
| vlan_cfg.func_id = HINIC_HWIF_FUNC_IDX(hwif); |
| vlan_cfg.vlan_rx_offload = en; |
| |
| err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_RX_VLAN_OFFLOAD, |
| &vlan_cfg, sizeof(vlan_cfg), |
| &vlan_cfg, &out_size); |
| if (err || !out_size || vlan_cfg.status) { |
| dev_err(&pdev->dev, |
| "Failed to set rx vlan offload, err: %d, status: 0x%x, out size: 0x%x\n", |
| err, vlan_cfg.status, out_size); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| int hinic_set_vlan_fliter(struct hinic_dev *nic_dev, u32 en) |
| { |
| struct hinic_hwdev *hwdev = nic_dev->hwdev; |
| struct hinic_hwif *hwif = hwdev->hwif; |
| struct pci_dev *pdev = hwif->pdev; |
| struct hinic_vlan_filter vlan_filter; |
| u16 out_size = sizeof(vlan_filter); |
| int err; |
| |
| if (!hwdev) |
| return -EINVAL; |
| |
| vlan_filter.func_idx = HINIC_HWIF_FUNC_IDX(hwif); |
| vlan_filter.enable = en; |
| |
| err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_VLAN_FILTER, |
| &vlan_filter, sizeof(vlan_filter), |
| &vlan_filter, &out_size); |
| if (vlan_filter.status == HINIC_MGMT_CMD_UNSUPPORTED) { |
| err = HINIC_MGMT_CMD_UNSUPPORTED; |
| } else if ((err == HINIC_MBOX_VF_CMD_ERROR) && |
| HINIC_IS_VF(hwif)) { |
| err = HINIC_MGMT_CMD_UNSUPPORTED; |
| } else if (err || !out_size || vlan_filter.status) { |
| dev_err(&pdev->dev, |
| "Failed to set vlan fliter, err: %d, status: 0x%x, out size: 0x%x\n", |
| err, vlan_filter.status, out_size); |
| err = -EINVAL; |
| } |
| |
| return err; |
| } |
| |
| int hinic_set_max_qnum(struct hinic_dev *nic_dev, u8 num_rqs) |
| { |
| struct hinic_hwdev *hwdev = nic_dev->hwdev; |
| struct hinic_hwif *hwif = hwdev->hwif; |
| struct hinic_rq_num rq_num = { 0 }; |
| struct pci_dev *pdev = hwif->pdev; |
| u16 out_size = sizeof(rq_num); |
| int err; |
| |
| rq_num.func_id = HINIC_HWIF_FUNC_IDX(hwif); |
| rq_num.num_rqs = num_rqs; |
| rq_num.rq_depth = ilog2(nic_dev->rq_depth); |
| |
| err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_RQ_IQ_MAP, |
| &rq_num, sizeof(rq_num), |
| &rq_num, &out_size); |
| if (err || !out_size || rq_num.status) { |
| dev_err(&pdev->dev, |
| "Failed to set rxq number, err: %d, status: 0x%x, out size: 0x%x\n", |
| err, rq_num.status, out_size); |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| static int hinic_set_rx_lro(struct hinic_dev *nic_dev, u8 ipv4_en, u8 ipv6_en, |
| u8 max_wqe_num) |
| { |
| struct hinic_hwdev *hwdev = nic_dev->hwdev; |
| struct hinic_lro_config lro_cfg = { 0 }; |
| struct hinic_hwif *hwif = hwdev->hwif; |
| struct pci_dev *pdev = hwif->pdev; |
| u16 out_size = sizeof(lro_cfg); |
| int err; |
| |
| lro_cfg.func_id = HINIC_HWIF_FUNC_IDX(hwif); |
| lro_cfg.lro_ipv4_en = ipv4_en; |
| lro_cfg.lro_ipv6_en = ipv6_en; |
| lro_cfg.lro_max_wqe_num = max_wqe_num; |
| |
| err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_LRO, |
| &lro_cfg, sizeof(lro_cfg), |
| &lro_cfg, &out_size); |
| if (err || !out_size || lro_cfg.status) { |
| dev_err(&pdev->dev, |
| "Failed to set lro offload, err: %d, status: 0x%x, out size: 0x%x\n", |
| err, lro_cfg.status, out_size); |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| static int hinic_set_rx_lro_timer(struct hinic_dev *nic_dev, u32 timer_value) |
| { |
| struct hinic_hwdev *hwdev = nic_dev->hwdev; |
| struct hinic_lro_timer lro_timer = { 0 }; |
| struct hinic_hwif *hwif = hwdev->hwif; |
| struct pci_dev *pdev = hwif->pdev; |
| u16 out_size = sizeof(lro_timer); |
| int err; |
| |
| lro_timer.status = 0; |
| lro_timer.type = 0; |
| lro_timer.enable = 1; |
| lro_timer.timer = timer_value; |
| |
| err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_LRO_TIMER, |
| &lro_timer, sizeof(lro_timer), |
| &lro_timer, &out_size); |
| if (lro_timer.status == 0xFF) { |
| /* For this case, we think status (0xFF) is OK */ |
| lro_timer.status = 0; |
| dev_dbg(&pdev->dev, |
| "Set lro timer not supported by the current FW version, it will be 1ms default\n"); |
| } |
| |
| if (err || !out_size || lro_timer.status) { |
| dev_err(&pdev->dev, |
| "Failed to set lro timer, err: %d, status: 0x%x, out size: 0x%x\n", |
| err, lro_timer.status, out_size); |
| |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| int hinic_set_rx_lro_state(struct hinic_dev *nic_dev, u8 lro_en, |
| u32 lro_timer, u32 wqe_num) |
| { |
| struct hinic_hwdev *hwdev = nic_dev->hwdev; |
| u8 ipv4_en; |
| u8 ipv6_en; |
| int err; |
| |
| if (!hwdev) |
| return -EINVAL; |
| |
| ipv4_en = lro_en ? 1 : 0; |
| ipv6_en = lro_en ? 1 : 0; |
| |
| err = hinic_set_rx_lro(nic_dev, ipv4_en, ipv6_en, (u8)wqe_num); |
| if (err) |
| return err; |
| |
| if (HINIC_IS_VF(nic_dev->hwdev->hwif)) |
| return 0; |
| |
| err = hinic_set_rx_lro_timer(nic_dev, lro_timer); |
| if (err) |
| return err; |
| |
| return 0; |
| } |
| |
| int hinic_rss_set_indir_tbl(struct hinic_dev *nic_dev, u32 tmpl_idx, |
| const u32 *indir_table) |
| { |
| struct hinic_rss_indirect_tbl *indir_tbl; |
| struct hinic_func_to_io *func_to_io; |
| struct hinic_cmdq_buf cmd_buf; |
| struct hinic_hwdev *hwdev; |
| struct hinic_hwif *hwif; |
| struct pci_dev *pdev; |
| u32 indir_size; |
| u64 out_param; |
| int err, i; |
| u32 *temp; |
| |
| hwdev = nic_dev->hwdev; |
| func_to_io = &hwdev->func_to_io; |
| hwif = hwdev->hwif; |
| pdev = hwif->pdev; |
| |
| err = hinic_alloc_cmdq_buf(&func_to_io->cmdqs, &cmd_buf); |
| if (err) { |
| dev_err(&pdev->dev, "Failed to allocate cmdq buf\n"); |
| return err; |
| } |
| |
| cmd_buf.size = sizeof(*indir_tbl); |
| |
| indir_tbl = cmd_buf.buf; |
| indir_tbl->group_index = cpu_to_be32(tmpl_idx); |
| |
| for (i = 0; i < HINIC_RSS_INDIR_SIZE; i++) { |
| indir_tbl->entry[i] = indir_table[i]; |
| |
| if (0x3 == (i & 0x3)) { |
| temp = (u32 *)&indir_tbl->entry[i - 3]; |
| *temp = cpu_to_be32(*temp); |
| } |
| } |
| |
| /* cfg the rss indirect table by command queue */ |
| indir_size = HINIC_RSS_INDIR_SIZE / 2; |
| indir_tbl->offset = 0; |
| indir_tbl->size = cpu_to_be32(indir_size); |
| |
| err = hinic_cmdq_direct_resp(&func_to_io->cmdqs, HINIC_MOD_L2NIC, |
| HINIC_UCODE_CMD_SET_RSS_INDIR_TABLE, |
| &cmd_buf, &out_param); |
| if (err || out_param != 0) { |
| dev_err(&pdev->dev, "Failed to set rss indir table\n"); |
| err = -EFAULT; |
| goto free_buf; |
| } |
| |
| indir_tbl->offset = cpu_to_be32(indir_size); |
| indir_tbl->size = cpu_to_be32(indir_size); |
| memcpy(&indir_tbl->entry[0], &indir_tbl->entry[indir_size], indir_size); |
| |
| err = hinic_cmdq_direct_resp(&func_to_io->cmdqs, HINIC_MOD_L2NIC, |
| HINIC_UCODE_CMD_SET_RSS_INDIR_TABLE, |
| &cmd_buf, &out_param); |
| if (err || out_param != 0) { |
| dev_err(&pdev->dev, "Failed to set rss indir table\n"); |
| err = -EFAULT; |
| } |
| |
| free_buf: |
| hinic_free_cmdq_buf(&func_to_io->cmdqs, &cmd_buf); |
| |
| return err; |
| } |
| |
| int hinic_rss_get_indir_tbl(struct hinic_dev *nic_dev, u32 tmpl_idx, |
| u32 *indir_table) |
| { |
| struct hinic_rss_indir_table rss_cfg = { 0 }; |
| struct hinic_hwdev *hwdev = nic_dev->hwdev; |
| struct hinic_hwif *hwif = hwdev->hwif; |
| struct pci_dev *pdev = hwif->pdev; |
| u16 out_size = sizeof(rss_cfg); |
| int err = 0, i; |
| |
| rss_cfg.func_id = HINIC_HWIF_FUNC_IDX(hwif); |
| rss_cfg.template_id = tmpl_idx; |
| |
| err = hinic_port_msg_cmd(hwdev, |
| HINIC_PORT_CMD_GET_RSS_TEMPLATE_INDIR_TBL, |
| &rss_cfg, sizeof(rss_cfg), &rss_cfg, |
| &out_size); |
| if (err || !out_size || rss_cfg.status) { |
| dev_err(&pdev->dev, "Failed to get indir table, err: %d, status: 0x%x, out size: 0x%x\n", |
| err, rss_cfg.status, out_size); |
| return -EINVAL; |
| } |
| |
| hinic_be32_to_cpu(rss_cfg.indir, HINIC_RSS_INDIR_SIZE); |
| for (i = 0; i < HINIC_RSS_INDIR_SIZE; i++) |
| indir_table[i] = rss_cfg.indir[i]; |
| |
| return 0; |
| } |
| |
| int hinic_set_rss_type(struct hinic_dev *nic_dev, u32 tmpl_idx, |
| struct hinic_rss_type rss_type) |
| { |
| struct hinic_rss_context_tbl *ctx_tbl; |
| struct hinic_func_to_io *func_to_io; |
| struct hinic_cmdq_buf cmd_buf; |
| struct hinic_hwdev *hwdev; |
| struct hinic_hwif *hwif; |
| struct pci_dev *pdev; |
| u64 out_param; |
| u32 ctx = 0; |
| int err; |
| |
| hwdev = nic_dev->hwdev; |
| func_to_io = &hwdev->func_to_io; |
| hwif = hwdev->hwif; |
| pdev = hwif->pdev; |
| |
| err = hinic_alloc_cmdq_buf(&func_to_io->cmdqs, &cmd_buf); |
| if (err) { |
| dev_err(&pdev->dev, "Failed to allocate cmd buf\n"); |
| return -ENOMEM; |
| } |
| |
| ctx |= HINIC_RSS_TYPE_SET(1, VALID) | |
| HINIC_RSS_TYPE_SET(rss_type.ipv4, IPV4) | |
| HINIC_RSS_TYPE_SET(rss_type.ipv6, IPV6) | |
| HINIC_RSS_TYPE_SET(rss_type.ipv6_ext, IPV6_EXT) | |
| HINIC_RSS_TYPE_SET(rss_type.tcp_ipv4, TCP_IPV4) | |
| HINIC_RSS_TYPE_SET(rss_type.tcp_ipv6, TCP_IPV6) | |
| HINIC_RSS_TYPE_SET(rss_type.tcp_ipv6_ext, TCP_IPV6_EXT) | |
| HINIC_RSS_TYPE_SET(rss_type.udp_ipv4, UDP_IPV4) | |
| HINIC_RSS_TYPE_SET(rss_type.udp_ipv6, UDP_IPV6); |
| |
| cmd_buf.size = sizeof(struct hinic_rss_context_tbl); |
| |
| ctx_tbl = (struct hinic_rss_context_tbl *)cmd_buf.buf; |
| ctx_tbl->group_index = cpu_to_be32(tmpl_idx); |
| ctx_tbl->offset = 0; |
| ctx_tbl->size = sizeof(u32); |
| ctx_tbl->size = cpu_to_be32(ctx_tbl->size); |
| ctx_tbl->rsvd = 0; |
| ctx_tbl->ctx = cpu_to_be32(ctx); |
| |
| /* cfg the rss context table by command queue */ |
| err = hinic_cmdq_direct_resp(&func_to_io->cmdqs, HINIC_MOD_L2NIC, |
| HINIC_UCODE_CMD_SET_RSS_CONTEXT_TABLE, |
| &cmd_buf, &out_param); |
| |
| hinic_free_cmdq_buf(&func_to_io->cmdqs, &cmd_buf); |
| |
| if (err || out_param != 0) { |
| dev_err(&pdev->dev, "Failed to set rss context table, err: %d\n", |
| err); |
| return -EFAULT; |
| } |
| |
| return 0; |
| } |
| |
| int hinic_get_rss_type(struct hinic_dev *nic_dev, u32 tmpl_idx, |
| struct hinic_rss_type *rss_type) |
| { |
| struct hinic_rss_context_table ctx_tbl = { 0 }; |
| struct hinic_hwdev *hwdev = nic_dev->hwdev; |
| u16 out_size = sizeof(ctx_tbl); |
| struct hinic_hwif *hwif; |
| struct pci_dev *pdev; |
| int err; |
| |
| if (!hwdev || !rss_type) |
| return -EINVAL; |
| |
| hwif = hwdev->hwif; |
| pdev = hwif->pdev; |
| |
| ctx_tbl.func_id = HINIC_HWIF_FUNC_IDX(hwif); |
| ctx_tbl.template_id = tmpl_idx; |
| |
| err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_RSS_CTX_TBL, |
| &ctx_tbl, sizeof(ctx_tbl), |
| &ctx_tbl, &out_size); |
| if (err || !out_size || ctx_tbl.status) { |
| dev_err(&pdev->dev, "Failed to get hash type, err: %d, status: 0x%x, out size: 0x%x\n", |
| err, ctx_tbl.status, out_size); |
| return -EINVAL; |
| } |
| |
| rss_type->ipv4 = HINIC_RSS_TYPE_GET(ctx_tbl.context, IPV4); |
| rss_type->ipv6 = HINIC_RSS_TYPE_GET(ctx_tbl.context, IPV6); |
| rss_type->ipv6_ext = HINIC_RSS_TYPE_GET(ctx_tbl.context, IPV6_EXT); |
| rss_type->tcp_ipv4 = HINIC_RSS_TYPE_GET(ctx_tbl.context, TCP_IPV4); |
| rss_type->tcp_ipv6 = HINIC_RSS_TYPE_GET(ctx_tbl.context, TCP_IPV6); |
| rss_type->tcp_ipv6_ext = HINIC_RSS_TYPE_GET(ctx_tbl.context, |
| TCP_IPV6_EXT); |
| rss_type->udp_ipv4 = HINIC_RSS_TYPE_GET(ctx_tbl.context, UDP_IPV4); |
| rss_type->udp_ipv6 = HINIC_RSS_TYPE_GET(ctx_tbl.context, UDP_IPV6); |
| |
| return 0; |
| } |
| |
| int hinic_rss_set_template_tbl(struct hinic_dev *nic_dev, u32 template_id, |
| const u8 *temp) |
| { |
| struct hinic_hwdev *hwdev = nic_dev->hwdev; |
| struct hinic_hwif *hwif = hwdev->hwif; |
| struct hinic_rss_key rss_key = { 0 }; |
| struct pci_dev *pdev = hwif->pdev; |
| u16 out_size = sizeof(rss_key); |
| int err; |
| |
| rss_key.func_id = HINIC_HWIF_FUNC_IDX(hwif); |
| rss_key.template_id = template_id; |
| memcpy(rss_key.key, temp, HINIC_RSS_KEY_SIZE); |
| |
| err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_RSS_TEMPLATE_TBL, |
| &rss_key, sizeof(rss_key), |
| &rss_key, &out_size); |
| if (err || !out_size || rss_key.status) { |
| dev_err(&pdev->dev, |
| "Failed to set rss hash key, err: %d, status: 0x%x, out size: 0x%x\n", |
| err, rss_key.status, out_size); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| int hinic_rss_get_template_tbl(struct hinic_dev *nic_dev, u32 tmpl_idx, |
| u8 *temp) |
| { |
| struct hinic_rss_template_key temp_key = { 0 }; |
| struct hinic_hwdev *hwdev = nic_dev->hwdev; |
| u16 out_size = sizeof(temp_key); |
| struct hinic_hwif *hwif; |
| struct pci_dev *pdev; |
| int err; |
| |
| if (!hwdev || !temp) |
| return -EINVAL; |
| |
| hwif = hwdev->hwif; |
| pdev = hwif->pdev; |
| |
| temp_key.func_id = HINIC_HWIF_FUNC_IDX(hwif); |
| temp_key.template_id = tmpl_idx; |
| |
| err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_RSS_TEMPLATE_TBL, |
| &temp_key, sizeof(temp_key), |
| &temp_key, &out_size); |
| if (err || !out_size || temp_key.status) { |
| dev_err(&pdev->dev, "Failed to set hash key, err: %d, status: 0x%x, out size: 0x%x\n", |
| err, temp_key.status, out_size); |
| return -EINVAL; |
| } |
| |
| memcpy(temp, temp_key.key, HINIC_RSS_KEY_SIZE); |
| |
| return 0; |
| } |
| |
| int hinic_rss_set_hash_engine(struct hinic_dev *nic_dev, u8 template_id, |
| u8 type) |
| { |
| struct hinic_rss_engine_type rss_engine = { 0 }; |
| struct hinic_hwdev *hwdev = nic_dev->hwdev; |
| struct hinic_hwif *hwif = hwdev->hwif; |
| struct pci_dev *pdev = hwif->pdev; |
| u16 out_size = sizeof(rss_engine); |
| int err; |
| |
| rss_engine.func_id = HINIC_HWIF_FUNC_IDX(hwif); |
| rss_engine.hash_engine = type; |
| rss_engine.template_id = template_id; |
| |
| err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_RSS_HASH_ENGINE, |
| &rss_engine, sizeof(rss_engine), |
| &rss_engine, &out_size); |
| if (err || !out_size || rss_engine.status) { |
| dev_err(&pdev->dev, |
| "Failed to set hash engine, err: %d, status: 0x%x, out size: 0x%x\n", |
| err, rss_engine.status, out_size); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| int hinic_rss_get_hash_engine(struct hinic_dev *nic_dev, u8 tmpl_idx, u8 *type) |
| { |
| struct hinic_rss_engine_type hash_type = { 0 }; |
| struct hinic_hwdev *hwdev = nic_dev->hwdev; |
| u16 out_size = sizeof(hash_type); |
| struct hinic_hwif *hwif; |
| struct pci_dev *pdev; |
| int err; |
| |
| if (!hwdev || !type) |
| return -EINVAL; |
| |
| hwif = hwdev->hwif; |
| pdev = hwif->pdev; |
| |
| hash_type.func_id = HINIC_HWIF_FUNC_IDX(hwif); |
| hash_type.template_id = tmpl_idx; |
| |
| err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_RSS_HASH_ENGINE, |
| &hash_type, sizeof(hash_type), |
| &hash_type, &out_size); |
| if (err || !out_size || hash_type.status) { |
| dev_err(&pdev->dev, "Failed to get hash engine, err: %d, status: 0x%x, out size: 0x%x\n", |
| err, hash_type.status, out_size); |
| return -EINVAL; |
| } |
| |
| *type = hash_type.hash_engine; |
| return 0; |
| } |
| |
| int hinic_rss_cfg(struct hinic_dev *nic_dev, u8 rss_en, u8 template_id) |
| { |
| struct hinic_hwdev *hwdev = nic_dev->hwdev; |
| struct hinic_rss_config rss_cfg = { 0 }; |
| struct hinic_hwif *hwif = hwdev->hwif; |
| struct pci_dev *pdev = hwif->pdev; |
| u16 out_size = sizeof(rss_cfg); |
| int err; |
| |
| rss_cfg.func_id = HINIC_HWIF_FUNC_IDX(hwif); |
| rss_cfg.rss_en = rss_en; |
| rss_cfg.template_id = template_id; |
| rss_cfg.rq_priority_number = 0; |
| |
| err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_RSS_CFG, |
| &rss_cfg, sizeof(rss_cfg), |
| &rss_cfg, &out_size); |
| if (err || !out_size || rss_cfg.status) { |
| dev_err(&pdev->dev, |
| "Failed to set rss cfg, err: %d, status: 0x%x, out size: 0x%x\n", |
| err, rss_cfg.status, out_size); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| int hinic_rss_template_alloc(struct hinic_dev *nic_dev, u8 *tmpl_idx) |
| { |
| struct hinic_rss_template_mgmt template_mgmt = { 0 }; |
| struct hinic_hwdev *hwdev = nic_dev->hwdev; |
| struct hinic_hwif *hwif = hwdev->hwif; |
| u16 out_size = sizeof(template_mgmt); |
| struct pci_dev *pdev = hwif->pdev; |
| int err; |
| |
| template_mgmt.func_id = HINIC_HWIF_FUNC_IDX(hwif); |
| template_mgmt.cmd = NIC_RSS_CMD_TEMP_ALLOC; |
| |
| err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_RSS_TEMP_MGR, |
| &template_mgmt, sizeof(template_mgmt), |
| &template_mgmt, &out_size); |
| if (err || !out_size || template_mgmt.status) { |
| dev_err(&pdev->dev, "Failed to alloc rss template, err: %d, status: 0x%x, out size: 0x%x\n", |
| err, template_mgmt.status, out_size); |
| return -EINVAL; |
| } |
| |
| *tmpl_idx = template_mgmt.template_id; |
| |
| return 0; |
| } |
| |
| int hinic_rss_template_free(struct hinic_dev *nic_dev, u8 tmpl_idx) |
| { |
| struct hinic_rss_template_mgmt template_mgmt = { 0 }; |
| struct hinic_hwdev *hwdev = nic_dev->hwdev; |
| struct hinic_hwif *hwif = hwdev->hwif; |
| u16 out_size = sizeof(template_mgmt); |
| struct pci_dev *pdev = hwif->pdev; |
| int err; |
| |
| template_mgmt.func_id = HINIC_HWIF_FUNC_IDX(hwif); |
| template_mgmt.template_id = tmpl_idx; |
| template_mgmt.cmd = NIC_RSS_CMD_TEMP_FREE; |
| |
| err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_RSS_TEMP_MGR, |
| &template_mgmt, sizeof(template_mgmt), |
| &template_mgmt, &out_size); |
| if (err || !out_size || template_mgmt.status) { |
| dev_err(&pdev->dev, "Failed to free rss template, err: %d, status: 0x%x, out size: 0x%x\n", |
| err, template_mgmt.status, out_size); |
| return -EINVAL; |
| } |
| |
| return 0; |
| } |
| |
| int hinic_get_vport_stats(struct hinic_dev *nic_dev, |
| struct hinic_vport_stats *stats) |
| { |
| struct hinic_cmd_vport_stats vport_stats = { 0 }; |
| struct hinic_port_stats_info stats_info = { 0 }; |
| struct hinic_hwdev *hwdev = nic_dev->hwdev; |
| struct hinic_hwif *hwif = hwdev->hwif; |
| u16 out_size = sizeof(vport_stats); |
| struct pci_dev *pdev = hwif->pdev; |
| int err; |
| |
| stats_info.stats_version = HINIC_PORT_STATS_VERSION; |
| stats_info.func_id = HINIC_HWIF_FUNC_IDX(hwif); |
| stats_info.stats_size = sizeof(vport_stats); |
| |
| err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_VPORT_STAT, |
| &stats_info, sizeof(stats_info), |
| &vport_stats, &out_size); |
| if (err || !out_size || vport_stats.status) { |
| dev_err(&pdev->dev, |
| "Failed to get function statistics, err: %d, status: 0x%x, out size: 0x%x\n", |
| err, vport_stats.status, out_size); |
| return -EFAULT; |
| } |
| |
| memcpy(stats, &vport_stats.stats, sizeof(*stats)); |
| return 0; |
| } |
| |
| int hinic_get_phy_port_stats(struct hinic_dev *nic_dev, |
| struct hinic_phy_port_stats *stats) |
| { |
| struct hinic_port_stats_info stats_info = { 0 }; |
| struct hinic_hwdev *hwdev = nic_dev->hwdev; |
| struct hinic_hwif *hwif = hwdev->hwif; |
| struct hinic_port_stats *port_stats; |
| u16 out_size = sizeof(*port_stats); |
| struct pci_dev *pdev = hwif->pdev; |
| int err; |
| |
| port_stats = kzalloc(sizeof(*port_stats), GFP_KERNEL); |
| if (!port_stats) |
| return -ENOMEM; |
| |
| stats_info.stats_version = HINIC_PORT_STATS_VERSION; |
| stats_info.stats_size = sizeof(*port_stats); |
| |
| err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_PORT_STATISTICS, |
| &stats_info, sizeof(stats_info), |
| port_stats, &out_size); |
| if (err || !out_size || port_stats->status) { |
| dev_err(&pdev->dev, |
| "Failed to get port statistics, err: %d, status: 0x%x, out size: 0x%x\n", |
| err, port_stats->status, out_size); |
| err = -EINVAL; |
| goto out; |
| } |
| |
| memcpy(stats, &port_stats->stats, sizeof(*stats)); |
| |
| out: |
| kfree(port_stats); |
| |
| return err; |
| } |
| |
| int hinic_get_mgmt_version(struct hinic_dev *nic_dev, u8 *mgmt_ver) |
| { |
| struct hinic_hwdev *hwdev = nic_dev->hwdev; |
| struct hinic_version_info up_ver = {0}; |
| u16 out_size = sizeof(up_ver); |
| struct hinic_hwif *hwif; |
| struct pci_dev *pdev; |
| int err; |
| |
| if (!hwdev) |
| return -EINVAL; |
| |
| hwif = hwdev->hwif; |
| pdev = hwif->pdev; |
| |
| err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_MGMT_VERSION, |
| &up_ver, sizeof(up_ver), &up_ver, |
| &out_size); |
| if (err || !out_size || up_ver.status) { |
| dev_err(&pdev->dev, |
| "Failed to get mgmt version, err: %d, status: 0x%x, out size: 0x%x\n", |
| err, up_ver.status, out_size); |
| return -EINVAL; |
| } |
| |
| snprintf(mgmt_ver, HINIC_MGMT_VERSION_MAX_LEN, "%s", up_ver.ver); |
| |
| return 0; |
| } |
| |
| int hinic_get_link_mode(struct hinic_hwdev *hwdev, |
| struct hinic_link_mode_cmd *link_mode) |
| { |
| u16 out_size; |
| int err; |
| |
| if (!hwdev || !link_mode) |
| return -EINVAL; |
| |
| link_mode->func_id = HINIC_HWIF_FUNC_IDX(hwdev->hwif); |
| out_size = sizeof(*link_mode); |
| |
| err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_LINK_MODE, |
| link_mode, sizeof(*link_mode), |
| link_mode, &out_size); |
| if (err || !out_size || link_mode->status) { |
| dev_err(&hwdev->hwif->pdev->dev, |
| "Failed to get link mode, err: %d, status: 0x%x, out size: 0x%x\n", |
| err, link_mode->status, out_size); |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| int hinic_set_autoneg(struct hinic_hwdev *hwdev, bool enable) |
| { |
| struct hinic_set_autoneg_cmd autoneg = {0}; |
| u16 out_size = sizeof(autoneg); |
| int err; |
| |
| if (!hwdev) |
| return -EINVAL; |
| |
| autoneg.func_id = HINIC_HWIF_FUNC_IDX(hwdev->hwif); |
| autoneg.enable = enable; |
| |
| err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_AUTONEG, |
| &autoneg, sizeof(autoneg), |
| &autoneg, &out_size); |
| if (err || !out_size || autoneg.status) { |
| dev_err(&hwdev->hwif->pdev->dev, "Failed to %s autoneg, err: %d, status: 0x%x, out size: 0x%x\n", |
| enable ? "enable" : "disable", err, autoneg.status, |
| out_size); |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| int hinic_set_speed(struct hinic_hwdev *hwdev, enum nic_speed_level speed) |
| { |
| struct hinic_speed_cmd speed_info = {0}; |
| u16 out_size = sizeof(speed_info); |
| int err; |
| |
| if (!hwdev) |
| return -EINVAL; |
| |
| speed_info.func_id = HINIC_HWIF_FUNC_IDX(hwdev->hwif); |
| speed_info.speed = speed; |
| |
| err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_SPEED, |
| &speed_info, sizeof(speed_info), |
| &speed_info, &out_size); |
| if (err || !out_size || speed_info.status) { |
| dev_err(&hwdev->hwif->pdev->dev, |
| "Failed to set speed, err: %d, status: 0x%x, out size: 0x%x\n", |
| err, speed_info.status, out_size); |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| int hinic_set_link_settings(struct hinic_hwdev *hwdev, |
| struct hinic_link_ksettings_info *info) |
| { |
| u16 out_size = sizeof(*info); |
| int err; |
| |
| err = hinic_hilink_msg_cmd(hwdev, HINIC_HILINK_CMD_SET_LINK_SETTINGS, |
| info, sizeof(*info), info, &out_size); |
| if ((info->status != HINIC_MGMT_CMD_UNSUPPORTED && |
| info->status) || err || !out_size) { |
| dev_err(&hwdev->hwif->pdev->dev, |
| "Failed to set link settings, err: %d, status: 0x%x, out size: 0x%x\n", |
| err, info->status, out_size); |
| return -EFAULT; |
| } |
| |
| return info->status; |
| } |
| |
| int hinic_get_hw_pause_info(struct hinic_hwdev *hwdev, |
| struct hinic_pause_config *pause_info) |
| { |
| u16 out_size = sizeof(*pause_info); |
| int err; |
| |
| pause_info->func_id = HINIC_HWIF_FUNC_IDX(hwdev->hwif); |
| |
| err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_PAUSE_INFO, |
| pause_info, sizeof(*pause_info), |
| pause_info, &out_size); |
| if (err || !out_size || pause_info->status) { |
| dev_err(&hwdev->hwif->pdev->dev, "Failed to get pause info, err: %d, status: 0x%x, out size: 0x%x\n", |
| err, pause_info->status, out_size); |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| int hinic_set_hw_pause_info(struct hinic_hwdev *hwdev, |
| struct hinic_pause_config *pause_info) |
| { |
| u16 out_size = sizeof(*pause_info); |
| int err; |
| |
| pause_info->func_id = HINIC_HWIF_FUNC_IDX(hwdev->hwif); |
| |
| err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_PAUSE_INFO, |
| pause_info, sizeof(*pause_info), |
| pause_info, &out_size); |
| if (err || !out_size || pause_info->status) { |
| dev_err(&hwdev->hwif->pdev->dev, "Failed to set pause info, err: %d, status: 0x%x, out size: 0x%x\n", |
| err, pause_info->status, out_size); |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| int hinic_dcb_set_pfc(struct hinic_hwdev *hwdev, u8 pfc_en, u8 pfc_bitmap) |
| { |
| struct hinic_nic_cfg *nic_cfg = &hwdev->func_to_io.nic_cfg; |
| struct hinic_set_pfc pfc = {0}; |
| u16 out_size = sizeof(pfc); |
| int err; |
| |
| if (HINIC_IS_VF(hwdev->hwif)) |
| return 0; |
| |
| mutex_lock(&nic_cfg->cfg_mutex); |
| |
| pfc.func_id = HINIC_HWIF_FUNC_IDX(hwdev->hwif); |
| pfc.pfc_bitmap = pfc_bitmap; |
| pfc.pfc_en = pfc_en; |
| |
| err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_PFC, |
| &pfc, sizeof(pfc), &pfc, &out_size); |
| if (err || pfc.status || !out_size) { |
| dev_err(&hwdev->hwif->pdev->dev, "Failed to %s pfc, err: %d, status: 0x%x, out size: 0x%x\n", |
| pfc_en ? "enable" : "disable", err, pfc.status, |
| out_size); |
| mutex_unlock(&nic_cfg->cfg_mutex); |
| return -EIO; |
| } |
| |
| /* pause settings is opposite from pfc */ |
| nic_cfg->rx_pause = pfc_en ? 0 : 1; |
| nic_cfg->tx_pause = pfc_en ? 0 : 1; |
| |
| mutex_unlock(&nic_cfg->cfg_mutex); |
| |
| return 0; |
| } |
| |
| int hinic_set_loopback_mode(struct hinic_hwdev *hwdev, u32 mode, u32 enable) |
| { |
| struct hinic_port_loopback lb = {0}; |
| u16 out_size = sizeof(lb); |
| int err; |
| |
| lb.mode = mode; |
| lb.en = enable; |
| |
| if (mode < LOOP_MODE_MIN || mode > LOOP_MODE_MAX) { |
| dev_err(&hwdev->hwif->pdev->dev, |
| "Invalid loopback mode %d to set\n", mode); |
| return -EINVAL; |
| } |
| |
| err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_LOOPBACK_MODE, |
| &lb, sizeof(lb), &lb, &out_size); |
| if (err || !out_size || lb.status) { |
| dev_err(&hwdev->hwif->pdev->dev, |
| "Failed to set loopback mode %d en %d, err: %d, status: 0x%x, out size: 0x%x\n", |
| mode, enable, err, lb.status, out_size); |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| static int _set_led_status(struct hinic_hwdev *hwdev, u8 port, |
| enum hinic_led_type type, |
| enum hinic_led_mode mode, u8 reset) |
| { |
| struct hinic_led_info led_info = {0}; |
| u16 out_size = sizeof(led_info); |
| struct hinic_pfhwdev *pfhwdev; |
| int err; |
| |
| pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev); |
| |
| led_info.port = port; |
| led_info.reset = reset; |
| |
| led_info.type = type; |
| led_info.mode = mode; |
| |
| err = hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_COMM, |
| HINIC_COMM_CMD_SET_LED_STATUS, |
| &led_info, sizeof(led_info), |
| &led_info, &out_size, HINIC_MGMT_MSG_SYNC); |
| if (err || led_info.status || !out_size) { |
| dev_err(&hwdev->hwif->pdev->dev, "Failed to set led status, err: %d, status: 0x%x, out size: 0x%x\n", |
| err, led_info.status, out_size); |
| return -EIO; |
| } |
| |
| return 0; |
| } |
| |
| int hinic_set_led_status(struct hinic_hwdev *hwdev, u8 port, |
| enum hinic_led_type type, enum hinic_led_mode mode) |
| { |
| if (!hwdev) |
| return -EINVAL; |
| |
| return _set_led_status(hwdev, port, type, mode, 0); |
| } |
| |
| int hinic_reset_led_status(struct hinic_hwdev *hwdev, u8 port) |
| { |
| int err; |
| |
| if (!hwdev) |
| return -EINVAL; |
| |
| err = _set_led_status(hwdev, port, HINIC_LED_TYPE_INVALID, |
| HINIC_LED_MODE_INVALID, 1); |
| if (err) |
| dev_err(&hwdev->hwif->pdev->dev, |
| "Failed to reset led status\n"); |
| |
| return err; |
| } |
| |
| static bool hinic_if_sfp_absent(struct hinic_hwdev *hwdev) |
| { |
| struct hinic_cmd_get_light_module_abs sfp_abs = {0}; |
| u16 out_size = sizeof(sfp_abs); |
| u8 port_id = hwdev->port_id; |
| int err; |
| |
| sfp_abs.port_id = port_id; |
| err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_SFP_ABS, |
| &sfp_abs, sizeof(sfp_abs), &sfp_abs, |
| &out_size); |
| if (sfp_abs.status || err || !out_size) { |
| dev_err(&hwdev->hwif->pdev->dev, |
| "Failed to get port%d sfp absent status, err: %d, status: 0x%x, out size: 0x%x\n", |
| port_id, err, sfp_abs.status, out_size); |
| return true; |
| } |
| |
| return ((sfp_abs.abs_status == 0) ? false : true); |
| } |
| |
| int hinic_get_sfp_eeprom(struct hinic_hwdev *hwdev, u8 *data, u16 *len) |
| { |
| struct hinic_cmd_get_std_sfp_info sfp_info = {0}; |
| u16 out_size = sizeof(sfp_info); |
| u8 port_id; |
| int err; |
| |
| if (!hwdev || !data || !len) |
| return -EINVAL; |
| |
| port_id = hwdev->port_id; |
| |
| if (hinic_if_sfp_absent(hwdev)) |
| return -ENXIO; |
| |
| sfp_info.port_id = port_id; |
| err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_STD_SFP_INFO, |
| &sfp_info, sizeof(sfp_info), &sfp_info, |
| &out_size); |
| if (sfp_info.status || err || !out_size) { |
| dev_err(&hwdev->hwif->pdev->dev, |
| "Failed to get port%d sfp eeprom information, err: %d, status: 0x%x, out size: 0x%x\n", |
| port_id, err, sfp_info.status, out_size); |
| return -EIO; |
| } |
| |
| *len = min_t(u16, sfp_info.eeprom_len, STD_SFP_INFO_MAX_SIZE); |
| memcpy(data, sfp_info.sfp_info, STD_SFP_INFO_MAX_SIZE); |
| |
| return 0; |
| } |
| |
| int hinic_get_sfp_type(struct hinic_hwdev *hwdev, u8 *data0, u8 *data1) |
| { |
| u8 sfp_data[STD_SFP_INFO_MAX_SIZE]; |
| u16 len; |
| int err; |
| |
| if (hinic_if_sfp_absent(hwdev)) |
| return -ENXIO; |
| |
| err = hinic_get_sfp_eeprom(hwdev, sfp_data, &len); |
| if (err) |
| return err; |
| |
| *data0 = sfp_data[0]; |
| *data1 = sfp_data[1]; |
| |
| return 0; |
| } |