Commit c4d06d2d authored by Aviad Krawczyk's avatar Aviad Krawczyk Committed by David S. Miller

net-next/hinic: Add Rx mode and link event handler

Add port management message for setting Rx mode in the card,
used for rx_mode netdev operation.
The link event handler is used for getting a notification about the
link state.
Signed-off-by: default avatarAviad Krawczyk <aviad.krawczyk@huawei.com>
Signed-off-by: default avatarZhao Chen <zhaochen6@huawei.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 25a3ba61
......@@ -19,19 +19,36 @@
#include <linux/netdevice.h>
#include <linux/types.h>
#include <linux/semaphore.h>
#include <linux/workqueue.h>
#include <linux/bitops.h>
#include "hinic_hw_dev.h"
#define HINIC_DRV_NAME "hinic"
enum hinic_flags {
HINIC_LINK_UP = BIT(0),
HINIC_INTF_UP = BIT(1),
};
struct hinic_rx_mode_work {
struct work_struct work;
u32 rx_mode;
};
struct hinic_dev {
struct net_device *netdev;
struct hinic_hwdev *hwdev;
u32 msg_enable;
unsigned int flags;
struct semaphore mgmt_lock;
unsigned long *vlan_bitmap;
struct hinic_rx_mode_work rx_mode_work;
struct workqueue_struct *workq;
};
#endif
......@@ -20,6 +20,8 @@
#define HINIC_CSR_FUNC_ATTR0_ADDR 0x0
#define HINIC_CSR_FUNC_ATTR1_ADDR 0x4
#define HINIC_CSR_FUNC_ATTR5_ADDR 0x14
#define HINIC_DMA_ATTR_BASE 0xC80
#define HINIC_ELECTION_BASE 0x4200
......
......@@ -229,6 +229,118 @@ int hinic_port_msg_cmd(struct hinic_hwdev *hwdev, enum hinic_port_cmd cmd,
HINIC_MGMT_MSG_SYNC);
}
/**
* hinic_hwdev_cb_register - register callback handler for MGMT events
* @hwdev: the NIC HW device
* @cmd: the mgmt event
* @handle: private data for the handler
* @handler: event handler
**/
void hinic_hwdev_cb_register(struct hinic_hwdev *hwdev,
enum hinic_mgmt_msg_cmd cmd, void *handle,
void (*handler)(void *handle, void *buf_in,
u16 in_size, void *buf_out,
u16 *out_size))
{
struct hinic_hwif *hwif = hwdev->hwif;
struct pci_dev *pdev = hwif->pdev;
struct hinic_pfhwdev *pfhwdev;
struct hinic_nic_cb *nic_cb;
u8 cmd_cb;
if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) {
dev_err(&pdev->dev, "unsupported PCI Function type\n");
return;
}
pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev);
cmd_cb = cmd - HINIC_MGMT_MSG_CMD_BASE;
nic_cb = &pfhwdev->nic_cb[cmd_cb];
nic_cb->handler = handler;
nic_cb->handle = handle;
nic_cb->cb_state = HINIC_CB_ENABLED;
}
/**
* hinic_hwdev_cb_unregister - unregister callback handler for MGMT events
* @hwdev: the NIC HW device
* @cmd: the mgmt event
**/
void hinic_hwdev_cb_unregister(struct hinic_hwdev *hwdev,
enum hinic_mgmt_msg_cmd cmd)
{
struct hinic_hwif *hwif = hwdev->hwif;
struct pci_dev *pdev = hwif->pdev;
struct hinic_pfhwdev *pfhwdev;
struct hinic_nic_cb *nic_cb;
u8 cmd_cb;
if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) {
dev_err(&pdev->dev, "unsupported PCI Function type\n");
return;
}
pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev);
cmd_cb = cmd - HINIC_MGMT_MSG_CMD_BASE;
nic_cb = &pfhwdev->nic_cb[cmd_cb];
nic_cb->cb_state &= ~HINIC_CB_ENABLED;
while (nic_cb->cb_state & HINIC_CB_RUNNING)
schedule();
nic_cb->handler = NULL;
}
/**
* nic_mgmt_msg_handler - nic mgmt event handler
* @handle: private data for the handler
* @buf_in: input buffer
* @in_size: input size
* @buf_out: output buffer
* @out_size: returned output size
**/
static void nic_mgmt_msg_handler(void *handle, u8 cmd, void *buf_in,
u16 in_size, void *buf_out, u16 *out_size)
{
struct hinic_pfhwdev *pfhwdev = handle;
enum hinic_cb_state cb_state;
struct hinic_nic_cb *nic_cb;
struct hinic_hwdev *hwdev;
struct hinic_hwif *hwif;
struct pci_dev *pdev;
u8 cmd_cb;
hwdev = &pfhwdev->hwdev;
hwif = hwdev->hwif;
pdev = hwif->pdev;
if ((cmd < HINIC_MGMT_MSG_CMD_BASE) ||
(cmd >= HINIC_MGMT_MSG_CMD_MAX)) {
dev_err(&pdev->dev, "unknown L2NIC event, cmd = %d\n", cmd);
return;
}
cmd_cb = cmd - HINIC_MGMT_MSG_CMD_BASE;
nic_cb = &pfhwdev->nic_cb[cmd_cb];
cb_state = cmpxchg(&nic_cb->cb_state,
HINIC_CB_ENABLED,
HINIC_CB_ENABLED | HINIC_CB_RUNNING);
if ((cb_state == HINIC_CB_ENABLED) && (nic_cb->handler))
nic_cb->handler(nic_cb->handle, buf_in,
in_size, buf_out, out_size);
else
dev_err(&pdev->dev, "Unhandled NIC Event %d\n", cmd);
nic_cb->cb_state &= ~HINIC_CB_RUNNING;
}
/**
* init_pfhwdev - Initialize the extended components of PF
* @pfhwdev: the HW device for PF
......@@ -248,6 +360,10 @@ static int init_pfhwdev(struct hinic_pfhwdev *pfhwdev)
return err;
}
hinic_register_mgmt_msg_cb(&pfhwdev->pf_to_mgmt, HINIC_MOD_L2NIC,
pfhwdev, nic_mgmt_msg_handler);
hinic_set_pf_action(hwif, HINIC_PF_MGMT_ACTIVE);
return 0;
}
......@@ -257,6 +373,12 @@ static int init_pfhwdev(struct hinic_pfhwdev *pfhwdev)
**/
static void free_pfhwdev(struct hinic_pfhwdev *pfhwdev)
{
struct hinic_hwdev *hwdev = &pfhwdev->hwdev;
hinic_set_pf_action(hwdev->hwif, HINIC_PF_MGMT_INIT);
hinic_unregister_mgmt_msg_cb(&pfhwdev->pf_to_mgmt, HINIC_MOD_L2NIC);
hinic_pf_to_mgmt_free(&pfhwdev->pf_to_mgmt);
}
......
......@@ -18,6 +18,7 @@
#include <linux/pci.h>
#include <linux/types.h>
#include <linux/bitops.h>
#include "hinic_hw_if.h"
#include "hinic_hw_eqs.h"
......@@ -25,6 +26,9 @@
#define HINIC_MAX_QPS 32
#define HINIC_MGMT_NUM_MSG_CMD (HINIC_MGMT_MSG_CMD_MAX - \
HINIC_MGMT_MSG_CMD_BASE)
struct hinic_cap {
u16 max_qps;
u16 num_qps;
......@@ -55,6 +59,19 @@ enum hinic_port_cmd {
HINIC_PORT_CMD_GET_CAP = 170,
};
enum hinic_mgmt_msg_cmd {
HINIC_MGMT_MSG_CMD_BASE = 160,
HINIC_MGMT_MSG_CMD_LINK_STATUS = 160,
HINIC_MGMT_MSG_CMD_MAX,
};
enum hinic_cb_state {
HINIC_CB_ENABLED = BIT(0),
HINIC_CB_RUNNING = BIT(1),
};
struct hinic_hwdev {
struct hinic_hwif *hwif;
struct msix_entry *msix_entries;
......@@ -64,12 +81,32 @@ struct hinic_hwdev {
struct hinic_cap nic_cap;
};
struct hinic_nic_cb {
void (*handler)(void *handle, void *buf_in,
u16 in_size, void *buf_out,
u16 *out_size);
void *handle;
unsigned long cb_state;
};
struct hinic_pfhwdev {
struct hinic_hwdev hwdev;
struct hinic_pf_to_mgmt pf_to_mgmt;
struct hinic_nic_cb nic_cb[HINIC_MGMT_NUM_MSG_CMD];
};
void hinic_hwdev_cb_register(struct hinic_hwdev *hwdev,
enum hinic_mgmt_msg_cmd cmd, void *handle,
void (*handler)(void *handle, void *buf_in,
u16 in_size, void *buf_out,
u16 *out_size));
void hinic_hwdev_cb_unregister(struct hinic_hwdev *hwdev,
enum hinic_mgmt_msg_cmd cmd);
int hinic_port_msg_cmd(struct hinic_hwdev *hwdev, enum hinic_port_cmd cmd,
void *buf_in, u16 in_size, void *buf_out,
u16 *out_size);
......
......@@ -115,6 +115,23 @@ int hinic_msix_attr_cnt_clear(struct hinic_hwif *hwif, u16 msix_index)
return 0;
}
/**
* hinic_set_pf_action - set action on pf channel
* @hwif: the HW interface of a pci function device
* @action: action on pf channel
*
* Return 0 - Success, negative - Failure
**/
void hinic_set_pf_action(struct hinic_hwif *hwif, enum hinic_pf_action action)
{
u32 attr5 = hinic_hwif_read_reg(hwif, HINIC_CSR_FUNC_ATTR5_ADDR);
attr5 = HINIC_FA5_CLEAR(attr5, PF_ACTION);
attr5 |= HINIC_FA5_SET(action, PF_ACTION);
hinic_hwif_write_reg(hwif, HINIC_CSR_FUNC_ATTR5_ADDR, attr5);
}
/**
* hwif_ready - test if the HW is ready for use
* @hwif: the HW interface of a pci function device
......
......@@ -73,6 +73,15 @@
#define HINIC_FA1_GET(val, member) \
(((val) >> HINIC_FA1_##member##_SHIFT) & HINIC_FA1_##member##_MASK)
#define HINIC_FA5_PF_ACTION_SHIFT 0
#define HINIC_FA5_PF_ACTION_MASK 0xFFFF
#define HINIC_FA5_SET(val, member) \
(((u32)(val) & HINIC_FA5_##member##_MASK) << HINIC_FA5_##member##_SHIFT)
#define HINIC_FA5_CLEAR(val, member) \
((val) & (~(HINIC_FA5_##member##_MASK << HINIC_FA5_##member##_SHIFT)))
#define HINIC_PPF_ELECTION_IDX_SHIFT 0
#define HINIC_PPF_ELECTION_IDX_MASK 0x1F
......@@ -166,6 +175,12 @@ enum hinic_node_id {
HINIC_NODE_ID_MGMT = 21,
};
enum hinic_pf_action {
HINIC_PF_MGMT_INIT = 0x0,
HINIC_PF_MGMT_ACTIVE = 0x11,
};
struct hinic_func_attr {
u16 func_idx;
u8 pf_idx;
......@@ -212,6 +227,8 @@ int hinic_msix_attr_get(struct hinic_hwif *hwif, u16 msix_index,
int hinic_msix_attr_cnt_clear(struct hinic_hwif *hwif, u16 msix_index);
void hinic_set_pf_action(struct hinic_hwif *hwif, enum hinic_pf_action action);
int hinic_init_hwif(struct hinic_hwif *hwif, struct pci_dev *pdev);
void hinic_free_hwif(struct hinic_hwif *hwif);
......
......@@ -72,6 +72,46 @@ enum msg_ack_type {
MSG_NO_ACK = 1,
};
/**
* hinic_register_mgmt_msg_cb - register msg handler for a msg from a module
* @pf_to_mgmt: PF to MGMT channel
* @mod: module in the chip that this handler will handle its messages
* @handle: private data for the callback
* @callback: the handler that will handle messages
**/
void hinic_register_mgmt_msg_cb(struct hinic_pf_to_mgmt *pf_to_mgmt,
enum hinic_mod_type mod,
void *handle,
void (*callback)(void *handle,
u8 cmd, void *buf_in,
u16 in_size, void *buf_out,
u16 *out_size))
{
struct hinic_mgmt_cb *mgmt_cb = &pf_to_mgmt->mgmt_cb[mod];
mgmt_cb->cb = callback;
mgmt_cb->handle = handle;
mgmt_cb->state = HINIC_MGMT_CB_ENABLED;
}
/**
* hinic_unregister_mgmt_msg_cb - unregister msg handler for a msg from a module
* @pf_to_mgmt: PF to MGMT channel
* @mod: module in the chip that this handler handles its messages
**/
void hinic_unregister_mgmt_msg_cb(struct hinic_pf_to_mgmt *pf_to_mgmt,
enum hinic_mod_type mod)
{
struct hinic_mgmt_cb *mgmt_cb = &pf_to_mgmt->mgmt_cb[mod];
mgmt_cb->state &= ~HINIC_MGMT_CB_ENABLED;
while (mgmt_cb->state & HINIC_MGMT_CB_RUNNING)
schedule();
mgmt_cb->cb = NULL;
}
/**
* prepare_header - prepare the header of the message
* @pf_to_mgmt: PF to MGMT channel
......@@ -337,9 +377,31 @@ static void mgmt_recv_msg_handler(struct hinic_pf_to_mgmt *pf_to_mgmt,
struct hinic_hwif *hwif = pf_to_mgmt->hwif;
struct pci_dev *pdev = hwif->pdev;
u8 *buf_out = recv_msg->buf_out;
struct hinic_mgmt_cb *mgmt_cb;
unsigned long cb_state;
u16 out_size = 0;
dev_err(&pdev->dev, "No MGMT msg handler, mod = %d\n", recv_msg->mod);
if (recv_msg->mod >= HINIC_MOD_MAX) {
dev_err(&pdev->dev, "Unknown MGMT MSG module = %d\n",
recv_msg->mod);
return;
}
mgmt_cb = &pf_to_mgmt->mgmt_cb[recv_msg->mod];
cb_state = cmpxchg(&mgmt_cb->state,
HINIC_MGMT_CB_ENABLED,
HINIC_MGMT_CB_ENABLED | HINIC_MGMT_CB_RUNNING);
if ((cb_state == HINIC_MGMT_CB_ENABLED) && (mgmt_cb->cb))
mgmt_cb->cb(mgmt_cb->handle, recv_msg->cmd,
recv_msg->msg, recv_msg->msg_len,
buf_out, &out_size);
else
dev_err(&pdev->dev, "No MGMT msg handler, mod = %d\n",
recv_msg->mod);
mgmt_cb->state &= ~HINIC_MGMT_CB_RUNNING;
if (!recv_msg->async_mgmt_to_pf)
/* MGMT sent sync msg, send the response */
......
......@@ -19,6 +19,7 @@
#include <linux/types.h>
#include <linux/semaphore.h>
#include <linux/completion.h>
#include <linux/bitops.h>
#include "hinic_hw_if.h"
#include "hinic_hw_api_cmd.h"
......@@ -67,6 +68,11 @@ enum hinic_cfg_cmd {
HINIC_CFG_NIC_CAP = 0,
};
enum hinic_mgmt_cb_state {
HINIC_MGMT_CB_ENABLED = BIT(0),
HINIC_MGMT_CB_RUNNING = BIT(1),
};
struct hinic_recv_msg {
u8 *msg;
u8 *buf_out;
......@@ -81,6 +87,15 @@ struct hinic_recv_msg {
u16 msg_id;
};
struct hinic_mgmt_cb {
void (*cb)(void *handle, u8 cmd,
void *buf_in, u16 in_size,
void *buf_out, u16 *out_size);
void *handle;
unsigned long state;
};
struct hinic_pf_to_mgmt {
struct hinic_hwif *hwif;
......@@ -92,8 +107,21 @@ struct hinic_pf_to_mgmt {
struct hinic_recv_msg recv_msg_from_mgmt;
struct hinic_api_cmd_chain *cmd_chain[HINIC_API_CMD_MAX];
struct hinic_mgmt_cb mgmt_cb[HINIC_MOD_MAX];
};
void hinic_register_mgmt_msg_cb(struct hinic_pf_to_mgmt *pf_to_mgmt,
enum hinic_mod_type mod,
void *handle,
void (*callback)(void *handle,
u8 cmd, void *buf_in,
u16 in_size, void *buf_out,
u16 *out_size));
void hinic_unregister_mgmt_msg_cb(struct hinic_pf_to_mgmt *pf_to_mgmt,
enum hinic_mod_type mod);
int hinic_msg_to_mgmt(struct hinic_pf_to_mgmt *pf_to_mgmt,
enum hinic_mod_type mod, u8 cmd,
void *buf_in, u16 in_size, void *buf_out, u16 *out_size,
......
......@@ -24,9 +24,11 @@
#include <linux/slab.h>
#include <linux/if_vlan.h>
#include <linux/semaphore.h>
#include <linux/workqueue.h>
#include <net/ip.h>
#include <linux/bitops.h>
#include <linux/bitmap.h>
#include <linux/delay.h>
#include <linux/err.h>
#include "hinic_hw_dev.h"
......@@ -39,12 +41,99 @@ MODULE_LICENSE("GPL");
#define PCI_DEVICE_ID_HI1822_PF 0x1822
#define HINIC_WQ_NAME "hinic_dev"
#define MSG_ENABLE_DEFAULT (NETIF_MSG_DRV | NETIF_MSG_PROBE | \
NETIF_MSG_IFUP | \
NETIF_MSG_TX_ERR | NETIF_MSG_RX_ERR)
#define VLAN_BITMAP_SIZE(nic_dev) (ALIGN(VLAN_N_VID, 8) / 8)
#define work_to_rx_mode_work(work) \
container_of(work, struct hinic_rx_mode_work, work)
#define rx_mode_work_to_nic_dev(rx_mode_work) \
container_of(rx_mode_work, struct hinic_dev, rx_mode_work)
static int change_mac_addr(struct net_device *netdev, const u8 *addr);
static int hinic_open(struct net_device *netdev)
{
struct hinic_dev *nic_dev = netdev_priv(netdev);
enum hinic_port_link_state link_state;
int err, ret;
err = hinic_port_set_state(nic_dev, HINIC_PORT_ENABLE);
if (err) {
netif_err(nic_dev, drv, netdev,
"Failed to set port state\n");
return err;
}
/* Wait up to 3 sec between port enable to link state */
msleep(3000);
down(&nic_dev->mgmt_lock);
err = hinic_port_link_state(nic_dev, &link_state);
if (err) {
netif_err(nic_dev, drv, netdev, "Failed to get link state\n");
goto err_port_link;
}
if (link_state == HINIC_LINK_STATE_UP)
nic_dev->flags |= HINIC_LINK_UP;
nic_dev->flags |= HINIC_INTF_UP;
if ((nic_dev->flags & (HINIC_LINK_UP | HINIC_INTF_UP)) ==
(HINIC_LINK_UP | HINIC_INTF_UP)) {
netif_info(nic_dev, drv, netdev, "link + intf UP\n");
netif_carrier_on(netdev);
netif_tx_wake_all_queues(netdev);
}
up(&nic_dev->mgmt_lock);
netif_info(nic_dev, drv, netdev, "HINIC_INTF is UP\n");
return 0;
err_port_link:
up(&nic_dev->mgmt_lock);
ret = hinic_port_set_state(nic_dev, HINIC_PORT_DISABLE);
if (ret)
netif_warn(nic_dev, drv, netdev,
"Failed to revert port state\n");
return err;
}
static int hinic_close(struct net_device *netdev)
{
struct hinic_dev *nic_dev = netdev_priv(netdev);
unsigned int flags;
int err;
down(&nic_dev->mgmt_lock);
flags = nic_dev->flags;
nic_dev->flags &= ~HINIC_INTF_UP;
netif_carrier_off(netdev);
netif_tx_disable(netdev);
up(&nic_dev->mgmt_lock);
err = hinic_port_set_state(nic_dev, HINIC_PORT_DISABLE);
if (err) {
netif_err(nic_dev, drv, netdev, "Failed to set port state\n");
nic_dev->flags |= (flags & HINIC_INTF_UP);
return err;
}
netif_info(nic_dev, drv, netdev, "HINIC_INTF is DOWN\n");
return 0;
}
static int hinic_change_mtu(struct net_device *netdev, int new_mtu)
{
struct hinic_dev *nic_dev = netdev_priv(netdev);
......@@ -118,6 +207,77 @@ static int hinic_set_mac_addr(struct net_device *netdev, void *addr)
return err;
}
/**
* add_mac_addr - add mac address to network device
* @netdev: network device
* @addr: mac address to add
*
* Return 0 - Success, negative - Failure
**/
static int add_mac_addr(struct net_device *netdev, const u8 *addr)
{
struct hinic_dev *nic_dev = netdev_priv(netdev);
u16 vid = 0;
int err;
if (!is_valid_ether_addr(addr))
return -EADDRNOTAVAIL;
netif_info(nic_dev, drv, netdev, "set mac addr = %02x %02x %02x %02x %02x %02x\n",
addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
down(&nic_dev->mgmt_lock);
do {
err = hinic_port_add_mac(nic_dev, addr, vid);
if (err) {
netif_err(nic_dev, drv, netdev, "Failed to add mac\n");
break;
}
vid = find_next_bit(nic_dev->vlan_bitmap, VLAN_N_VID, vid + 1);
} while (vid != VLAN_N_VID);
up(&nic_dev->mgmt_lock);
return err;
}
/**
* remove_mac_addr - remove mac address from network device
* @netdev: network device
* @addr: mac address to remove
*
* Return 0 - Success, negative - Failure
**/
static int remove_mac_addr(struct net_device *netdev, const u8 *addr)
{
struct hinic_dev *nic_dev = netdev_priv(netdev);
u16 vid = 0;
int err;
if (!is_valid_ether_addr(addr))
return -EADDRNOTAVAIL;
netif_info(nic_dev, drv, netdev, "remove mac addr = %02x %02x %02x %02x %02x %02x\n",
addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
down(&nic_dev->mgmt_lock);
do {
err = hinic_port_del_mac(nic_dev, addr, vid);
if (err) {
netif_err(nic_dev, drv, netdev,
"Failed to delete mac\n");
break;
}
vid = find_next_bit(nic_dev->vlan_bitmap, VLAN_N_VID, vid + 1);
} while (vid != VLAN_N_VID);
up(&nic_dev->mgmt_lock);
return err;
}
static int hinic_vlan_rx_add_vid(struct net_device *netdev,
__always_unused __be16 proto, u16 vid)
{
......@@ -182,12 +342,56 @@ static int hinic_vlan_rx_kill_vid(struct net_device *netdev,
return err;
}
static void set_rx_mode(struct work_struct *work)
{
struct hinic_rx_mode_work *rx_mode_work = work_to_rx_mode_work(work);
struct hinic_dev *nic_dev = rx_mode_work_to_nic_dev(rx_mode_work);
netif_info(nic_dev, drv, nic_dev->netdev, "set rx mode work\n");
hinic_port_set_rx_mode(nic_dev, rx_mode_work->rx_mode);
__dev_uc_sync(nic_dev->netdev, add_mac_addr, remove_mac_addr);
__dev_mc_sync(nic_dev->netdev, add_mac_addr, remove_mac_addr);
}
static void hinic_set_rx_mode(struct net_device *netdev)
{
struct hinic_dev *nic_dev = netdev_priv(netdev);
struct hinic_rx_mode_work *rx_mode_work;
u32 rx_mode;
rx_mode_work = &nic_dev->rx_mode_work;
rx_mode = HINIC_RX_MODE_UC |
HINIC_RX_MODE_MC |
HINIC_RX_MODE_BC;
if (netdev->flags & IFF_PROMISC)
rx_mode |= HINIC_RX_MODE_PROMISC;
else if (netdev->flags & IFF_ALLMULTI)
rx_mode |= HINIC_RX_MODE_MC_ALL;
rx_mode_work->rx_mode = rx_mode;
queue_work(nic_dev->workq, &rx_mode_work->work);
}
netdev_tx_t hinic_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
{
return NETDEV_TX_BUSY;
}
static const struct net_device_ops hinic_netdev_ops = {
.ndo_open = hinic_open,
.ndo_stop = hinic_close,
.ndo_change_mtu = hinic_change_mtu,
.ndo_set_mac_address = hinic_set_mac_addr,
.ndo_validate_addr = eth_validate_addr,
.ndo_vlan_rx_add_vid = hinic_vlan_rx_add_vid,
.ndo_vlan_rx_kill_vid = hinic_vlan_rx_kill_vid,
.ndo_set_rx_mode = hinic_set_rx_mode,
.ndo_start_xmit = hinic_xmit_frame,
/* more operations should be filled */
};
......@@ -200,6 +404,57 @@ static void netdev_features_init(struct net_device *netdev)
netdev->features = netdev->hw_features | NETIF_F_HW_VLAN_CTAG_FILTER;
}
/**
* link_status_event_handler - link event handler
* @handle: nic device for the handler
* @buf_in: input buffer
* @in_size: input size
* @buf_in: output buffer
* @out_size: returned output size
*
* Return 0 - Success, negative - Failure
**/
static void link_status_event_handler(void *handle, void *buf_in, u16 in_size,
void *buf_out, u16 *out_size)
{
struct hinic_port_link_status *link_status, *ret_link_status;
struct hinic_dev *nic_dev = handle;
link_status = buf_in;
if (link_status->link == HINIC_LINK_STATE_UP) {
down(&nic_dev->mgmt_lock);
nic_dev->flags |= HINIC_LINK_UP;
if ((nic_dev->flags & (HINIC_LINK_UP | HINIC_INTF_UP)) ==
(HINIC_LINK_UP | HINIC_INTF_UP)) {
netif_carrier_on(nic_dev->netdev);
netif_tx_wake_all_queues(nic_dev->netdev);
}
up(&nic_dev->mgmt_lock);
netif_info(nic_dev, drv, nic_dev->netdev, "HINIC_Link is UP\n");
} else {
down(&nic_dev->mgmt_lock);
nic_dev->flags &= ~HINIC_LINK_UP;
netif_carrier_off(nic_dev->netdev);
netif_tx_disable(nic_dev->netdev);
up(&nic_dev->mgmt_lock);
netif_info(nic_dev, drv, nic_dev->netdev, "HINIC_Link is DOWN\n");
}
ret_link_status = buf_out;
ret_link_status->status = 0;
*out_size = sizeof(*ret_link_status);
}
/**
* nic_dev_init - Initialize the NIC device
* @pdev: the NIC pci device
......@@ -208,6 +463,7 @@ static void netdev_features_init(struct net_device *netdev)
**/
static int nic_dev_init(struct pci_dev *pdev)
{
struct hinic_rx_mode_work *rx_mode_work;
struct hinic_dev *nic_dev;
struct net_device *netdev;
struct hinic_hwdev *hwdev;
......@@ -239,6 +495,7 @@ static int nic_dev_init(struct pci_dev *pdev)
nic_dev->netdev = netdev;
nic_dev->hwdev = hwdev;
nic_dev->msg_enable = MSG_ENABLE_DEFAULT;
nic_dev->flags = 0;
sema_init(&nic_dev->mgmt_lock, 1);
......@@ -250,6 +507,12 @@ static int nic_dev_init(struct pci_dev *pdev)
goto err_vlan_bitmap;
}
nic_dev->workq = create_singlethread_workqueue(HINIC_WQ_NAME);
if (!nic_dev->workq) {
err = -ENOMEM;
goto err_workq;
}
pci_set_drvdata(pdev, netdev);
err = hinic_port_get_mac(nic_dev, netdev->dev_addr);
......@@ -268,10 +531,16 @@ static int nic_dev_init(struct pci_dev *pdev)
goto err_set_mtu;
}
rx_mode_work = &nic_dev->rx_mode_work;
INIT_WORK(&rx_mode_work->work, set_rx_mode);
netdev_features_init(netdev);
netif_carrier_off(netdev);
hinic_hwdev_cb_register(nic_dev->hwdev, HINIC_MGMT_MSG_CMD_LINK_STATUS,
nic_dev, link_status_event_handler);
err = register_netdev(netdev);
if (err) {
dev_err(&pdev->dev, "Failed to register netdev\n");
......@@ -281,10 +550,16 @@ static int nic_dev_init(struct pci_dev *pdev)
return 0;
err_reg_netdev:
hinic_hwdev_cb_unregister(nic_dev->hwdev,
HINIC_MGMT_MSG_CMD_LINK_STATUS);
cancel_work_sync(&rx_mode_work->work);
err_set_mtu:
err_add_mac:
pci_set_drvdata(pdev, NULL);
destroy_workqueue(nic_dev->workq);
err_workq:
err_vlan_bitmap:
free_netdev(netdev);
......@@ -357,11 +632,20 @@ static void hinic_remove(struct pci_dev *pdev)
{
struct net_device *netdev = pci_get_drvdata(pdev);
struct hinic_dev *nic_dev = netdev_priv(netdev);
struct hinic_rx_mode_work *rx_mode_work;
unregister_netdev(netdev);
hinic_hwdev_cb_unregister(nic_dev->hwdev,
HINIC_MGMT_MSG_CMD_LINK_STATUS);
rx_mode_work = &nic_dev->rx_mode_work;
cancel_work_sync(&rx_mode_work->work);
pci_set_drvdata(pdev, NULL);
destroy_workqueue(nic_dev->workq);
hinic_free_hwdev(nic_dev->hwdev);
free_netdev(netdev);
......
......@@ -222,3 +222,95 @@ int hinic_port_del_vlan(struct hinic_dev *nic_dev, u16 vlan_id)
&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;
}
......@@ -18,9 +18,28 @@
#include <linux/types.h>
#include <linux/etherdevice.h>
#include <linux/bitops.h>
#include "hinic_dev.h"
enum hinic_rx_mode {
HINIC_RX_MODE_UC = BIT(0),
HINIC_RX_MODE_MC = BIT(1),
HINIC_RX_MODE_BC = BIT(2),
HINIC_RX_MODE_MC_ALL = BIT(3),
HINIC_RX_MODE_PROMISC = BIT(4),
};
enum hinic_port_link_state {
HINIC_LINK_STATE_DOWN,
HINIC_LINK_STATE_UP,
};
enum hinic_port_state {
HINIC_PORT_DISABLE = 0,
HINIC_PORT_ENABLE = 3,
};
struct hinic_port_mac_cmd {
u8 status;
u8 version;
......@@ -51,6 +70,45 @@ struct hinic_port_vlan_cmd {
u16 vlan_id;
};
struct hinic_port_rx_mode_cmd {
u8 status;
u8 version;
u8 rsvd0[6];
u16 func_idx;
u16 rsvd;
u32 rx_mode;
};
struct hinic_port_link_cmd {
u8 status;
u8 version;
u8 rsvd0[6];
u16 func_idx;
u8 state;
u8 rsvd1;
};
struct hinic_port_state_cmd {
u8 status;
u8 version;
u8 rsvd0[6];
u8 state;
u8 rsvd1[3];
};
struct hinic_port_link_status {
u8 status;
u8 version;
u8 rsvd0[6];
u16 rsvd1;
u8 link;
u8 rsvd2;
};
int hinic_port_add_mac(struct hinic_dev *nic_dev, const u8 *addr,
u16 vlan_id);
......@@ -65,4 +123,12 @@ int hinic_port_add_vlan(struct hinic_dev *nic_dev, u16 vlan_id);
int hinic_port_del_vlan(struct hinic_dev *nic_dev, u16 vlan_id);
int hinic_port_set_rx_mode(struct hinic_dev *nic_dev, u32 rx_mode);
int hinic_port_link_state(struct hinic_dev *nic_dev,
enum hinic_port_link_state *link_state);
int hinic_port_set_state(struct hinic_dev *nic_dev,
enum hinic_port_state state);
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment