380 lines
9.7 KiB
C
380 lines
9.7 KiB
C
/*
|
|
* Huawei HiNIC PCI Express Linux driver
|
|
* Copyright(c) 2017 Huawei Technologies Co., Ltd
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms and conditions of the GNU General Public License,
|
|
* version 2, as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* for more details.
|
|
*
|
|
*/
|
|
|
|
#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"
|
|
|
|
#define HINIC_MIN_MTU_SIZE 256
|
|
#define HINIC_MAX_JUMBO_FRAME_SIZE 15872
|
|
|
|
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 net_device *netdev = nic_dev->netdev;
|
|
struct hinic_hwdev *hwdev = nic_dev->hwdev;
|
|
struct hinic_port_mac_cmd port_mac_cmd;
|
|
struct hinic_hwif *hwif = hwdev->hwif;
|
|
struct pci_dev *pdev = hwif->pdev;
|
|
enum hinic_port_cmd cmd;
|
|
u16 out_size;
|
|
int err;
|
|
|
|
if (vlan_id >= VLAN_N_VID) {
|
|
netif_err(nic_dev, drv, netdev, "Invalid VLAN number\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
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) {
|
|
dev_err(&pdev->dev, "Failed to change MAC, ret = %d\n",
|
|
port_mac_cmd.status);
|
|
return -EFAULT;
|
|
}
|
|
|
|
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;
|
|
struct pci_dev *pdev = hwif->pdev;
|
|
u16 out_size;
|
|
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, ret = %d\n",
|
|
port_mac_cmd.status);
|
|
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 net_device *netdev = nic_dev->netdev;
|
|
struct hinic_hwdev *hwdev = nic_dev->hwdev;
|
|
struct hinic_port_mtu_cmd port_mtu_cmd;
|
|
struct hinic_hwif *hwif = hwdev->hwif;
|
|
struct pci_dev *pdev = hwif->pdev;
|
|
int err, max_frame;
|
|
u16 out_size;
|
|
|
|
if (new_mtu < HINIC_MIN_MTU_SIZE) {
|
|
netif_err(nic_dev, drv, netdev, "mtu < MIN MTU size");
|
|
return -EINVAL;
|
|
}
|
|
|
|
max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN;
|
|
if (max_frame > HINIC_MAX_JUMBO_FRAME_SIZE) {
|
|
netif_err(nic_dev, drv, netdev, "mtu > MAX MTU size");
|
|
return -EINVAL;
|
|
}
|
|
|
|
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, ret = %d\n",
|
|
port_mtu_cmd.status);
|
|
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;
|
|
int err;
|
|
|
|
if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) {
|
|
dev_err(&pdev->dev, "unsupported PCI Function type\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
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, ret = %d\n",
|
|
link_cmd.status);
|
|
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;
|
|
int err;
|
|
|
|
if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) {
|
|
dev_err(&pdev->dev, "unsupported PCI Function type\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
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, ret = %d\n",
|
|
port_state.status);
|
|
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;
|
|
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, ret = %d\n",
|
|
func_state.status);
|
|
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;
|
|
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, ret = %d\n",
|
|
port_cap->status);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|