From patchwork Wed Jul 12 14:17:15 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aviad Krawczyk X-Patchwork-Id: 787288 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3x71PN4fQVz9s2s for ; Thu, 13 Jul 2017 00:22:32 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753008AbdGLOTl (ORCPT ); Wed, 12 Jul 2017 10:19:41 -0400 Received: from szxga03-in.huawei.com ([45.249.212.189]:8906 "EHLO szxga03-in.huawei.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752957AbdGLOTi (ORCPT ); Wed, 12 Jul 2017 10:19:38 -0400 Received: from 172.30.72.57 (EHLO DGGEML402-HUB.china.huawei.com) ([172.30.72.57]) by dggrg03-dlp.huawei.com (MOS 4.4.6-GA FastPath queued) with ESMTP id AQZ88189; Wed, 12 Jul 2017 22:19:27 +0800 (CST) Received: from SZV1000299114.huawei.com (10.162.197.60) by DGGEML402-HUB.china.huawei.com (10.3.17.38) with Microsoft SMTP Server id 14.3.301.0; Wed, 12 Jul 2017 22:19:17 +0800 From: Aviad Krawczyk To: CC: , , , , , , Subject: [PATCH net 09/20] net/hinic: Add Rx mode and link event handler Date: Wed, 12 Jul 2017 22:17:15 +0800 Message-ID: <13f1b08a430493e7714842345da649e64d0992b4.1499865197.git.aviad.krawczyk@huawei.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: References: MIME-Version: 1.0 X-Originating-IP: [10.162.197.60] X-CFilter-Loop: Reflected X-Mirapoint-Virus-RAPID-Raw: score=unknown(0), refid=str=0001.0A020206.59662FEF.0258, ss=1, re=0.000, recu=0.000, reip=0.000, cl=1, cld=1, fgs=0, ip=0.0.0.0, so=2014-11-16 11:51:01, dmn=2013-03-21 17:37:32 X-Mirapoint-Loop-Id: aac9e5656e94748db76ceeac0a327ee2 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org 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: Aviad Krawczyk Signed-off-by: Zhaochen --- drivers/net/ethernet/huawei/hinic/hinic_dev.h | 17 ++ drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h | 2 + drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c | 118 +++++++++ drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h | 37 +++ drivers/net/ethernet/huawei/hinic/hinic_hw_if.c | 17 ++ drivers/net/ethernet/huawei/hinic/hinic_hw_if.h | 17 ++ drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c | 65 ++++- drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h | 28 +++ drivers/net/ethernet/huawei/hinic/hinic_main.c | 289 +++++++++++++++++++++- drivers/net/ethernet/huawei/hinic/hinic_port.c | 101 ++++++++ drivers/net/ethernet/huawei/hinic/hinic_port.h | 66 +++++ 11 files changed, 754 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/huawei/hinic/hinic_dev.h b/drivers/net/ethernet/huawei/hinic/hinic_dev.h index f642186..7cb9533 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_dev.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_dev.h @@ -19,20 +19,37 @@ #include #include #include +#include +#include #include "hinic_hw_dev.h" #define HINIC_DRV_NAME "HiNIC" #define HINIC_DRV_VERSION "1.0" +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 diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h index ae84719..6f9df4d 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h @@ -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 diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c index c6138f1..31747dd 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c @@ -237,6 +237,112 @@ int hinic_port_msg_cmd(struct hinic_hwdev *hwdev, enum hinic_port_cmd cmd, } /** + * 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 hinic_pfhwdev *pfhwdev; + struct hinic_nic_cb *nic_cb; + u8 cmd_cb; + + if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) { + pr_err("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 hinic_pfhwdev *pfhwdev; + struct hinic_nic_cb *nic_cb; + u8 cmd_cb; + + if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) { + pr_err("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 = (struct hinic_pfhwdev *)handle; + struct hinic_hwdev *hwdev = &pfhwdev->hwdev; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + struct hinic_nic_cb *nic_cb; + enum hinic_cb_state cb_state; + u8 cmd_cb; + + 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 = (enum hinic_mgmt_msg_cmd)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 * @@ -254,6 +360,11 @@ 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; } @@ -263,6 +374,13 @@ static int init_pfhwdev(struct hinic_pfhwdev *pfhwdev) **/ static void free_pfhwdev(struct hinic_pfhwdev *pfhwdev) { + struct hinic_hwdev *hwdev = &pfhwdev->hwdev; + struct hinic_hwif *hwif = hwdev->hwif; + + hinic_set_pf_action(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); } diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h index 03795be..d40d822 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h @@ -18,6 +18,7 @@ #include #include +#include #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); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_if.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.c index 79331ae..11dfa2d 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_if.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.c @@ -118,6 +118,23 @@ int hinic_msix_attr_cnt_clear(struct hinic_hwif *hwif, u16 msix_index) } /** + * 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 * diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h index cb6c2be..162c090 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h @@ -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_global_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); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c index 2759054..8a98625 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c @@ -75,6 +75,46 @@ enum msg_ack_type { }; /** + * 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 * @header: pointer of the header to prepare @@ -337,11 +377,32 @@ 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; - void *buf_out = recv_msg->buf_out; + void *handle, *buf_out = recv_msg->buf_out; enum hinic_mod_type mod = recv_msg->mod; + 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", mod); + if (mod >= HINIC_MOD_MAX) { + dev_err(&pdev->dev, "Unknown MGMT MSG module = %d\n", mod); + return; + } + + mgmt_cb = &pf_to_mgmt->mgmt_cb[mod]; + handle = mgmt_cb->handle; + + 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(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", mod); + + mgmt_cb->state &= ~HINIC_MGMT_CB_RUNNING; if (!recv_msg->async_mgmt_to_pf) /* MGMT sent sync msg, send the response */ diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h index a6a67cb..373b6aa 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h @@ -19,6 +19,7 @@ #include #include #include +#include #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 { void *msg; void *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, diff --git a/drivers/net/ethernet/huawei/hinic/hinic_main.c b/drivers/net/ethernet/huawei/hinic/hinic_main.c index 2d50650..6277112 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_main.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_main.c @@ -26,9 +26,11 @@ #include #include #include +#include #include #include #include +#include #include "hinic_pci_id_tbl.h" #include "hinic_hw_dev.h" @@ -40,14 +42,99 @@ MODULE_VERSION(HINIC_DRV_VERSION); MODULE_LICENSE("GPL"); +#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); + + err = hinic_port_link_state(nic_dev, &link_state); + if (err) { + netif_err(nic_dev, drv, netdev, "Failed to get link state\n"); + goto port_link_err; + } + + down(&nic_dev->mgmt_lock); + + 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; + +port_link_err: + 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); @@ -124,6 +211,82 @@ static int change_mac_addr(struct net_device *netdev, const u8 *addr) return err; } +/** + * set_mac_addr - adding mac address to network device + * @netdev: network device + * @addr: mac address to add + * + * Return 0 - Success, negative - Failure + **/ +static int set_mac_addr(struct net_device *netdev, const u8 *addr) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + unsigned long *vlan_bitmap = nic_dev->vlan_bitmap; + 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(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); + unsigned long *vlan_bitmap = nic_dev->vlan_bitmap; + 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(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) { @@ -192,12 +355,54 @@ 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); + struct net_device *netdev = nic_dev->netdev; + + netif_info(nic_dev, drv, netdev, "set rx mode work\n"); + + hinic_port_set_rx_mode(nic_dev, rx_mode_work->rx_mode); + + __dev_uc_sync(netdev, set_mac_addr, remove_mac_addr); + + __dev_mc_sync(netdev, set_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 = &nic_dev->rx_mode_work; + u32 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 */ }; @@ -211,6 +416,58 @@ static void netdev_features_init(struct net_device *netdev) } /** + * 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_dev *nic_dev = (struct hinic_dev *)handle; + struct net_device *netdev = nic_dev->netdev; + struct hinic_port_link_status *link_status, *ret_link_status; + + 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(netdev); + netif_tx_wake_all_queues(netdev); + } + + up(&nic_dev->mgmt_lock); + + netif_info(nic_dev, drv, netdev, "HINIC_Link is UP\n"); + } else { + down(&nic_dev->mgmt_lock); + + nic_dev->flags &= ~HINIC_LINK_UP; + + netif_carrier_off(netdev); + netif_tx_disable(netdev); + + up(&nic_dev->mgmt_lock); + + netif_info(nic_dev, drv, 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 * @@ -221,6 +478,7 @@ static int nic_dev_init(struct pci_dev *pdev) struct hinic_dev *nic_dev; struct net_device *netdev; struct hinic_hwdev *hwdev; + struct hinic_rx_mode_work *rx_mode_work; int err, num_qps; err = hinic_init_hwdev(&hwdev, pdev); @@ -249,6 +507,7 @@ static int nic_dev_init(struct pci_dev *pdev) nic_dev->hwdev = hwdev; nic_dev->netdev = netdev; nic_dev->msg_enable = MSG_ENABLE_DEFAULT; + nic_dev->flags = 0; sema_init(&nic_dev->mgmt_lock, 1); @@ -258,6 +517,12 @@ static int nic_dev_init(struct pci_dev *pdev) goto vlan_bitmap_err; } + nic_dev->workq = create_singlethread_workqueue(HINIC_WQ_NAME); + if (!nic_dev->workq) { + err = -ENOMEM; + goto workq_err; + } + pci_set_drvdata(pdev, netdev); err = hinic_port_get_mac(nic_dev, netdev->dev_addr); @@ -276,10 +541,16 @@ static int nic_dev_init(struct pci_dev *pdev) goto set_mtu_err; } + 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) { netif_err(nic_dev, probe, netdev, "Failed to register netdev\n"); @@ -289,9 +560,16 @@ static int nic_dev_init(struct pci_dev *pdev) return 0; reg_netdev_err: + hinic_hwdev_cb_unregister(nic_dev->hwdev, + HINIC_MGMT_MSG_CMD_LINK_STATUS); + cancel_work_sync(&rx_mode_work->work); + set_mtu_err: add_mac_err: pci_set_drvdata(pdev, NULL); + destroy_workqueue(nic_dev->workq); + +workq_err: kfree(nic_dev->vlan_bitmap); vlan_bitmap_err: @@ -366,15 +644,24 @@ static void hinic_remove(struct pci_dev *pdev) { struct net_device *netdev = pci_get_drvdata(pdev); struct hinic_dev *nic_dev; + struct hinic_rx_mode_work *rx_mode_work; if (!netdev) return; unregister_netdev(netdev); + nic_dev = netdev_priv(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); - nic_dev = netdev_priv(netdev); + destroy_workqueue(nic_dev->workq); kfree(nic_dev->vlan_bitmap); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_port.c b/drivers/net/ethernet/huawei/hinic/hinic_port.c index 7d9e87f..11e4ebf 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_port.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_port.c @@ -239,3 +239,104 @@ int hinic_port_del_vlan(struct hinic_dev *nic_dev, u16 vlan_id) return 0; } + +/** + * 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_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + struct hinic_port_rx_mode_cmd rx_mode_cmd; + int err; + + rx_mode_cmd.func_idx = HINIC_HWIF_GLOB_IDX(hwif); + rx_mode_cmd.rx_mode = rx_mode; + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_RX_MODE, + &rx_mode_cmd, sizeof(rx_mode_cmd), + NULL, NULL); + if (err) { + dev_err(&pdev->dev, "Failed to set RX mode\n"); + return err; + } + + return 0; +} + +/** + * 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 pci_dev *pdev = hwif->pdev; + struct hinic_port_link_cmd link_cmd; + u16 out_size; + int err; + + if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) { + pr_err("unsupported PCI Function type\n"); + return -EINVAL; + } + + link_cmd.func_idx = HINIC_HWIF_GLOB_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_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + struct hinic_port_state_cmd port_state; + u16 out_size; + int err; + + if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) { + pr_err("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; +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_port.h b/drivers/net/ethernet/huawei/hinic/hinic_port.h index 20672b8..abe645d 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_port.h +++ b/drivers/net/ethernet/huawei/hinic/hinic_port.h @@ -18,9 +18,28 @@ #include #include +#include #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 = 0, + HINIC_LINK_STATE_UP = 1, +}; + +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_del_mac(struct hinic_dev *nic_dev, const u8 *addr, 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