Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1691183 > unrolled thread
| Started by | Aviad Krawczyk <aviad.krawczyk@huawei.com> |
|---|---|
| First post | 2017-07-19 11:30 +0200 |
| Last post | 2017-07-19 11:40 +0200 |
| Articles | 7 — 1 participant |
Back to article view | Back to linux.kernel
[PATCH V2 net-next 00/21] Huawei HiNIC Ethernet Driver Aviad Krawczyk <aviad.krawczyk@huawei.com> - 2017-07-19 11:30 +0200
[PATCH V2 net-next 15/21] net-next/hinic: Add ceqs Aviad Krawczyk <aviad.krawczyk@huawei.com> - 2017-07-19 11:30 +0200
[PATCH V2 net-next 08/21] net-next/hinic: Add port management commands Aviad Krawczyk <aviad.krawczyk@huawei.com> - 2017-07-19 11:30 +0200
[PATCH V2 net-next 16/21] net-next/hinic: Add cmdq commands Aviad Krawczyk <aviad.krawczyk@huawei.com> - 2017-07-19 11:30 +0200
[PATCH V2 net-next 07/21] net-next/hinic: Add aeqs Aviad Krawczyk <aviad.krawczyk@huawei.com> - 2017-07-19 11:40 +0200
[PATCH V2 net-next 09/21] net-next/hinic: Add Rx mode and link event handler Aviad Krawczyk <aviad.krawczyk@huawei.com> - 2017-07-19 11:40 +0200
[PATCH V2 net-next 06/21] net-next/hinic: Add api cmd commands Aviad Krawczyk <aviad.krawczyk@huawei.com> - 2017-07-19 11:40 +0200
| From | Aviad Krawczyk <aviad.krawczyk@huawei.com> |
|---|---|
| Date | 2017-07-19 11:30 +0200 |
| Subject | [PATCH V2 net-next 00/21] Huawei HiNIC Ethernet Driver |
| Message-ID | <u4TsR-6zb-5@gated-at.bofh.it> |
The patch-set contains the support of the HiNIC Ethernet driver for hinic family of PCIE Network Interface Cards. The Huawei's PCIE HiNIC card is a new Ethernet card and hence there was a need of a new driver. The current driver is meant to be used for the Physical Function and there would soon be a support for Virtual Function and more features once the basic PF driver has been accepted. Changes v1 -> v2: 1. remove driver version - Andrew Lunn comment https://lkml.org/lkml/2017/7/12/372 2. replace kzalloc by devm_kzalloc for short clean - Andrew Lunn comment https://lkml.org/lkml/2017/7/12/372 3. replace pr_ functions by dev_ functions - Andrew Lunn comment https://lkml.org/lkml/2017/7/12/375 4. seperate last patch by moving ops to a new patch - Andrew Lunn comment https://lkml.org/lkml/2017/7/12/377 Aviad Krawczyk (21): net-next/hinic: Initialize hw interface net-next/hinic: Initialize hw device components net-next/hinic: Initialize api cmd resources net-next/hinic: Initialize api cmd hw net-next/hinic: Add management messages net-next/hinic: Add api cmd commands net-next/hinic: Add aeqs net-next/hinic: Add port management commands net-next/hinic: Add Rx mode and link event handler net-next/hinic: Add logical Txq and Rxq net-next/hinic: Add wq net-next/hinic: Add qp resources net-next/hinic: Set qp context net-next/hinic: Initialize cmdq net-next/hinic: Add ceqs net-next/hinic: Add cmdq commands net-next/hinic: Add cmdq completion handler net-next/hinic: Add Rx handler net-next/hinic: Add Tx operation net-next/hinic: Add ethtool and stats net-next/hinic: Add select_queue and netpoll Documentation/networking/hinic.txt | 125 +++ MAINTAINERS | 7 + drivers/net/ethernet/Kconfig | 1 + drivers/net/ethernet/Makefile | 1 + drivers/net/ethernet/huawei/Kconfig | 19 + drivers/net/ethernet/huawei/Makefile | 5 + drivers/net/ethernet/huawei/hinic/Kconfig | 13 + drivers/net/ethernet/huawei/hinic/Makefile | 6 + drivers/net/ethernet/huawei/hinic/hinic_common.c | 80 ++ drivers/net/ethernet/huawei/hinic/hinic_common.h | 38 + drivers/net/ethernet/huawei/hinic/hinic_dev.h | 64 ++ .../net/ethernet/huawei/hinic/hinic_hw_api_cmd.c | 978 +++++++++++++++++ .../net/ethernet/huawei/hinic/hinic_hw_api_cmd.h | 208 ++++ drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c | 940 ++++++++++++++++ drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.h | 302 ++++++ drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h | 149 +++ drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c | 1058 ++++++++++++++++++ drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h | 239 ++++ drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.c | 877 +++++++++++++++ drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.h | 265 +++++ drivers/net/ethernet/huawei/hinic/hinic_hw_if.c | 353 ++++++ drivers/net/ethernet/huawei/hinic/hinic_hw_if.h | 272 +++++ drivers/net/ethernet/huawei/hinic/hinic_hw_io.c | 537 +++++++++ drivers/net/ethernet/huawei/hinic/hinic_hw_io.h | 97 ++ drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c | 599 ++++++++++ drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h | 153 +++ drivers/net/ethernet/huawei/hinic/hinic_hw_qp.c | 864 +++++++++++++++ drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h | 394 +++++++ .../net/ethernet/huawei/hinic/hinic_hw_qp_ctxt.h | 214 ++++ drivers/net/ethernet/huawei/hinic/hinic_hw_wq.c | 885 +++++++++++++++ drivers/net/ethernet/huawei/hinic/hinic_hw_wq.h | 113 ++ drivers/net/ethernet/huawei/hinic/hinic_main.c | 1142 ++++++++++++++++++++ .../net/ethernet/huawei/hinic/hinic_pci_id_tbl.h | 27 + drivers/net/ethernet/huawei/hinic/hinic_port.c | 403 +++++++ drivers/net/ethernet/huawei/hinic/hinic_port.h | 198 ++++ drivers/net/ethernet/huawei/hinic/hinic_rx.c | 518 +++++++++ drivers/net/ethernet/huawei/hinic/hinic_rx.h | 55 + drivers/net/ethernet/huawei/hinic/hinic_tx.c | 516 +++++++++ drivers/net/ethernet/huawei/hinic/hinic_tx.h | 62 ++ 39 files changed, 12777 insertions(+) create mode 100644 Documentation/networking/hinic.txt create mode 100644 drivers/net/ethernet/huawei/Kconfig create mode 100644 drivers/net/ethernet/huawei/Makefile create mode 100644 drivers/net/ethernet/huawei/hinic/Kconfig create mode 100644 drivers/net/ethernet/huawei/hinic/Makefile create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_common.c create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_common.h create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_dev.h create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.c create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.h create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.h create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.c create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.h create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_hw_if.c create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_hw_if.h create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_hw_io.c create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_hw_io.h create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_hw_qp.c create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_hw_qp_ctxt.h create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_hw_wq.c create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_hw_wq.h create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_main.c create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_pci_id_tbl.h create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_port.c create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_port.h create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_rx.c create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_rx.h create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_tx.c create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_tx.h -- 1.9.1
[toc] | [next] | [standalone]
| From | Aviad Krawczyk <aviad.krawczyk@huawei.com> |
|---|---|
| Date | 2017-07-19 11:30 +0200 |
| Subject | [PATCH V2 net-next 15/21] net-next/hinic: Add ceqs |
| Message-ID | <u4TsU-6zb-71@gated-at.bofh.it> |
| In reply to | #1691183 |
Initialize the completion event queues and handle ceq events by calling
the registered handlers. Used for cmdq command completion.
Signed-off-by: Aviad Krawczyk <aviad.krawczyk@huawei.com>
Signed-off-by: Zhao Chen <zhaochen6@huawei.com>
---
drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c | 17 ++
drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h | 29 +++
drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c | 7 +-
drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.c | 290 +++++++++++++++++++++-
drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.h | 75 ++++++
drivers/net/ethernet/huawei/hinic/hinic_hw_io.c | 15 +-
drivers/net/ethernet/huawei/hinic/hinic_hw_io.h | 3 +
7 files changed, 428 insertions(+), 8 deletions(-)
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c
index f3a6e24..7099341 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c
+++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c
@@ -27,6 +27,7 @@
#include <asm/byteorder.h>
#include "hinic_hw_if.h"
+#include "hinic_hw_eqs.h"
#include "hinic_hw_mgmt.h"
#include "hinic_hw_wq.h"
#include "hinic_hw_cmdq.h"
@@ -110,6 +111,16 @@ int hinic_cmdq_direct_resp(struct hinic_cmdqs *cmdqs,
}
/**
+ * cmdq_ceq_handler - cmdq completion event handler
+ * @handle: private data for the handler(cmdqs)
+ * @ceqe_data: ceq element data
+ **/
+static void cmdq_ceq_handler(void *handle, u32 ceqe_data)
+{
+ /* should be implemented */
+}
+
+/**
* cmdq_init_queue_ctxt - init the queue ctxt of a cmdq
* @cmdq: the cmdq
* @cmdq_pages: the memory of the queue
@@ -322,6 +333,9 @@ int hinic_init_cmdqs(struct hinic_cmdqs *cmdqs, struct hinic_hwif *hwif,
goto cmdq_ctxt_err;
}
+ hinic_ceq_register_cb(&func_to_io->ceqs, HINIC_CEQ_CMDQ, cmdqs,
+ cmdq_ceq_handler);
+
return 0;
cmdq_ctxt_err:
@@ -342,10 +356,13 @@ int hinic_init_cmdqs(struct hinic_cmdqs *cmdqs, struct hinic_hwif *hwif,
**/
void hinic_free_cmdqs(struct hinic_cmdqs *cmdqs)
{
+ struct hinic_func_to_io *func_to_io = cmdqs_to_func_to_io(cmdqs);
struct hinic_hwif *hwif = cmdqs->hwif;
struct pci_dev *pdev = hwif->pdev;
enum hinic_cmdq_type cmdq_type;
+ hinic_ceq_unregister_cb(&func_to_io->ceqs, HINIC_CEQ_CMDQ);
+
cmdq_type = HINIC_CMDQ_SYNC;
for (; cmdq_type < HINIC_MAX_CMDQ_TYPES; cmdq_type++)
free_cmdq(&cmdqs->cmdq[cmdq_type]);
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h
index 1f57301..10b8c7b 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h
+++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h
@@ -81,27 +81,44 @@
/* EQ registers */
#define HINIC_AEQ_MTT_OFF_BASE_ADDR 0x200
+#define HINIC_CEQ_MTT_OFF_BASE_ADDR 0x400
#define HINIC_EQ_MTT_OFF_STRIDE 0x40
#define HINIC_CSR_AEQ_MTT_OFF(id) \
(HINIC_AEQ_MTT_OFF_BASE_ADDR + (id) * HINIC_EQ_MTT_OFF_STRIDE)
+#define HINIC_CSR_CEQ_MTT_OFF(id) \
+ (HINIC_CEQ_MTT_OFF_BASE_ADDR + (id) * HINIC_EQ_MTT_OFF_STRIDE)
+
#define HINIC_CSR_EQ_PAGE_OFF_STRIDE 8
#define HINIC_CSR_AEQ_HI_PHYS_ADDR_REG(q_id, pg_num) \
(HINIC_CSR_AEQ_MTT_OFF(q_id) + \
(pg_num) * HINIC_CSR_EQ_PAGE_OFF_STRIDE)
+#define HINIC_CSR_CEQ_HI_PHYS_ADDR_REG(q_id, pg_num) \
+ (HINIC_CSR_CEQ_MTT_OFF(q_id) + \
+ (pg_num) * HINIC_CSR_EQ_PAGE_OFF_STRIDE)
+
#define HINIC_CSR_AEQ_LO_PHYS_ADDR_REG(q_id, pg_num) \
(HINIC_CSR_AEQ_MTT_OFF(q_id) + \
(pg_num) * HINIC_CSR_EQ_PAGE_OFF_STRIDE + 4)
+#define HINIC_CSR_CEQ_LO_PHYS_ADDR_REG(q_id, pg_num) \
+ (HINIC_CSR_CEQ_MTT_OFF(q_id) + \
+ (pg_num) * HINIC_CSR_EQ_PAGE_OFF_STRIDE + 4)
+
#define HINIC_AEQ_CTRL_0_ADDR_BASE 0xE00
#define HINIC_AEQ_CTRL_1_ADDR_BASE 0xE04
#define HINIC_AEQ_CONS_IDX_ADDR_BASE 0xE08
#define HINIC_AEQ_PROD_IDX_ADDR_BASE 0xE0C
+#define HINIC_CEQ_CTRL_0_ADDR_BASE 0x1000
+#define HINIC_CEQ_CTRL_1_ADDR_BASE 0x1004
+#define HINIC_CEQ_CONS_IDX_ADDR_BASE 0x1008
+#define HINIC_CEQ_PROD_IDX_ADDR_BASE 0x100C
+
#define HINIC_EQ_OFF_STRIDE 0x80
#define HINIC_CSR_AEQ_CTRL_0_ADDR(idx) \
@@ -116,4 +133,16 @@
#define HINIC_CSR_AEQ_PROD_IDX_ADDR(idx) \
(HINIC_AEQ_PROD_IDX_ADDR_BASE + (idx) * HINIC_EQ_OFF_STRIDE)
+#define HINIC_CSR_CEQ_CTRL_0_ADDR(idx) \
+ (HINIC_CEQ_CTRL_0_ADDR_BASE + (idx) * HINIC_EQ_OFF_STRIDE)
+
+#define HINIC_CSR_CEQ_CTRL_1_ADDR(idx) \
+ (HINIC_CEQ_CTRL_1_ADDR_BASE + (idx) * HINIC_EQ_OFF_STRIDE)
+
+#define HINIC_CSR_CEQ_CONS_IDX_ADDR(idx) \
+ (HINIC_CEQ_CONS_IDX_ADDR_BASE + (idx) * HINIC_EQ_OFF_STRIDE)
+
+#define HINIC_CSR_CEQ_PROD_IDX_ADDR(idx) \
+ (HINIC_CEQ_PROD_IDX_ADDR_BASE + (idx) * HINIC_EQ_OFF_STRIDE)
+
#endif
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c
index acfc906..8c21b3a 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c
+++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c
@@ -281,6 +281,7 @@ int hinic_hwdev_ifup(struct hinic_hwdev *hwdev)
struct pci_dev *pdev = hwif->pdev;
int num_qps = nic_cap->num_qps;
int max_qps = nic_cap->max_qps;
+ struct msix_entry *ceq_msix_entries;
struct msix_entry *sq_msix_entries;
struct msix_entry *rq_msix_entries;
u16 base_qpn;
@@ -294,7 +295,11 @@ int hinic_hwdev_ifup(struct hinic_hwdev *hwdev)
num_aeqs = HINIC_HWIF_NUM_AEQS(hwif);
num_ceqs = HINIC_HWIF_NUM_CEQS(hwif);
- err = hinic_io_init(func_to_io, hwif, max_qps, 0, NULL);
+
+ ceq_msix_entries = &hwdev->msix_entries[num_aeqs];
+
+ err = hinic_io_init(func_to_io, hwif, max_qps, num_ceqs,
+ ceq_msix_entries);
if (err) {
dev_err(&pdev->dev, "Failed to init IO channel\n");
return err;
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.c
index afeab36..12ec913 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.c
+++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.c
@@ -37,14 +37,21 @@
#define GET_EQ_NUM_ELEMS_IN_PG(eq, pg_size) ((pg_size) / (eq)->elem_size)
-#define EQ_CONS_IDX_REG_ADDR(eq) HINIC_CSR_AEQ_CONS_IDX_ADDR((eq)->q_id)
-#define EQ_PROD_IDX_REG_ADDR(eq) HINIC_CSR_AEQ_PROD_IDX_ADDR((eq)->q_id)
+#define EQ_CONS_IDX_REG_ADDR(eq) (((eq)->type == HINIC_AEQ) ? \
+ HINIC_CSR_AEQ_CONS_IDX_ADDR((eq)->q_id) : \
+ HINIC_CSR_CEQ_CONS_IDX_ADDR((eq)->q_id))
-#define EQ_HI_PHYS_ADDR_REG(eq, pg_num) \
- HINIC_CSR_AEQ_HI_PHYS_ADDR_REG((eq)->q_id, pg_num)
+#define EQ_PROD_IDX_REG_ADDR(eq) (((eq)->type == HINIC_AEQ) ? \
+ HINIC_CSR_AEQ_PROD_IDX_ADDR((eq)->q_id) : \
+ HINIC_CSR_CEQ_PROD_IDX_ADDR((eq)->q_id))
-#define EQ_LO_PHYS_ADDR_REG(eq, pg_num) \
- HINIC_CSR_AEQ_LO_PHYS_ADDR_REG((eq)->q_id, pg_num)
+#define EQ_HI_PHYS_ADDR_REG(eq, pg_num) (((eq)->type == HINIC_AEQ) ? \
+ HINIC_CSR_AEQ_HI_PHYS_ADDR_REG((eq)->q_id, pg_num) : \
+ HINIC_CSR_CEQ_HI_PHYS_ADDR_REG((eq)->q_id, pg_num))
+
+#define EQ_LO_PHYS_ADDR_REG(eq, pg_num) (((eq)->type == HINIC_AEQ) ? \
+ HINIC_CSR_AEQ_LO_PHYS_ADDR_REG((eq)->q_id, pg_num) : \
+ HINIC_CSR_CEQ_LO_PHYS_ADDR_REG((eq)->q_id, pg_num))
#define GET_EQ_ELEMENT(eq, idx) \
((eq)->virt_addr[(idx) / (eq)->num_elem_in_pg] + \
@@ -53,8 +60,13 @@
#define GET_AEQ_ELEM(eq, idx) ((struct hinic_aeq_elem *) \
GET_EQ_ELEMENT(eq, idx))
+#define GET_CEQ_ELEM(eq, idx) ((u32 *) \
+ GET_EQ_ELEMENT(eq, idx))
+
#define GET_CURR_AEQ_ELEM(eq) GET_AEQ_ELEM(eq, (eq)->cons_idx)
+#define GET_CURR_CEQ_ELEM(eq) GET_CEQ_ELEM(eq, (eq)->cons_idx)
+
#define PAGE_IN_4K(page_size) ((page_size) >> 12)
#define EQ_SET_HW_PAGE_SIZE_VAL(eq) (ilog2(PAGE_IN_4K((eq)->page_size)))
@@ -63,13 +75,29 @@
#define EQ_MAX_PAGES 8
+#define CEQE_TYPE_SHIFT 23
+#define CEQE_TYPE_MASK 0x7
+
+#define CEQE_TYPE(ceqe) (((ceqe) >> CEQE_TYPE_SHIFT) & \
+ CEQE_TYPE_MASK)
+
+#define CEQE_DATA_MASK 0x3FFFFFF
+#define CEQE_DATA(ceqe) ((ceqe) & CEQE_DATA_MASK)
+
#define aeq_to_aeqs(eq) \
container_of((eq) - (eq)->q_id, struct hinic_aeqs, aeq[0])
+#define ceq_to_ceqs(eq) \
+ container_of((eq) - (eq)->q_id, struct hinic_ceqs, ceq[0])
+
#define work_to_aeq_work(work) \
container_of(work, struct hinic_eq_work, work)
#define DMA_ATTR_AEQ_DEFAULT 0
+#define DMA_ATTR_CEQ_DEFAULT 0
+
+/* No coalescence */
+#define THRESH_CEQ_DEFAULT 0
enum eq_int_mode {
EQ_INT_MODE_ARMED,
@@ -118,6 +146,42 @@ void hinic_aeq_unregister_hw_cb(struct hinic_aeqs *aeqs,
hwe_cb->hwe_handler = NULL;
}
+/**
+ * hinic_ceq_register_cb - register CEQ callback for specific event
+ * @ceqs: pointer to Completion eqs part of the chip
+ * @event: ceq event to register callback for it
+ * @handle: private data will be used by the callback
+ * @handler: callback function
+ **/
+void hinic_ceq_register_cb(struct hinic_ceqs *ceqs,
+ enum hinic_ceq_type event, void *handle,
+ void (*handler)(void *handle, u32 ceqe_data))
+{
+ struct hinic_ceq_cb *ceq_cb = &ceqs->ceq_cb[event];
+
+ ceq_cb->handler = handler;
+ ceq_cb->handle = handle;
+ ceq_cb->ceqe_state = HINIC_EQE_ENABLED;
+}
+
+/**
+ * hinic_ceq_unregister_cb - unregister the CEQ callback for specific event
+ * @ceqs: pointer to Completion eqs part of the chip
+ * @event: ceq event to unregister callback for it
+ **/
+void hinic_ceq_unregister_cb(struct hinic_ceqs *ceqs,
+ enum hinic_ceq_type event)
+{
+ struct hinic_ceq_cb *ceq_cb = &ceqs->ceq_cb[event];
+
+ ceq_cb->ceqe_state &= ~HINIC_EQE_ENABLED;
+
+ while (ceq_cb->ceqe_state & HINIC_EQE_RUNNING)
+ schedule();
+
+ ceq_cb->handler = NULL;
+}
+
static u8 eq_cons_idx_checksum_set(u32 val)
{
u8 checksum = 0;
@@ -220,6 +284,70 @@ static void aeq_irq_handler(struct hinic_eq *eq)
}
/**
+ * ceq_event_handler - handler for the ceq events
+ * @ceqs: ceqs part of the chip
+ * @ceqe: ceq element that describes the event
+ **/
+static void ceq_event_handler(struct hinic_ceqs *ceqs, u32 ceqe)
+{
+ struct hinic_hwif *hwif = ceqs->hwif;
+ struct pci_dev *pdev = hwif->pdev;
+ enum hinic_ceq_type event = CEQE_TYPE(ceqe);
+ u32 ceqe_data = CEQE_DATA(ceqe);
+ struct hinic_ceq_cb *ceq_cb;
+ unsigned long eqe_state;
+
+ if (event >= HINIC_MAX_CEQ_EVENTS) {
+ dev_err(&pdev->dev, "Unknown CEQ event, event = %d\n", event);
+ return;
+ }
+
+ ceq_cb = &ceqs->ceq_cb[event];
+
+ eqe_state = cmpxchg(&ceq_cb->ceqe_state,
+ HINIC_EQE_ENABLED,
+ HINIC_EQE_ENABLED | HINIC_EQE_RUNNING);
+
+ if ((eqe_state == HINIC_EQE_ENABLED) && (ceq_cb->handler))
+ ceq_cb->handler(ceq_cb->handle, ceqe_data);
+ else
+ dev_err(&pdev->dev, "Unhandled CEQ Event %d\n", event);
+
+ ceq_cb->ceqe_state &= ~HINIC_EQE_RUNNING;
+}
+
+/**
+ * ceq_irq_handler - handler for the CEQ event
+ * @eq: the Completion Event Queue that received the event
+ **/
+static void ceq_irq_handler(struct hinic_eq *eq)
+{
+ struct hinic_ceqs *ceqs = ceq_to_ceqs(eq);
+ u32 ceqe;
+ int i;
+
+ for (i = 0; i < eq->q_len; i++) {
+ ceqe = *(GET_CURR_CEQ_ELEM(eq));
+
+ /* Data in HW is in Big endian Format */
+ ceqe = be32_to_cpu(ceqe);
+
+ /* HW toggles the wrapped bit, when it adds eq element event */
+ if (HINIC_EQ_ELEM_DESC_GET(ceqe, WRAPPED) == eq->wrapped)
+ break;
+
+ ceq_event_handler(ceqs, ceqe);
+
+ eq->cons_idx++;
+
+ if (eq->cons_idx == eq->q_len) {
+ eq->cons_idx = 0;
+ eq->wrapped = !eq->wrapped;
+ }
+ }
+}
+
+/**
* eq_irq_handler - handler for the EQ event
* @data: the Event Queue that received the event
**/
@@ -229,6 +357,8 @@ static void eq_irq_handler(void *data)
if (eq->type == HINIC_AEQ)
aeq_irq_handler(eq);
+ else if (eq->type == HINIC_CEQ)
+ ceq_irq_handler(eq);
eq_update_ci(eq);
}
@@ -246,6 +376,17 @@ static void eq_irq_work(struct work_struct *work)
}
/**
+ * ceq_tasklet - the tasklet of the EQ that received the event
+ * @ceq_data: the eq
+ **/
+static void ceq_tasklet(unsigned long ceq_data)
+{
+ struct hinic_eq *ceq = (struct hinic_eq *)ceq_data;
+
+ eq_irq_handler(ceq);
+}
+
+/**
* aeq_interrupt - aeq interrupt handler
* @irq: irq number
* @data: the Async Event Queue that collected the event
@@ -270,6 +411,24 @@ static irqreturn_t aeq_interrupt(int irq, void *data)
}
/**
+ * ceq_interrupt - ceq interrupt handler
+ * @irq: irq number
+ * @data: the Completion Event Queue that collected the event
+ **/
+static irqreturn_t ceq_interrupt(int irq, void *data)
+{
+ struct hinic_eq *ceq = (struct hinic_eq *)data;
+ struct hinic_hwif *hwif = ceq->hwif;
+
+ /* clear resend timer cnt register */
+ hinic_msix_attr_cnt_clear(hwif, ceq->msix_entry.entry);
+
+ tasklet_schedule(&ceq->ceq_tasklet);
+
+ return IRQ_HANDLED;
+}
+
+/**
* set_eq_ctrls - setting eq's ctrl registers
* @eq: the Event Queue for setting
**/
@@ -320,6 +479,44 @@ static void set_eq_ctrls(struct hinic_eq *eq)
val |= ctrl1;
hinic_hwif_write_reg(hwif, addr, val);
+ } else if (type == HINIC_CEQ) {
+ /* RMW Ctrl0 */
+ addr = HINIC_CSR_CEQ_CTRL_0_ADDR(eq->q_id);
+
+ val = hinic_hwif_read_reg(hwif, addr);
+
+ val = HINIC_CEQ_CTRL_0_CLEAR(val, INTR_IDX) &
+ HINIC_CEQ_CTRL_0_CLEAR(val, DMA_ATTR) &
+ HINIC_CEQ_CTRL_0_CLEAR(val, KICK_THRESH) &
+ HINIC_CEQ_CTRL_0_CLEAR(val, PCI_INTF_IDX) &
+ HINIC_CEQ_CTRL_0_CLEAR(val, INTR_MODE);
+
+ ctrl0 = HINIC_CEQ_CTRL_0_SET(msix_entry->entry, INTR_IDX) |
+ HINIC_CEQ_CTRL_0_SET(DMA_ATTR_CEQ_DEFAULT, DMA_ATTR) |
+ HINIC_CEQ_CTRL_0_SET(THRESH_CEQ_DEFAULT, KICK_THRESH) |
+ HINIC_CEQ_CTRL_0_SET(pci_intf_idx, PCI_INTF_IDX) |
+ HINIC_CEQ_CTRL_0_SET(EQ_INT_MODE_ARMED, INTR_MODE);
+
+ val |= ctrl0;
+
+ hinic_hwif_write_reg(hwif, addr, val);
+
+ /* RMW Ctrl1 */
+ addr = HINIC_CSR_CEQ_CTRL_1_ADDR(eq->q_id);
+
+ page_size_val = EQ_SET_HW_PAGE_SIZE_VAL(eq);
+
+ val = hinic_hwif_read_reg(hwif, addr);
+
+ val = HINIC_CEQ_CTRL_1_CLEAR(val, LEN) &
+ HINIC_CEQ_CTRL_1_CLEAR(val, PAGE_SIZE);
+
+ ctrl1 = HINIC_CEQ_CTRL_1_SET(eq->q_len, LEN) |
+ HINIC_CEQ_CTRL_1_SET(page_size_val, PAGE_SIZE);
+
+ val |= ctrl1;
+
+ hinic_hwif_write_reg(hwif, addr, val);
}
}
@@ -342,6 +539,24 @@ static void aeq_elements_init(struct hinic_eq *eq, u32 init_val)
}
/**
+ * ceq_elements_init - Initialize all the elements in the ceq
+ * @eq: the event queue
+ * @init_val: value to init with it the elements
+ **/
+static void ceq_elements_init(struct hinic_eq *eq, u32 init_val)
+{
+ int i;
+ u32 *ceqe;
+
+ for (i = 0; i < eq->q_len; i++) {
+ ceqe = GET_CEQ_ELEM(eq, i);
+ *(ceqe) = cpu_to_be32(init_val);
+ }
+
+ wmb(); /* Write the initilzation values */
+}
+
+/**
* alloc_eq_pages - allocate the pages for the queue
* @eq: the event queue
*
@@ -393,6 +608,8 @@ static int alloc_eq_pages(struct hinic_eq *eq)
if (eq->type == HINIC_AEQ)
aeq_elements_init(eq, init_val);
+ else if (eq->type == HINIC_CEQ)
+ ceq_elements_init(eq, init_val);
return 0;
@@ -462,6 +679,8 @@ static int init_eq(struct hinic_eq *eq, struct hinic_hwif *hwif,
if (type == HINIC_AEQ) {
eq->elem_size = HINIC_AEQE_SIZE;
+ } else if (type == HINIC_CEQ) {
+ eq->elem_size = HINIC_CEQE_SIZE;
} else {
dev_err(&pdev->dev, "Invalid EQ type\n");
return -EINVAL;
@@ -495,6 +714,9 @@ static int init_eq(struct hinic_eq *eq, struct hinic_hwif *hwif,
struct hinic_eq_work *aeq_work = &eq->aeq_work;
INIT_WORK(&aeq_work->work, eq_irq_work);
+ } else if (type == HINIC_CEQ) {
+ tasklet_init(&eq->ceq_tasklet, ceq_tasklet,
+ (unsigned long)eq);
}
/* set the attributes of the msix entry */
@@ -508,6 +730,9 @@ static int init_eq(struct hinic_eq *eq, struct hinic_hwif *hwif,
if (type == HINIC_AEQ)
err = request_irq(entry.vector, aeq_interrupt, 0,
"hinic_aeq", eq);
+ else if (type == HINIC_CEQ)
+ err = request_irq(entry.vector, ceq_interrupt, 0,
+ "hinic_ceq", eq);
if (err) {
dev_err(&pdev->dev, "Failed to request irq for the EQ\n");
@@ -535,6 +760,8 @@ static void remove_eq(struct hinic_eq *eq)
struct hinic_eq_work *aeq_work = &eq->aeq_work;
cancel_work_sync(&aeq_work->work);
+ } else if (eq->type == HINIC_CEQ) {
+ tasklet_kill(&eq->ceq_tasklet);
}
free_eq_pages(eq);
@@ -597,3 +824,54 @@ void hinic_aeqs_free(struct hinic_aeqs *aeqs)
destroy_workqueue(aeqs->workq);
}
+
+/**
+ * hinic_ceqs_init - init all the ceqs
+ * @ceqs: ceqs part of the chip
+ * @hwif: the hardware interface of a pci function device
+ * @num_ceqs: number of CEQs
+ * @q_len: number of EQ elements
+ * @page_size: the page size of the event queue
+ * @msix_entries: msix entries associated with the event queues
+ *
+ * Return 0 - Success, Negative - Failure
+ **/
+int hinic_ceqs_init(struct hinic_ceqs *ceqs, struct hinic_hwif *hwif,
+ int num_ceqs, u32 q_len, u32 page_size,
+ struct msix_entry *msix_entries)
+{
+ struct pci_dev *pdev = hwif->pdev;
+ int i, q_id, err;
+
+ ceqs->hwif = hwif;
+ ceqs->num_ceqs = num_ceqs;
+
+ for (q_id = 0; q_id < num_ceqs; q_id++) {
+ err = init_eq(&ceqs->ceq[q_id], hwif, HINIC_CEQ, q_id, q_len,
+ page_size, msix_entries[q_id]);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to init ceq %d\n", q_id);
+ goto init_ceq_err;
+ }
+ }
+
+ return 0;
+
+init_ceq_err:
+ for (i = 0; i < q_id; i++)
+ remove_eq(&ceqs->ceq[i]);
+
+ return err;
+}
+
+/**
+ * hinic_ceqs_free - free all the ceqs
+ * @ceqs: ceqs part of the chip
+ **/
+void hinic_ceqs_free(struct hinic_ceqs *ceqs)
+{
+ int q_id;
+
+ for (q_id = 0; q_id < ceqs->num_ceqs; q_id++)
+ remove_eq(&ceqs->ceq[q_id]);
+}
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.h
index f8f06d2..5a1468c 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.h
+++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.h
@@ -21,6 +21,7 @@
#include <linux/pci.h>
#include <linux/sizes.h>
#include <linux/bitops.h>
+#include <linux/interrupt.h>
#include "hinic_hw_if.h"
@@ -58,6 +59,40 @@
((val) & (~(HINIC_AEQ_CTRL_1_##member##_MASK \
<< HINIC_AEQ_CTRL_1_##member##_SHIFT)))
+#define HINIC_CEQ_CTRL_0_INTR_IDX_SHIFT 0
+#define HINIC_CEQ_CTRL_0_DMA_ATTR_SHIFT 12
+#define HINIC_CEQ_CTRL_0_KICK_THRESH_SHIFT 20
+#define HINIC_CEQ_CTRL_0_PCI_INTF_IDX_SHIFT 24
+#define HINIC_CEQ_CTRL_0_INTR_MODE_SHIFT 31
+
+#define HINIC_CEQ_CTRL_0_INTR_IDX_MASK 0x3FF
+#define HINIC_CEQ_CTRL_0_DMA_ATTR_MASK 0x3F
+#define HINIC_CEQ_CTRL_0_KICK_THRESH_MASK 0xF
+#define HINIC_CEQ_CTRL_0_PCI_INTF_IDX_MASK 0x3
+#define HINIC_CEQ_CTRL_0_INTR_MODE_MASK 0x1
+
+#define HINIC_CEQ_CTRL_0_SET(val, member) \
+ (((u32)(val) & HINIC_CEQ_CTRL_0_##member##_MASK) << \
+ HINIC_CEQ_CTRL_0_##member##_SHIFT)
+
+#define HINIC_CEQ_CTRL_0_CLEAR(val, member) \
+ ((val) & (~(HINIC_CEQ_CTRL_0_##member##_MASK \
+ << HINIC_CEQ_CTRL_0_##member##_SHIFT)))
+
+#define HINIC_CEQ_CTRL_1_LEN_SHIFT 0
+#define HINIC_CEQ_CTRL_1_PAGE_SIZE_SHIFT 28
+
+#define HINIC_CEQ_CTRL_1_LEN_MASK 0x1FFFFF
+#define HINIC_CEQ_CTRL_1_PAGE_SIZE_MASK 0xF
+
+#define HINIC_CEQ_CTRL_1_SET(val, member) \
+ (((u32)(val) & HINIC_CEQ_CTRL_1_##member##_MASK) << \
+ HINIC_CEQ_CTRL_1_##member##_SHIFT)
+
+#define HINIC_CEQ_CTRL_1_CLEAR(val, member) \
+ ((val) & (~(HINIC_CEQ_CTRL_1_##member##_MASK \
+ << HINIC_CEQ_CTRL_1_##member##_SHIFT)))
+
#define HINIC_EQ_ELEM_DESC_TYPE_SHIFT 0
#define HINIC_EQ_ELEM_DESC_SRC_SHIFT 7
#define HINIC_EQ_ELEM_DESC_SIZE_SHIFT 8
@@ -95,14 +130,17 @@
<< HINIC_EQ_CI_##member##_SHIFT)))
#define HINIC_MAX_AEQS 4
+#define HINIC_MAX_CEQS 32
#define HINIC_AEQE_SIZE 64
+#define HINIC_CEQE_SIZE 4
#define HINIC_AEQE_DESC_SIZE 4
#define HINIC_AEQE_DATA_SIZE \
(HINIC_AEQE_SIZE - HINIC_AEQE_DESC_SIZE)
#define HINIC_DEFAULT_AEQ_LEN 64
+#define HINIC_DEFAULT_CEQ_LEN 1024
#define HINIC_EQ_PAGE_SIZE SZ_4K
@@ -110,6 +148,7 @@
enum hinic_eq_type {
HINIC_AEQ,
+ HINIC_CEQ,
};
enum hinic_aeq_type {
@@ -118,6 +157,12 @@ enum hinic_aeq_type {
HINIC_MAX_AEQ_EVENTS,
};
+enum hinic_ceq_type {
+ HINIC_CEQ_CMDQ = 3,
+
+ HINIC_MAX_CEQ_EVENTS,
+};
+
enum hinic_eqe_state {
HINIC_EQE_ENABLED = BIT(0),
HINIC_EQE_RUNNING = BIT(1),
@@ -154,6 +199,8 @@ struct hinic_eq {
void **virt_addr;
struct hinic_eq_work aeq_work;
+
+ struct tasklet_struct ceq_tasklet;
};
struct hinic_hw_event_cb {
@@ -173,6 +220,21 @@ struct hinic_aeqs {
struct workqueue_struct *workq;
};
+struct hinic_ceq_cb {
+ void (*handler)(void *handle, u32 ceqe_data);
+ void *handle;
+ enum hinic_eqe_state ceqe_state;
+};
+
+struct hinic_ceqs {
+ struct hinic_hwif *hwif;
+
+ struct hinic_eq ceq[HINIC_MAX_CEQS];
+ int num_ceqs;
+
+ struct hinic_ceq_cb ceq_cb[HINIC_MAX_CEQ_EVENTS];
+};
+
void hinic_aeq_register_hw_cb(struct hinic_aeqs *aeqs,
enum hinic_aeq_type event, void *handle,
void (*hwe_handler)(void *handle, void *data,
@@ -181,10 +243,23 @@ void hinic_aeq_register_hw_cb(struct hinic_aeqs *aeqs,
void hinic_aeq_unregister_hw_cb(struct hinic_aeqs *aeqs,
enum hinic_aeq_type event);
+void hinic_ceq_register_cb(struct hinic_ceqs *ceqs,
+ enum hinic_ceq_type event, void *handle,
+ void (*ceq_cb)(void *handle, u32 ceqe_data));
+
+void hinic_ceq_unregister_cb(struct hinic_ceqs *ceqs,
+ enum hinic_ceq_type event);
+
int hinic_aeqs_init(struct hinic_aeqs *aeqs, struct hinic_hwif *hwif,
int num_aeqs, u32 q_len, u32 page_size,
struct msix_entry *msix_entries);
void hinic_aeqs_free(struct hinic_aeqs *aeqs);
+int hinic_ceqs_init(struct hinic_ceqs *ceqs, struct hinic_hwif *hwif,
+ int num_ceqs, u32 q_len, u32 page_size,
+ struct msix_entry *msix_entries);
+
+void hinic_ceqs_free(struct hinic_ceqs *ceqs);
+
#endif
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_io.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_io.c
index f40346e..e9c8c4b 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_hw_io.c
+++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_io.c
@@ -24,6 +24,7 @@
#include <linux/io.h>
#include "hinic_hw_if.h"
+#include "hinic_hw_eqs.h"
#include "hinic_hw_wq.h"
#include "hinic_hw_cmdq.h"
#include "hinic_hw_qp_ctxt.h"
@@ -462,10 +463,18 @@ int hinic_io_init(struct hinic_func_to_io *func_to_io,
func_to_io->qps = NULL;
func_to_io->max_qps = max_qps;
+ err = hinic_ceqs_init(&func_to_io->ceqs, hwif, num_ceqs,
+ HINIC_DEFAULT_CEQ_LEN, HINIC_EQ_PAGE_SIZE,
+ ceq_msix_entries);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to init CEQs\n");
+ return err;
+ }
+
err = hinic_wqs_alloc(&func_to_io->wqs, 2 * max_qps, hwif);
if (err) {
dev_err(&pdev->dev, "Failed to allocate WQS for IO\n");
- return err;
+ goto wqs_alloc_err;
}
func_to_io->db_base = pci_ioremap_bar(pdev, HINIC_PCI_DB_BAR);
@@ -503,6 +512,9 @@ int hinic_io_init(struct hinic_func_to_io *func_to_io,
db_ioremap_err:
hinic_wqs_free(&func_to_io->wqs);
+
+wqs_alloc_err:
+ hinic_ceqs_free(&func_to_io->ceqs);
return err;
}
@@ -521,4 +533,5 @@ void hinic_io_free(struct hinic_func_to_io *func_to_io)
iounmap(func_to_io->db_base);
hinic_wqs_free(&func_to_io->wqs);
+ hinic_ceqs_free(&func_to_io->ceqs);
}
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_io.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_io.h
index 60d77b34..cfc21ba 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_hw_io.h
+++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_io.h
@@ -22,6 +22,7 @@
#include <linux/sizes.h>
#include "hinic_hw_if.h"
+#include "hinic_hw_eqs.h"
#include "hinic_hw_wq.h"
#include "hinic_hw_cmdq.h"
#include "hinic_hw_qp.h"
@@ -46,6 +47,8 @@ struct hinic_free_db_area {
struct hinic_func_to_io {
struct hinic_hwif *hwif;
+ struct hinic_ceqs ceqs;
+
struct hinic_wqs wqs;
struct hinic_wq *sq_wq;
--
1.9.1
[toc] | [prev] | [next] | [standalone]
| From | Aviad Krawczyk <aviad.krawczyk@huawei.com> |
|---|---|
| Date | 2017-07-19 11:30 +0200 |
| Subject | [PATCH V2 net-next 08/21] net-next/hinic: Add port management commands |
| Message-ID | <u4TsU-6zb-77@gated-at.bofh.it> |
| In reply to | #1691183 |
Add the port management commands that are sent as management messages.
The port management commands are used for netdev operations.
Signed-off-by: Aviad Krawczyk <aviad.krawczyk@huawei.com>
Signed-off-by: Zhao Chen <zhaochen6@huawei.com>
---
drivers/net/ethernet/huawei/hinic/Makefile | 4 +-
drivers/net/ethernet/huawei/hinic/hinic_dev.h | 4 +
drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c | 30 +++
drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h | 29 +++
drivers/net/ethernet/huawei/hinic/hinic_main.c | 201 ++++++++++++++++++-
drivers/net/ethernet/huawei/hinic/hinic_port.c | 241 +++++++++++++++++++++++
drivers/net/ethernet/huawei/hinic/hinic_port.h | 68 +++++++
7 files changed, 574 insertions(+), 3 deletions(-)
create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_port.c
create mode 100644 drivers/net/ethernet/huawei/hinic/hinic_port.h
diff --git a/drivers/net/ethernet/huawei/hinic/Makefile b/drivers/net/ethernet/huawei/hinic/Makefile
index 88223d0..08951a6 100644
--- a/drivers/net/ethernet/huawei/hinic/Makefile
+++ b/drivers/net/ethernet/huawei/hinic/Makefile
@@ -1,4 +1,4 @@
obj-$(CONFIG_HINIC) += hinic.o
-hinic-y := hinic_main.o hinic_hw_dev.o hinic_hw_mgmt.o hinic_hw_api_cmd.o \
- hinic_hw_eqs.o hinic_hw_if.o
\ No newline at end of file
+hinic-y := hinic_main.o hinic_port.o hinic_hw_dev.o hinic_hw_mgmt.o \
+ hinic_hw_api_cmd.o hinic_hw_eqs.o hinic_hw_if.o
\ No newline at end of file
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_dev.h b/drivers/net/ethernet/huawei/hinic/hinic_dev.h
index f23e8af..dd540b4 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_dev.h
+++ b/drivers/net/ethernet/huawei/hinic/hinic_dev.h
@@ -18,6 +18,7 @@
#include <linux/netdevice.h>
#include <linux/types.h>
+#include <linux/semaphore.h>
#include "hinic_hw_dev.h"
@@ -28,6 +29,9 @@ struct hinic_dev {
struct hinic_hwdev *hwdev;
u32 msg_enable;
+
+ struct semaphore mgmt_lock;
+ unsigned long *vlan_bitmap;
};
#endif
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c
index c61ff10..52d9bb7 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c
+++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c
@@ -202,6 +202,36 @@ static void disable_msix(struct hinic_hwdev *hwdev)
}
/**
+ * hinic_port_msg_cmd - send port msg to mgmt
+ * @hwdev: the NIC HW device
+ * @cmd: the port command
+ * @buf_in: input buffer
+ * @in_size: input size
+ * @buf_out: output buffer
+ * @out_size: returned output size
+ *
+ * Return 0 - Success, negative - Failure
+ **/
+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)
+{
+ struct hinic_pfhwdev *pfhwdev;
+ struct hinic_hwif *hwif = hwdev->hwif;
+ struct pci_dev *pdev = hwif->pdev;
+
+ if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) {
+ dev_err(&pdev->dev, "unsupported PCI Function type\n");
+ return -EINVAL;
+ }
+
+ pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev);
+
+ return hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_L2NIC, cmd,
+ buf_in, in_size, buf_out, out_size,
+ HINIC_MGMT_MSG_SYNC);
+}
+
+/**
* init_pfhwdev - Initialize the extended components of PF
* @pfhwdev: the HW device for PF
*
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h
index 5b4b686..b3325e7 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h
+++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h
@@ -30,6 +30,31 @@ struct hinic_cap {
u16 num_qps;
};
+enum hinic_port_cmd {
+ HINIC_PORT_CMD_CHANGE_MTU = 2,
+
+ HINIC_PORT_CMD_ADD_VLAN = 3,
+ HINIC_PORT_CMD_DEL_VLAN = 4,
+
+ HINIC_PORT_CMD_SET_MAC = 9,
+ HINIC_PORT_CMD_GET_MAC = 10,
+ HINIC_PORT_CMD_DEL_MAC = 11,
+
+ HINIC_PORT_CMD_SET_RX_MODE = 12,
+
+ HINIC_PORT_CMD_GET_LINK_STATE = 24,
+
+ HINIC_PORT_CMD_SET_PORT_STATE = 41,
+
+ HINIC_PORT_CMD_FWCTXT_INIT = 69,
+
+ HINIC_PORT_CMD_SET_FUNC_STATE = 93,
+
+ HINIC_PORT_CMD_GET_GLOBAL_QPN = 102,
+
+ HINIC_PORT_CMD_GET_CAP = 170,
+};
+
struct hinic_hwdev {
struct hinic_hwif *hwif;
struct msix_entry *msix_entries;
@@ -45,6 +70,10 @@ struct hinic_pfhwdev {
struct hinic_pf_to_mgmt pf_to_mgmt;
};
+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);
+
int hinic_init_hwdev(struct hinic_hwdev **hwdev, struct pci_dev *pdev);
void hinic_free_hwdev(struct hinic_hwdev *hwdev);
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_main.c b/drivers/net/ethernet/huawei/hinic/hinic_main.c
index c61c769..09d303b 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_main.c
+++ b/drivers/net/ethernet/huawei/hinic/hinic_main.c
@@ -13,6 +13,7 @@
*
*/
+#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/device.h>
@@ -20,9 +21,16 @@
#include <linux/types.h>
#include <linux/etherdevice.h>
#include <linux/netdevice.h>
+#include <linux/slab.h>
+#include <linux/if_vlan.h>
+#include <linux/semaphore.h>
+#include <net/ip.h>
+#include <linux/bitops.h>
+#include <linux/bitmap.h>
#include "hinic_pci_id_tbl.h"
#include "hinic_hw_dev.h"
+#include "hinic_port.h"
#include "hinic_dev.h"
MODULE_AUTHOR("Huawei Technologies CO., Ltd");
@@ -33,10 +41,169 @@
NETIF_MSG_IFUP | \
NETIF_MSG_TX_ERR | NETIF_MSG_RX_ERR)
+#define VLAN_BITMAP_SIZE(nic_dev) (ALIGN(VLAN_N_VID, 8) / 8)
+
+static int change_mac_addr(struct net_device *netdev, const u8 *addr);
+
+static int hinic_change_mtu(struct net_device *netdev, int new_mtu)
+{
+ struct hinic_dev *nic_dev = netdev_priv(netdev);
+ int err;
+
+ netif_info(nic_dev, drv, netdev, "set_mtu mtu = %d\n", new_mtu);
+
+ err = hinic_port_set_mtu(nic_dev, new_mtu);
+ if (err)
+ dev_err(&netdev->dev, "Failed to set port mtu\n");
+ else
+ netdev->mtu = new_mtu;
+
+ return err;
+}
+
+static int hinic_set_mac_addr(struct net_device *netdev, void *addr)
+{
+ struct sockaddr *saddr = addr;
+ unsigned char new_mac[ETH_ALEN];
+ int err;
+
+ memcpy(new_mac, saddr->sa_data, ETH_ALEN);
+
+ err = change_mac_addr(netdev, new_mac);
+ if (!err)
+ memcpy(netdev->dev_addr, new_mac, ETH_ALEN);
+
+ return err;
+}
+
+/**
+ * change_mac_addr - change the main mac address of network device
+ * @netdev: network device
+ * @addr: mac address to set
+ *
+ * Return 0 - Success, negative - Failure
+ **/
+static int change_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, "change 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, netdev->dev_addr, vid);
+ if (err) {
+ dev_err(&netdev->dev, "Failed to delete mac\n");
+ break;
+ }
+
+ err = hinic_port_add_mac(nic_dev, addr, vid);
+ if (err) {
+ dev_err(&netdev->dev, "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;
+}
+
+static int hinic_vlan_rx_add_vid(struct net_device *netdev,
+ __always_unused __be16 proto, u16 vid)
+{
+ struct hinic_dev *nic_dev = netdev_priv(netdev);
+ unsigned long *vlan_bitmap = nic_dev->vlan_bitmap;
+ int ret, err;
+
+ netif_info(nic_dev, drv, netdev, "add vid = %d\n", vid);
+
+ down(&nic_dev->mgmt_lock);
+
+ err = hinic_port_add_vlan(nic_dev, vid);
+ if (err) {
+ dev_err(&netdev->dev, "Failed to add vlan\n");
+ goto vlan_add_err;
+ }
+
+ err = hinic_port_add_mac(nic_dev, netdev->dev_addr, vid);
+ if (err) {
+ dev_err(&netdev->dev, "Failed to set mac\n");
+ goto add_mac_err;
+ }
+
+ bitmap_set(vlan_bitmap, vid, 1);
+
+ up(&nic_dev->mgmt_lock);
+
+ return 0;
+
+add_mac_err:
+ ret = hinic_port_del_vlan(nic_dev, vid);
+ if (ret)
+ dev_err(&netdev->dev, "Failed to revert by removing vlan\n");
+
+vlan_add_err:
+ up(&nic_dev->mgmt_lock);
+ return err;
+}
+
+static int hinic_vlan_rx_kill_vid(struct net_device *netdev,
+ __always_unused __be16 proto, u16 vid)
+{
+ struct hinic_dev *nic_dev = netdev_priv(netdev);
+ unsigned long *vlan_bitmap = nic_dev->vlan_bitmap;
+ int err;
+
+ netif_info(nic_dev, drv, netdev, "remove vid = %d\n", vid);
+
+ down(&nic_dev->mgmt_lock);
+
+ err = hinic_port_del_vlan(nic_dev, vid);
+ if (err) {
+ dev_err(&netdev->dev, "Failed to delete vlan\n");
+ goto del_vlan_err;
+ }
+
+ bitmap_clear(vlan_bitmap, vid, 1);
+
+ up(&nic_dev->mgmt_lock);
+
+ return 0;
+
+del_vlan_err:
+ up(&nic_dev->mgmt_lock);
+ return err;
+}
+
static const struct net_device_ops hinic_netdev_ops = {
- /* Operations are empty, should be filled */
+ .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,
+ /* more operations should be filled */
};
+static void netdev_features_init(struct net_device *netdev)
+{
+ netdev->hw_features = NETIF_F_SG | NETIF_F_HIGHDMA;
+
+ netdev->vlan_features = netdev->hw_features;
+
+ netdev->features = netdev->hw_features | NETIF_F_HW_VLAN_CTAG_FILTER;
+}
+
/**
* nic_dev_init - Initialize the NIC device
* @pdev: the NIC pci device
@@ -77,8 +244,36 @@ static int nic_dev_init(struct pci_dev *pdev)
nic_dev->netdev = netdev;
nic_dev->msg_enable = MSG_ENABLE_DEFAULT;
+ sema_init(&nic_dev->mgmt_lock, 1);
+
+ nic_dev->vlan_bitmap = devm_kzalloc(&pdev->dev,
+ VLAN_BITMAP_SIZE(nic_dev),
+ GFP_KERNEL);
+ if (!nic_dev->vlan_bitmap) {
+ err = -ENOMEM;
+ goto vlan_bitmap_err;
+ }
+
pci_set_drvdata(pdev, netdev);
+ err = hinic_port_get_mac(nic_dev, netdev->dev_addr);
+ if (err)
+ dev_warn(&netdev->dev, "Failed to get mac address\n");
+
+ err = hinic_port_add_mac(nic_dev, netdev->dev_addr, 0);
+ if (err) {
+ dev_err(&netdev->dev, "Failed to add mac\n");
+ goto add_mac_err;
+ }
+
+ err = hinic_port_set_mtu(nic_dev, netdev->mtu);
+ if (err) {
+ dev_err(&netdev->dev, "Failed to set mtu\n");
+ goto set_mtu_err;
+ }
+
+ netdev_features_init(netdev);
+
netif_carrier_off(netdev);
err = register_netdev(netdev);
@@ -90,7 +285,11 @@ static int nic_dev_init(struct pci_dev *pdev)
return 0;
reg_netdev_err:
+set_mtu_err:
+add_mac_err:
pci_set_drvdata(pdev, NULL);
+
+vlan_bitmap_err:
free_netdev(netdev);
alloc_etherdev_err:
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_port.c b/drivers/net/ethernet/huawei/hinic/hinic_port.c
new file mode 100644
index 0000000..5524320
--- /dev/null
+++ b/drivers/net/ethernet/huawei/hinic/hinic_port.c
@@ -0,0 +1,241 @@
+/*
+ * 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_hwif *hwif = hwdev->hwif;
+ struct pci_dev *pdev = hwif->pdev;
+ struct hinic_port_mac_cmd port_mac_cmd;
+ 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_GLOB_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_hwif *hwif = hwdev->hwif;
+ struct pci_dev *pdev = hwif->pdev;
+ struct hinic_port_mac_cmd port_mac_cmd;
+ u16 out_size;
+ int err;
+
+ port_mac_cmd.func_idx = HINIC_HWIF_GLOB_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_hwif *hwif = hwdev->hwif;
+ struct pci_dev *pdev = hwif->pdev;
+ struct hinic_port_mtu_cmd port_mtu_cmd;
+ u16 out_size;
+ int err, max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN;
+
+ if (new_mtu < HINIC_MIN_MTU_SIZE) {
+ netif_err(nic_dev, drv, netdev, "mtu < MIN MTU size");
+ return -EINVAL;
+ }
+
+ 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_GLOB_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_hwif *hwif = hwdev->hwif;
+ struct pci_dev *pdev = hwif->pdev;
+ struct hinic_port_vlan_cmd port_vlan_cmd;
+ int err;
+
+ port_vlan_cmd.func_idx = HINIC_HWIF_GLOB_IDX(hwif);
+ port_vlan_cmd.vlan_id = vlan_id;
+
+ err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_ADD_VLAN,
+ &port_vlan_cmd, sizeof(port_vlan_cmd),
+ NULL, NULL);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to add vlan\n");
+ return err;
+ }
+
+ return 0;
+}
+
+/**
+ * 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_hwif *hwif = hwdev->hwif;
+ struct pci_dev *pdev = hwif->pdev;
+ struct hinic_port_vlan_cmd port_vlan_cmd;
+ int err;
+
+ port_vlan_cmd.func_idx = HINIC_HWIF_GLOB_IDX(hwif);
+ port_vlan_cmd.vlan_id = vlan_id;
+
+ err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_DEL_VLAN,
+ &port_vlan_cmd, sizeof(port_vlan_cmd),
+ NULL, NULL);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to delete vlan\n");
+ return err;
+ }
+
+ return 0;
+}
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_port.h b/drivers/net/ethernet/huawei/hinic/hinic_port.h
new file mode 100644
index 0000000..4cafb94
--- /dev/null
+++ b/drivers/net/ethernet/huawei/hinic/hinic_port.h
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef HINIC_PORT_H
+#define HINIC_PORT_H
+
+#include <linux/types.h>
+#include <linux/etherdevice.h>
+
+#include "hinic_dev.h"
+
+struct hinic_port_mac_cmd {
+ u8 status;
+ u8 version;
+ u8 rsvd0[6];
+
+ u16 func_idx;
+ u16 vlan_id;
+ u16 rsvd1;
+ unsigned char mac[ETH_ALEN];
+};
+
+struct hinic_port_mtu_cmd {
+ u8 status;
+ u8 version;
+ u8 rsvd0[6];
+
+ u16 func_idx;
+ u16 rsvd1;
+ u32 mtu;
+};
+
+struct hinic_port_vlan_cmd {
+ u8 status;
+ u8 version;
+ u8 rsvd0[6];
+
+ u16 func_idx;
+ u16 vlan_id;
+};
+
+int hinic_port_add_mac(struct hinic_dev *nic_dev, const u8 *addr,
+ u16 vlan_id);
+
+int hinic_port_del_mac(struct hinic_dev *nic_dev, const u8 *addr,
+ u16 vlan_id);
+
+int hinic_port_get_mac(struct hinic_dev *nic_dev, u8 *addr);
+
+int hinic_port_set_mtu(struct hinic_dev *nic_dev, int new_mtu);
+
+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);
+
+#endif
--
1.9.1
[toc] | [prev] | [next] | [standalone]
| From | Aviad Krawczyk <aviad.krawczyk@huawei.com> |
|---|---|
| Date | 2017-07-19 11:30 +0200 |
| Subject | [PATCH V2 net-next 16/21] net-next/hinic: Add cmdq commands |
| Message-ID | <u4TsU-6zb-79@gated-at.bofh.it> |
| In reply to | #1691183 |
Add cmdq commands for setting queue pair contexts in the nic.
Signed-off-by: Aviad Krawczyk <aviad.krawczyk@huawei.com>
Signed-off-by: Zhao Chen <zhaochen6@huawei.com>
---
drivers/net/ethernet/huawei/hinic/hinic_common.c | 25 ++
drivers/net/ethernet/huawei/hinic/hinic_common.h | 15 ++
drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c | 284 +++++++++++++++++++++-
drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.h | 153 ++++++++++++
drivers/net/ethernet/huawei/hinic/hinic_hw_io.h | 9 +
drivers/net/ethernet/huawei/hinic/hinic_hw_wq.c | 193 +++++++++++++++
drivers/net/ethernet/huawei/hinic/hinic_hw_wq.h | 8 +
7 files changed, 685 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_common.c b/drivers/net/ethernet/huawei/hinic/hinic_common.c
index 3b439e9..07d264c 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_common.c
+++ b/drivers/net/ethernet/huawei/hinic/hinic_common.c
@@ -13,6 +13,7 @@
*
*/
+#include <linux/kernel.h>
#include <linux/types.h>
#include <asm/byteorder.h>
@@ -53,3 +54,27 @@ void hinic_be32_to_cpu(void *data, int len)
mem++;
}
}
+
+/**
+ * hinic_set_sge - set dma area in scatter gather entry
+ * @sge: scatter gather entry
+ * @addr: dma address
+ * @len: length of relevant data in the dma address
+ **/
+void hinic_set_sge(struct hinic_sge *sge, dma_addr_t addr, int len)
+{
+ sge->hi_addr = upper_32_bits(addr);
+ sge->lo_addr = lower_32_bits(addr);
+ sge->len = len;
+}
+
+/**
+ * hinic_sge_to_dma - get dma address from scatter gather entry
+ * @sge: scatter gather entry
+ *
+ * Return dma address of sg entry
+ **/
+dma_addr_t hinic_sge_to_dma(struct hinic_sge *sge)
+{
+ return (dma_addr_t)((((u64)sge->hi_addr) << 32) | sge->lo_addr);
+}
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_common.h b/drivers/net/ethernet/huawei/hinic/hinic_common.h
index 21921ec..2c06b76 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_common.h
+++ b/drivers/net/ethernet/huawei/hinic/hinic_common.h
@@ -16,8 +16,23 @@
#ifndef HINIC_COMMON_H
#define HINIC_COMMON_H
+#include <linux/types.h>
+
+#define UPPER_8_BITS(data) (((data) >> 8) & 0xFF)
+#define LOWER_8_BITS(data) ((data) & 0xFF)
+
+struct hinic_sge {
+ u32 hi_addr;
+ u32 lo_addr;
+ u32 len;
+};
+
void hinic_cpu_to_be32(void *data, int len);
void hinic_be32_to_cpu(void *data, int len);
+void hinic_set_sge(struct hinic_sge *sge, dma_addr_t addr, int len);
+
+dma_addr_t hinic_sge_to_dma(struct hinic_sge *sge);
+
#endif
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c
index 7099341..1bc51d7 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c
+++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c
@@ -24,8 +24,12 @@
#include <linux/sizes.h>
#include <linux/atomic.h>
#include <linux/log2.h>
+#include <linux/io.h>
+#include <linux/completion.h>
#include <asm/byteorder.h>
+#include <asm/barrier.h>
+#include "hinic_common.h"
#include "hinic_hw_if.h"
#include "hinic_hw_eqs.h"
#include "hinic_hw_mgmt.h"
@@ -34,9 +38,18 @@
#include "hinic_hw_io.h"
#include "hinic_hw_dev.h"
+#define CMDQ_DB_PI_OFF(pi) (((u16)LOWER_8_BITS(pi)) << 3)
+
+#define CMDQ_DB_ADDR(db_base, pi) ((db_base) + CMDQ_DB_PI_OFF(pi))
+
+#define CMDQ_WQE_HEADER(wqe) ((struct hinic_cmdq_header *)(wqe))
+
+#define FIRST_DATA_TO_WRITE_LAST sizeof(u64)
+
#define CMDQ_DB_OFF SZ_2K
#define CMDQ_WQEBB_SIZE 64
+#define CMDQ_WQE_SIZE 64
#define CMDQ_DEPTH SZ_4K
#define CMDQ_WQ_PAGE_SIZE SZ_4K
@@ -44,6 +57,10 @@
#define WQE_LCMD_SIZE 64
#define WQE_SCMD_SIZE 64
+#define COMPLETE_LEN 3
+
+#define CMDQ_TIMEOUT 1000
+
#define CMDQ_PFN(addr, page_size) ((addr) >> (ilog2(page_size)))
#define cmdq_to_cmdqs(cmdq) container_of((cmdq) - (cmdq)->cmdq_type, \
@@ -58,6 +75,40 @@ enum cmdq_wqe_type {
WQE_SCMD_TYPE,
};
+enum completion_format {
+ COMPLETE_DIRECT,
+ COMPLETE_SGE,
+};
+
+enum data_format {
+ DATA_SGE,
+ DATA_DIRECT,
+};
+
+enum bufdesc_len {
+ BUFDESC_LCMD_LEN = 2, /* 16 bytes - 2(8 byte unit) */
+ BUFDESC_SCMD_LEN = 3, /* 24 bytes - 3(8 byte unit) */
+};
+
+enum ctrl_sect_len {
+ CTRL_SECT_LEN = 1, /* 4 bytes (ctrl) - 1(8 byte unit) */
+ CTRL_DIRECT_SECT_LEN = 2, /* 12 bytes (ctrl + rsvd) - 2(8 byte unit) */
+};
+
+enum cmdq_scmd_type {
+ CMDQ_SET_ARM_CMD = 2,
+};
+
+enum cmdq_cmd_type {
+ CMDQ_CMD_SYNC_DIRECT_RESP,
+ CMDQ_CMD_SYNC_SGE_RESP,
+};
+
+enum completion_request {
+ NO_CEQ,
+ CEQ_SET,
+};
+
/**
* hinic_alloc_cmdq_buf - alloc buffer for sending command
* @cmdqs: the cmdqs
@@ -92,6 +143,226 @@ void hinic_free_cmdq_buf(struct hinic_cmdqs *cmdqs,
pci_pool_free(cmdqs->cmdq_buf_pool, cmdq_buf->buf, cmdq_buf->dma_addr);
}
+static void cmdq_set_sge_completion(struct hinic_cmdq_completion *completion,
+ struct hinic_cmdq_buf *buf_out)
+{
+ struct hinic_sge_resp *sge_resp = &completion->sge_resp;
+
+ hinic_set_sge(&sge_resp->sge, buf_out->dma_addr,
+ buf_out->size);
+}
+
+static void cmdq_prepare_wqe_ctrl(struct hinic_cmdq_wqe *wqe, int wrapped,
+ enum hinic_cmd_ack_type ack_type,
+ enum hinic_mod_type mod, u8 cmd, u16 prod_idx,
+ enum completion_format complete_format,
+ enum data_format data_format,
+ enum bufdesc_len buf_len)
+{
+ struct hinic_cmdq_wqe_lcmd *wqe_lcmd;
+ struct hinic_cmdq_wqe_scmd *wqe_scmd;
+ struct hinic_ctrl *ctrl;
+ enum ctrl_sect_len ctrl_len;
+ u32 saved_data;
+
+ if (data_format == DATA_SGE) {
+ wqe_lcmd = &wqe->wqe_lcmd;
+
+ wqe_lcmd->status.status_info = 0;
+ ctrl = &wqe_lcmd->ctrl;
+ ctrl_len = CTRL_SECT_LEN;
+ } else {
+ wqe_scmd = &wqe->direct_wqe.wqe_scmd;
+
+ wqe_scmd->status.status_info = 0;
+ ctrl = &wqe_scmd->ctrl;
+ ctrl_len = CTRL_DIRECT_SECT_LEN;
+ }
+
+ ctrl->ctrl_info = HINIC_CMDQ_CTRL_SET(prod_idx, PI) |
+ HINIC_CMDQ_CTRL_SET(cmd, CMD) |
+ HINIC_CMDQ_CTRL_SET(mod, MOD) |
+ HINIC_CMDQ_CTRL_SET(ack_type, ACK_TYPE);
+
+ CMDQ_WQE_HEADER(wqe)->header_info =
+ HINIC_CMDQ_WQE_HEADER_SET(buf_len, BUFDESC_LEN) |
+ HINIC_CMDQ_WQE_HEADER_SET(complete_format, COMPLETE_FMT) |
+ HINIC_CMDQ_WQE_HEADER_SET(data_format, DATA_FMT) |
+ HINIC_CMDQ_WQE_HEADER_SET(CEQ_SET, COMPLETE_REQ) |
+ HINIC_CMDQ_WQE_HEADER_SET(COMPLETE_LEN, COMPLETE_SECT_LEN) |
+ HINIC_CMDQ_WQE_HEADER_SET(ctrl_len, CTRL_LEN) |
+ HINIC_CMDQ_WQE_HEADER_SET(wrapped, TOGGLED_WRAPPED);
+
+ saved_data = CMDQ_WQE_HEADER(wqe)->saved_data;
+ saved_data = HINIC_SAVED_DATA_CLEAR(saved_data, ARM);
+
+ if ((cmd == CMDQ_SET_ARM_CMD) && (mod == HINIC_MOD_COMM))
+ CMDQ_WQE_HEADER(wqe)->saved_data |=
+ HINIC_SAVED_DATA_SET(1, ARM);
+ else
+ CMDQ_WQE_HEADER(wqe)->saved_data = saved_data;
+}
+
+static void cmdq_set_lcmd_bufdesc(struct hinic_cmdq_wqe_lcmd *wqe_lcmd,
+ struct hinic_cmdq_buf *buf_in)
+{
+ hinic_set_sge(&wqe_lcmd->buf_desc.sge, buf_in->dma_addr, buf_in->size);
+}
+
+static void cmdq_set_lcmd_wqe(struct hinic_cmdq_wqe *wqe,
+ enum cmdq_cmd_type cmd_type,
+ struct hinic_cmdq_buf *buf_in,
+ struct hinic_cmdq_buf *buf_out, int wrapped,
+ enum hinic_cmd_ack_type ack_type,
+ enum hinic_mod_type mod, u8 cmd, u16 prod_idx)
+{
+ struct hinic_cmdq_wqe_lcmd *wqe_lcmd = &wqe->wqe_lcmd;
+ enum completion_format complete_format;
+
+ switch (cmd_type) {
+ case CMDQ_CMD_SYNC_SGE_RESP:
+ complete_format = COMPLETE_SGE;
+ cmdq_set_sge_completion(&wqe_lcmd->completion, buf_out);
+ break;
+ case CMDQ_CMD_SYNC_DIRECT_RESP:
+ complete_format = COMPLETE_DIRECT;
+ wqe_lcmd->completion.direct_resp = 0;
+ break;
+ }
+
+ cmdq_prepare_wqe_ctrl(wqe, wrapped, ack_type, mod, cmd,
+ prod_idx, complete_format, DATA_SGE,
+ BUFDESC_LCMD_LEN);
+
+ cmdq_set_lcmd_bufdesc(wqe_lcmd, buf_in);
+}
+
+static void cmdq_wqe_fill(void *dst, void *src)
+{
+ memcpy(dst + FIRST_DATA_TO_WRITE_LAST, src + FIRST_DATA_TO_WRITE_LAST,
+ CMDQ_WQE_SIZE - FIRST_DATA_TO_WRITE_LAST);
+
+ wmb(); /* The first 8 bytes should be written last */
+
+ *(u64 *)dst = *(u64 *)src;
+}
+
+static void cmdq_fill_db(u32 *db_info,
+ enum hinic_cmdq_type cmdq_type, u16 prod_idx)
+{
+ *db_info = HINIC_CMDQ_DB_INFO_SET(UPPER_8_BITS(prod_idx), HI_PROD_IDX) |
+ HINIC_CMDQ_DB_INFO_SET(HINIC_CTRL_PATH, PATH) |
+ HINIC_CMDQ_DB_INFO_SET(cmdq_type, CMDQ_TYPE) |
+ HINIC_CMDQ_DB_INFO_SET(HINIC_DB_CMDQ_TYPE, DB_TYPE);
+}
+
+static void cmdq_set_db(struct hinic_cmdq *cmdq,
+ enum hinic_cmdq_type cmdq_type, u16 prod_idx)
+{
+ u32 db_info;
+
+ cmdq_fill_db(&db_info, cmdq_type, prod_idx);
+
+ /* The data that is written to HW should be in Big Endian Format */
+ db_info = cpu_to_be32(db_info);
+
+ wmb(); /* write all before the doorbell */
+
+ writel(db_info, CMDQ_DB_ADDR(cmdq->db_base, prod_idx));
+}
+
+static int cmdq_sync_cmd_direct_resp(struct hinic_cmdq *cmdq,
+ enum hinic_mod_type mod, u8 cmd,
+ struct hinic_cmdq_buf *buf_in,
+ u64 *resp)
+{
+ struct hinic_cmdqs *cmdqs = cmdq_to_cmdqs(cmdq);
+ struct hinic_hwif *hwif = cmdqs->hwif;
+ struct pci_dev *pdev = hwif->pdev;
+ struct hinic_wq *wq = cmdq->wq;
+ struct hinic_cmdq_wqe *curr_wqe, wqe;
+ struct hinic_cmdq_wqe_lcmd *wqe_lcmd;
+ struct completion done;
+ u16 curr_prod_idx, next_prod_idx;
+ int errcode, wrapped, num_wqebbs;
+
+ /* Keep doorbell index correct. bh - for tasklet(ceq). */
+ spin_lock_bh(&cmdq->cmdq_lock);
+
+ /* WQE_SIZE = WQEBB_SIZE, we will get the wq element and not shadow*/
+ curr_wqe = hinic_get_wqe(wq, WQE_LCMD_SIZE, &curr_prod_idx);
+ if (!curr_wqe) {
+ spin_unlock_bh(&cmdq->cmdq_lock);
+ return -EBUSY;
+ }
+
+ wrapped = cmdq->wrapped;
+
+ num_wqebbs = ALIGN(WQE_LCMD_SIZE, wq->wqebb_size) / wq->wqebb_size;
+ next_prod_idx = curr_prod_idx + num_wqebbs;
+ if (next_prod_idx >= wq->q_depth) {
+ cmdq->wrapped = !cmdq->wrapped;
+ next_prod_idx -= wq->q_depth;
+ }
+
+ cmdq->errcode[curr_prod_idx] = &errcode;
+
+ init_completion(&done);
+ cmdq->done[curr_prod_idx] = &done;
+
+ cmdq_set_lcmd_wqe(&wqe, CMDQ_CMD_SYNC_DIRECT_RESP, buf_in, NULL,
+ wrapped, HINIC_CMD_ACK_TYPE_CMDQ, mod, cmd,
+ curr_prod_idx);
+
+ /* The data that is written to HW should be in Big Endian Format */
+ hinic_cpu_to_be32(&wqe, WQE_LCMD_SIZE);
+
+ /* CMDQ WQE is not shadow, therefore wqe will be written to wq */
+ cmdq_wqe_fill(curr_wqe, &wqe);
+
+ cmdq_set_db(cmdq, HINIC_CMDQ_SYNC, next_prod_idx);
+
+ spin_unlock_bh(&cmdq->cmdq_lock);
+
+ if (!wait_for_completion_timeout(&done, CMDQ_TIMEOUT)) {
+ spin_lock_bh(&cmdq->cmdq_lock);
+
+ if (cmdq->errcode[curr_prod_idx] == &errcode)
+ cmdq->errcode[curr_prod_idx] = NULL;
+
+ if (cmdq->done[curr_prod_idx] == &done)
+ cmdq->done[curr_prod_idx] = NULL;
+
+ spin_unlock_bh(&cmdq->cmdq_lock);
+
+ dev_err(&pdev->dev, "CMDQ sync command - Timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ smp_rmb(); /* read error code after completion */
+
+ if (resp) {
+ wqe_lcmd = &curr_wqe->wqe_lcmd;
+ *resp = cpu_to_be64(wqe_lcmd->completion.direct_resp);
+ }
+
+ if (errcode != 0) {
+ dev_err(&pdev->dev, "CMDQ sync command failed, errcode = %d\n",
+ errcode);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int cmdq_params_valid(struct hinic_cmdq_buf *buf_in)
+{
+ if (buf_in->size > HINIC_CMDQ_MAX_DATA_SIZE)
+ return -EINVAL;
+
+ return 0;
+}
+
/**
* hinic_cmdq_direct_resp - send command with direct data as resp
* @cmdqs: the cmdqs
@@ -106,8 +377,17 @@ int hinic_cmdq_direct_resp(struct hinic_cmdqs *cmdqs,
enum hinic_mod_type mod, u8 cmd,
struct hinic_cmdq_buf *buf_in, u64 *resp)
{
- /* should be implemented */
- return -EINVAL;
+ struct hinic_hwif *hwif = cmdqs->hwif;
+ struct pci_dev *pdev = hwif->pdev;
+ int err = cmdq_params_valid(buf_in);
+
+ if (err) {
+ dev_err(&pdev->dev, "Invalid CMDQ parameters\n");
+ return err;
+ }
+
+ return cmdq_sync_cmd_direct_resp(&cmdqs->cmdq[HINIC_CMDQ_SYNC],
+ mod, cmd, buf_in, resp);
}
/**
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.h
index 5ec59f1..bbdad66 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.h
+++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.h
@@ -21,6 +21,7 @@
#include <linux/completion.h>
#include <linux/pci.h>
+#include "hinic_common.h"
#include "hinic_hw_if.h"
#include "hinic_hw_wq.h"
@@ -58,20 +59,172 @@
((val) & (~((u64)HINIC_CMDQ_CTXT_##member##_MASK \
<< HINIC_CMDQ_CTXT_##member##_SHIFT)))
+#define HINIC_CMDQ_CTRL_PI_SHIFT 0
+#define HINIC_CMDQ_CTRL_CMD_SHIFT 16
+#define HINIC_CMDQ_CTRL_MOD_SHIFT 24
+#define HINIC_CMDQ_CTRL_ACK_TYPE_SHIFT 29
+#define HINIC_CMDQ_CTRL_HW_BUSY_BIT_SHIFT 31
+
+#define HINIC_CMDQ_CTRL_PI_MASK 0xFFFF
+#define HINIC_CMDQ_CTRL_CMD_MASK 0xFF
+#define HINIC_CMDQ_CTRL_MOD_MASK 0x1F
+#define HINIC_CMDQ_CTRL_ACK_TYPE_MASK 0x3
+#define HINIC_CMDQ_CTRL_HW_BUSY_BIT_MASK 0x1
+
+#define HINIC_CMDQ_CTRL_SET(val, member) \
+ (((u32)(val) & HINIC_CMDQ_CTRL_##member##_MASK) \
+ << HINIC_CMDQ_CTRL_##member##_SHIFT)
+
+#define HINIC_CMDQ_CTRL_GET(val, member) \
+ (((val) >> HINIC_CMDQ_CTRL_##member##_SHIFT) \
+ & HINIC_CMDQ_CTRL_##member##_MASK)
+
+#define HINIC_CMDQ_WQE_HEADER_BUFDESC_LEN_SHIFT 0
+#define HINIC_CMDQ_WQE_HEADER_COMPLETE_FMT_SHIFT 15
+#define HINIC_CMDQ_WQE_HEADER_DATA_FMT_SHIFT 22
+#define HINIC_CMDQ_WQE_HEADER_COMPLETE_REQ_SHIFT 23
+#define HINIC_CMDQ_WQE_HEADER_COMPLETE_SECT_LEN_SHIFT 27
+#define HINIC_CMDQ_WQE_HEADER_CTRL_LEN_SHIFT 29
+#define HINIC_CMDQ_WQE_HEADER_TOGGLED_WRAPPED_SHIFT 31
+
+#define HINIC_CMDQ_WQE_HEADER_BUFDESC_LEN_MASK 0xFF
+#define HINIC_CMDQ_WQE_HEADER_COMPLETE_FMT_MASK 0x1
+#define HINIC_CMDQ_WQE_HEADER_DATA_FMT_MASK 0x1
+#define HINIC_CMDQ_WQE_HEADER_COMPLETE_REQ_MASK 0x1
+#define HINIC_CMDQ_WQE_HEADER_COMPLETE_SECT_LEN_MASK 0x3
+#define HINIC_CMDQ_WQE_HEADER_CTRL_LEN_MASK 0x3
+#define HINIC_CMDQ_WQE_HEADER_TOGGLED_WRAPPED_MASK 0x1
+
+#define HINIC_CMDQ_WQE_HEADER_SET(val, member) \
+ (((u32)(val) & HINIC_CMDQ_WQE_HEADER_##member##_MASK) \
+ << HINIC_CMDQ_WQE_HEADER_##member##_SHIFT)
+
+#define HINIC_CMDQ_WQE_HEADER_GET(val, member) \
+ (((val) >> HINIC_CMDQ_WQE_HEADER_##member##_SHIFT) \
+ & HINIC_CMDQ_WQE_HEADER_##member##_MASK)
+
+#define HINIC_SAVED_DATA_ARM_SHIFT 31
+
+#define HINIC_SAVED_DATA_ARM_MASK 0x1
+
+#define HINIC_SAVED_DATA_SET(val, member) \
+ (((u32)(val) & HINIC_SAVED_DATA_##member##_MASK) \
+ << HINIC_SAVED_DATA_##member##_SHIFT)
+
+#define HINIC_SAVED_DATA_GET(val, member) \
+ (((val) >> HINIC_SAVED_DATA_##member##_SHIFT) \
+ & HINIC_SAVED_DATA_##member##_MASK)
+
+#define HINIC_SAVED_DATA_CLEAR(val, member) \
+ ((val) & (~(HINIC_SAVED_DATA_##member##_MASK \
+ << HINIC_SAVED_DATA_##member##_SHIFT)))
+
+#define HINIC_CMDQ_DB_INFO_HI_PROD_IDX_SHIFT 0
+#define HINIC_CMDQ_DB_INFO_PATH_SHIFT 23
+#define HINIC_CMDQ_DB_INFO_CMDQ_TYPE_SHIFT 24
+#define HINIC_CMDQ_DB_INFO_DB_TYPE_SHIFT 27
+
+#define HINIC_CMDQ_DB_INFO_HI_PROD_IDX_MASK 0xFF
+#define HINIC_CMDQ_DB_INFO_PATH_MASK 0x1
+#define HINIC_CMDQ_DB_INFO_CMDQ_TYPE_MASK 0x7
+#define HINIC_CMDQ_DB_INFO_DB_TYPE_MASK 0x1F
+
+#define HINIC_CMDQ_DB_INFO_SET(val, member) \
+ (((u32)(val) & HINIC_CMDQ_DB_INFO_##member##_MASK) \
+ << HINIC_CMDQ_DB_INFO_##member##_SHIFT)
+
#define HINIC_CMDQ_BUF_SIZE 2048
+#define HINIC_CMDQ_BUF_HW_RSVD 8
+#define HINIC_CMDQ_MAX_DATA_SIZE (HINIC_CMDQ_BUF_SIZE - \
+ HINIC_CMDQ_BUF_HW_RSVD)
+
+#define HINIC_SCMD_DATA_LEN 16
+
enum hinic_cmdq_type {
HINIC_CMDQ_SYNC,
HINIC_MAX_CMDQ_TYPES,
};
+enum hinic_cmd_ack_type {
+ HINIC_CMD_ACK_TYPE_CMDQ,
+};
+
struct hinic_cmdq_buf {
void *buf;
dma_addr_t dma_addr;
size_t size;
};
+struct hinic_cmdq_header {
+ u32 header_info;
+ u32 saved_data;
+};
+
+struct hinic_status {
+ u32 status_info;
+};
+
+struct hinic_ctrl {
+ u32 ctrl_info;
+};
+
+struct hinic_sge_resp {
+ struct hinic_sge sge;
+ u32 rsvd;
+};
+
+struct hinic_cmdq_completion {
+ /* HW Format */
+ union {
+ struct hinic_sge_resp sge_resp;
+ u64 direct_resp;
+ };
+};
+
+struct hinic_scmd_bufdesc {
+ u32 buf_len;
+ u32 rsvd;
+ u8 data[HINIC_SCMD_DATA_LEN];
+};
+
+struct hinic_lcmd_bufdesc {
+ struct hinic_sge sge;
+ u32 rsvd1;
+ u64 rsvd2;
+ u64 rsvd3;
+};
+
+struct hinic_cmdq_wqe_scmd {
+ struct hinic_cmdq_header header;
+ u64 rsvd;
+ struct hinic_status status;
+ struct hinic_ctrl ctrl;
+ struct hinic_cmdq_completion completion;
+ struct hinic_scmd_bufdesc buf_desc;
+};
+
+struct hinic_cmdq_wqe_lcmd {
+ struct hinic_cmdq_header header;
+ struct hinic_status status;
+ struct hinic_ctrl ctrl;
+ struct hinic_cmdq_completion completion;
+ struct hinic_lcmd_bufdesc buf_desc;
+};
+
+struct hinic_cmdq_direct_wqe {
+ struct hinic_cmdq_wqe_scmd wqe_scmd;
+};
+
+struct hinic_cmdq_wqe {
+ /* HW Format */
+ union {
+ struct hinic_cmdq_direct_wqe direct_wqe;
+ struct hinic_cmdq_wqe_lcmd wqe_lcmd;
+ };
+};
+
struct hinic_cmdq_ctxt_info {
u64 curr_wqe_page_pfn;
u64 wq_block_pfn;
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_io.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_io.h
index cfc21ba..6f40e6a 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_hw_io.h
+++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_io.h
@@ -32,6 +32,15 @@
#define HINIC_DB_MAX_AREAS (HINIC_DB_SIZE / HINIC_DB_PAGE_SIZE)
+enum hinic_db_type {
+ HINIC_DB_CMDQ_TYPE,
+ HINIC_DB_SQ_TYPE,
+};
+
+enum hinic_io_path {
+ HINIC_CTRL_PATH,
+};
+
struct hinic_free_db_area {
int db_idx[HINIC_DB_MAX_AREAS];
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.c
index 1e12946..887d729 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.c
+++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.c
@@ -73,6 +73,25 @@
(((void *)((cmdq_pages)->shadow_page_vaddr)) \
+ (wq)->block_idx * CMDQ_BLOCK_SIZE)
+#define WQE_PAGE_OFF(wq, idx) (((idx) & ((wq)->num_wqebbs_per_page - 1)) * \
+ (wq)->wqebb_size)
+
+#define WQE_PAGE_NUM(wq, idx) (((idx) / ((wq)->num_wqebbs_per_page)) \
+ & ((wq)->num_q_pages - 1))
+
+#define WQ_PAGE_ADDR(wq, idx) \
+ ((wq)->shadow_block_vaddr[WQE_PAGE_NUM(wq, idx)])
+
+#define MASKED_WQE_IDX(wq, idx) ((idx) & (wq)->mask)
+
+#define WQE_IN_RANGE(wqe, start, end) \
+ (((unsigned long)(wqe) >= (unsigned long)(start)) && \
+ ((unsigned long)(wqe) < (unsigned long)(end)))
+
+#define WQE_SHADOW_PAGE(wq, wqe) \
+ (((unsigned long)(wqe) - (unsigned long)(wq)->shadow_wqe) \
+ / (wq)->max_wqe_size)
+
/**
* queue_alloc_page - allocate page for Queue
* @hwif: HW interface for allocating DMA
@@ -678,3 +697,177 @@ void hinic_wqs_cmdq_free(struct hinic_cmdq_pages *cmdq_pages,
cmdq_free_page(cmdq_pages);
}
+
+static void copy_wqe_to_shadow(struct hinic_wq *wq, void *shadow_addr,
+ int num_wqebbs, u16 idx)
+{
+ void *wqe_page_addr, *wqebb_addr;
+ int i;
+
+ for (i = 0; i < num_wqebbs; i++, idx++) {
+ idx = MASKED_WQE_IDX(wq, idx);
+ wqe_page_addr = WQ_PAGE_ADDR(wq, idx);
+ wqebb_addr = wqe_page_addr +
+ WQE_PAGE_OFF(wq, idx);
+
+ memcpy(shadow_addr, wqebb_addr, wq->wqebb_size);
+
+ shadow_addr += wq->wqebb_size;
+ }
+}
+
+static void copy_wqe_from_shadow(struct hinic_wq *wq, void *shadow_addr,
+ int num_wqebbs, u16 idx)
+{
+ void *wqe_page_addr, *wqebb_addr;
+ int i;
+
+ for (i = 0; i < num_wqebbs; i++, idx++) {
+ idx = MASKED_WQE_IDX(wq, idx);
+ wqe_page_addr = WQ_PAGE_ADDR(wq, idx);
+ wqebb_addr = wqe_page_addr +
+ WQE_PAGE_OFF(wq, idx);
+
+ memcpy(wqebb_addr, shadow_addr, wq->wqebb_size);
+ shadow_addr += wq->wqebb_size;
+ }
+}
+
+/**
+ * hinic_get_wqe - get wqe ptr in the current pi and update the pi
+ * @wq: wq to get wqe from
+ * @wqe_size: wqe size
+ * @prod_idx: returned pi
+ *
+ * Return wqe pointer
+ **/
+void *hinic_get_wqe(struct hinic_wq *wq, unsigned int wqe_size, u16 *prod_idx)
+{
+ int curr_pg, end_pg, num_wqebbs;
+ u16 curr_prod_idx, end_prod_idx;
+
+ *prod_idx = MASKED_WQE_IDX(wq, atomic_read(&wq->prod_idx));
+
+ num_wqebbs = ALIGN(wqe_size, wq->wqebb_size) / wq->wqebb_size;
+
+ if (atomic_sub_return(num_wqebbs, &wq->delta) <= 0) {
+ atomic_add(num_wqebbs, &wq->delta);
+ return NULL;
+ }
+
+ end_prod_idx = atomic_add_return(num_wqebbs, &wq->prod_idx);
+
+ end_prod_idx = MASKED_WQE_IDX(wq, end_prod_idx);
+ curr_prod_idx = end_prod_idx - num_wqebbs;
+ curr_prod_idx = MASKED_WQE_IDX(wq, curr_prod_idx);
+
+ /* end prod index points to the next wqebb, therefore minus 1 */
+ end_prod_idx = MASKED_WQE_IDX(wq, end_prod_idx - 1);
+
+ curr_pg = WQE_PAGE_NUM(wq, curr_prod_idx);
+ end_pg = WQE_PAGE_NUM(wq, end_prod_idx);
+
+ *prod_idx = curr_prod_idx;
+
+ if (curr_pg != end_pg) {
+ void *shadow_addr = &wq->shadow_wqe[curr_pg * wq->max_wqe_size];
+
+ copy_wqe_to_shadow(wq, shadow_addr, num_wqebbs, *prod_idx);
+
+ wq->shadow_idx[curr_pg] = *prod_idx;
+ return shadow_addr;
+ }
+
+ return WQ_PAGE_ADDR(wq, *prod_idx) + WQE_PAGE_OFF(wq, *prod_idx);
+}
+
+/**
+ * hinic_put_wqe - return the wqe place to use for a new wqe
+ * @wq: wq to return wqe
+ * @wqe_size: wqe size
+ **/
+void hinic_put_wqe(struct hinic_wq *wq, unsigned int wqe_size)
+{
+ int num_wqebbs = ALIGN(wqe_size, wq->wqebb_size) / wq->wqebb_size;
+
+ atomic_add(num_wqebbs, &wq->cons_idx);
+
+ atomic_add(num_wqebbs, &wq->delta);
+}
+
+/**
+ * hinic_read_wqe - read wqe ptr in the current ci
+ * @wq: wq to get read from
+ * @wqe_size: wqe size
+ * @cons_idx: returned ci
+ *
+ * Return wqe pointer
+ **/
+void *hinic_read_wqe(struct hinic_wq *wq, unsigned int wqe_size, u16 *cons_idx)
+{
+ int num_wqebbs = ALIGN(wqe_size, wq->wqebb_size) / wq->wqebb_size;
+ int curr_pg, end_pg;
+ u16 curr_cons_idx, end_cons_idx;
+
+ if ((atomic_read(&wq->delta) + num_wqebbs) > wq->q_depth)
+ return NULL;
+
+ curr_cons_idx = atomic_read(&wq->cons_idx);
+
+ curr_cons_idx = MASKED_WQE_IDX(wq, curr_cons_idx);
+ end_cons_idx = MASKED_WQE_IDX(wq, curr_cons_idx + num_wqebbs - 1);
+
+ curr_pg = WQE_PAGE_NUM(wq, curr_cons_idx);
+ end_pg = WQE_PAGE_NUM(wq, end_cons_idx);
+
+ *cons_idx = curr_cons_idx;
+
+ if (curr_pg != end_pg) {
+ void *shadow_addr = &wq->shadow_wqe[curr_pg * wq->max_wqe_size];
+
+ copy_wqe_to_shadow(wq, shadow_addr, num_wqebbs, *cons_idx);
+ return shadow_addr;
+ }
+
+ return WQ_PAGE_ADDR(wq, *cons_idx) + WQE_PAGE_OFF(wq, *cons_idx);
+}
+
+/**
+ * wqe_shadow - check if a wqe is shadow
+ * @wq: wq of the wqe
+ * @wqe: the wqe for shadow checking
+ *
+ * Return true - shadow, false - Not shadow
+ **/
+static inline bool wqe_shadow(struct hinic_wq *wq, void *wqe)
+{
+ void *end_wqe_shadow_addr;
+ size_t wqe_shadow_size = wq->num_q_pages * wq->max_wqe_size;
+
+ end_wqe_shadow_addr = &wq->shadow_wqe[wqe_shadow_size];
+
+ return WQE_IN_RANGE(wqe, wq->shadow_wqe, end_wqe_shadow_addr);
+}
+
+/**
+ * hinic_write_wqe - write the wqe to the wq
+ * @wq: wq to write wqe to
+ * @wqe: wqe to write
+ * @wqe_size: wqe size
+ **/
+void hinic_write_wqe(struct hinic_wq *wq, void *wqe, unsigned int wqe_size)
+{
+ void *shadow_addr;
+ int curr_pg, num_wqebbs;
+ u16 prod_idx;
+
+ if (wqe_shadow(wq, wqe)) {
+ curr_pg = WQE_SHADOW_PAGE(wq, wqe);
+
+ prod_idx = wq->shadow_idx[curr_pg];
+ num_wqebbs = ALIGN(wqe_size, wq->wqebb_size) / wq->wqebb_size;
+ shadow_addr = &wq->shadow_wqe[curr_pg * wq->max_wqe_size];
+
+ copy_wqe_from_shadow(wq, shadow_addr, num_wqebbs, prod_idx);
+ }
+}
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.h
index a3c4469..da66848 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.h
+++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.h
@@ -100,4 +100,12 @@ int hinic_wq_allocate(struct hinic_wqs *wqs, struct hinic_wq *wq,
void hinic_wq_free(struct hinic_wqs *wqs, struct hinic_wq *wq);
+void *hinic_get_wqe(struct hinic_wq *wq, unsigned int wqe_size, u16 *prod_idx);
+
+void hinic_put_wqe(struct hinic_wq *wq, unsigned int wqe_size);
+
+void *hinic_read_wqe(struct hinic_wq *wq, unsigned int wqe_size, u16 *cons_idx);
+
+void hinic_write_wqe(struct hinic_wq *wq, void *wqe, unsigned int wqe_size);
+
#endif
--
1.9.1
[toc] | [prev] | [next] | [standalone]
| From | Aviad Krawczyk <aviad.krawczyk@huawei.com> |
|---|---|
| Date | 2017-07-19 11:40 +0200 |
| Subject | [PATCH V2 net-next 07/21] net-next/hinic: Add aeqs |
| Message-ID | <u4TCz-6D1-33@gated-at.bofh.it> |
| In reply to | #1691183 |
Handle aeq elements that are accumulated on the aeq by calling the
registered handler for the specific event.
Signed-off-by: Aviad Krawczyk <aviad.krawczyk@huawei.com>
Signed-off-by: Zhao Chen <zhaochen6@huawei.com>
---
drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h | 49 +++
drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.c | 454 ++++++++++++++++++++++-
drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.h | 81 ++++
drivers/net/ethernet/huawei/hinic/hinic_hw_if.c | 92 +++++
drivers/net/ethernet/huawei/hinic/hinic_hw_if.h | 58 ++-
5 files changed, 726 insertions(+), 8 deletions(-)
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h
index ebbf054..52eb89c 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h
+++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h
@@ -65,4 +65,53 @@
#define HINIC_CSR_API_CMD_STATUS_ADDR(idx) \
(HINIC_CSR_API_CMD_BASE + 0x30 + (idx) * HINIC_CSR_API_CMD_STRIDE)
+/* MSI-X registers */
+#define HINIC_CSR_MSIX_CTRL_BASE 0x2000
+#define HINIC_CSR_MSIX_CNT_BASE 0x2004
+
+#define HINIC_CSR_MSIX_STRIDE 0x8
+
+#define HINIC_CSR_MSIX_CTRL_ADDR(idx) \
+ (HINIC_CSR_MSIX_CTRL_BASE + (idx) * HINIC_CSR_MSIX_STRIDE)
+
+#define HINIC_CSR_MSIX_CNT_ADDR(idx) \
+ (HINIC_CSR_MSIX_CNT_BASE + (idx) * HINIC_CSR_MSIX_STRIDE)
+
+/* EQ registers */
+#define HINIC_AEQ_MTT_OFF_BASE_ADDR 0x200
+
+#define HINIC_EQ_MTT_OFF_STRIDE 0x40
+
+#define HINIC_CSR_AEQ_MTT_OFF(id) \
+ (HINIC_AEQ_MTT_OFF_BASE_ADDR + (id) * HINIC_EQ_MTT_OFF_STRIDE)
+
+#define HINIC_CSR_EQ_PAGE_OFF_STRIDE 8
+
+#define HINIC_CSR_AEQ_HI_PHYS_ADDR_REG(q_id, pg_num) \
+ (HINIC_CSR_AEQ_MTT_OFF(q_id) + \
+ (pg_num) * HINIC_CSR_EQ_PAGE_OFF_STRIDE)
+
+#define HINIC_CSR_AEQ_LO_PHYS_ADDR_REG(q_id, pg_num) \
+ (HINIC_CSR_AEQ_MTT_OFF(q_id) + \
+ (pg_num) * HINIC_CSR_EQ_PAGE_OFF_STRIDE + 4)
+
+#define HINIC_AEQ_CTRL_0_ADDR_BASE 0xE00
+#define HINIC_AEQ_CTRL_1_ADDR_BASE 0xE04
+#define HINIC_AEQ_CONS_IDX_ADDR_BASE 0xE08
+#define HINIC_AEQ_PROD_IDX_ADDR_BASE 0xE0C
+
+#define HINIC_EQ_OFF_STRIDE 0x80
+
+#define HINIC_CSR_AEQ_CTRL_0_ADDR(idx) \
+ (HINIC_AEQ_CTRL_0_ADDR_BASE + (idx) * HINIC_EQ_OFF_STRIDE)
+
+#define HINIC_CSR_AEQ_CTRL_1_ADDR(idx) \
+ (HINIC_AEQ_CTRL_1_ADDR_BASE + (idx) * HINIC_EQ_OFF_STRIDE)
+
+#define HINIC_CSR_AEQ_CONS_IDX_ADDR(idx) \
+ (HINIC_AEQ_CONS_IDX_ADDR_BASE + (idx) * HINIC_EQ_OFF_STRIDE)
+
+#define HINIC_CSR_AEQ_PROD_IDX_ADDR(idx) \
+ (HINIC_AEQ_PROD_IDX_ADDR_BASE + (idx) * HINIC_EQ_OFF_STRIDE)
+
#endif
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.c
index 09d881b..afeab36 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.c
+++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.c
@@ -13,17 +13,74 @@
*
*/
+#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/pci.h>
#include <linux/device.h>
#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/log2.h>
+#include <asm/byteorder.h>
+#include <asm/barrier.h>
+#include "hinic_hw_csr.h"
#include "hinic_hw_if.h"
#include "hinic_hw_eqs.h"
#define HINIC_EQS_WQ_NAME "hinic_eqs"
+#define GET_EQ_NUM_PAGES(eq, pg_size) \
+ (ALIGN((eq)->q_len * (eq)->elem_size, pg_size) / (pg_size))
+
+#define GET_EQ_NUM_ELEMS_IN_PG(eq, pg_size) ((pg_size) / (eq)->elem_size)
+
+#define EQ_CONS_IDX_REG_ADDR(eq) HINIC_CSR_AEQ_CONS_IDX_ADDR((eq)->q_id)
+#define EQ_PROD_IDX_REG_ADDR(eq) HINIC_CSR_AEQ_PROD_IDX_ADDR((eq)->q_id)
+
+#define EQ_HI_PHYS_ADDR_REG(eq, pg_num) \
+ HINIC_CSR_AEQ_HI_PHYS_ADDR_REG((eq)->q_id, pg_num)
+
+#define EQ_LO_PHYS_ADDR_REG(eq, pg_num) \
+ HINIC_CSR_AEQ_LO_PHYS_ADDR_REG((eq)->q_id, pg_num)
+
+#define GET_EQ_ELEMENT(eq, idx) \
+ ((eq)->virt_addr[(idx) / (eq)->num_elem_in_pg] + \
+ (((idx) & ((eq)->num_elem_in_pg - 1)) * (eq)->elem_size))
+
+#define GET_AEQ_ELEM(eq, idx) ((struct hinic_aeq_elem *) \
+ GET_EQ_ELEMENT(eq, idx))
+
+#define GET_CURR_AEQ_ELEM(eq) GET_AEQ_ELEM(eq, (eq)->cons_idx)
+
+#define PAGE_IN_4K(page_size) ((page_size) >> 12)
+#define EQ_SET_HW_PAGE_SIZE_VAL(eq) (ilog2(PAGE_IN_4K((eq)->page_size)))
+
+#define ELEMENT_SIZE_IN_32B(eq) (((eq)->elem_size) >> 5)
+#define EQ_SET_HW_ELEM_SIZE_VAL(eq) (ilog2(ELEMENT_SIZE_IN_32B(eq)))
+
+#define EQ_MAX_PAGES 8
+
+#define aeq_to_aeqs(eq) \
+ container_of((eq) - (eq)->q_id, struct hinic_aeqs, aeq[0])
+
+#define work_to_aeq_work(work) \
+ container_of(work, struct hinic_eq_work, work)
+
+#define DMA_ATTR_AEQ_DEFAULT 0
+
+enum eq_int_mode {
+ EQ_INT_MODE_ARMED,
+ EQ_INT_MODE_ALWAYS
+};
+
+enum eq_arm_state {
+ EQ_NOT_ARMED,
+ EQ_ARMED
+};
+
/**
* hinic_aeq_register_hw_cb - register AEQ callback for specific event
* @aeqs: pointer to Async eqs of the chip
@@ -61,6 +118,316 @@ void hinic_aeq_unregister_hw_cb(struct hinic_aeqs *aeqs,
hwe_cb->hwe_handler = NULL;
}
+static u8 eq_cons_idx_checksum_set(u32 val)
+{
+ u8 checksum = 0;
+ int idx;
+
+ for (idx = 0; idx < 32; idx += 4)
+ checksum ^= ((val >> idx) & 0xF);
+
+ return (checksum & 0xF);
+}
+
+/**
+ * eq_update_ci - update the HW cons idx of event queue
+ * @eq: the event queue to update the cons idx for
+ **/
+static void eq_update_ci(struct hinic_eq *eq)
+{
+ u32 eq_cons_idx, val, addr = EQ_CONS_IDX_REG_ADDR(eq);
+
+ /* Read Modify Write */
+ val = hinic_hwif_read_reg(eq->hwif, addr);
+
+ val = HINIC_EQ_CI_CLEAR(val, IDX) &
+ HINIC_EQ_CI_CLEAR(val, WRAPPED) &
+ HINIC_EQ_CI_CLEAR(val, INT_ARMED) &
+ HINIC_EQ_CI_CLEAR(val, XOR_CHKSUM);
+
+ eq_cons_idx = HINIC_EQ_CI_SET(eq->cons_idx, IDX) |
+ HINIC_EQ_CI_SET(eq->wrapped, WRAPPED) |
+ HINIC_EQ_CI_SET(EQ_ARMED, INT_ARMED);
+
+ val |= eq_cons_idx;
+
+ val |= HINIC_EQ_CI_SET(eq_cons_idx_checksum_set(val), XOR_CHKSUM);
+
+ hinic_hwif_write_reg(eq->hwif, addr, val);
+}
+
+/**
+ * aeq_irq_handler - handler for the AEQ event
+ * @eq: the Async Event Queue that received the event
+ **/
+static void aeq_irq_handler(struct hinic_eq *eq)
+{
+ struct hinic_aeqs *aeqs = aeq_to_aeqs(eq);
+ struct hinic_hwif *hwif = aeqs->hwif;
+ struct pci_dev *pdev = hwif->pdev;
+ struct hinic_aeq_elem *aeqe_curr;
+ struct hinic_hw_event_cb *hwe_cb;
+ unsigned long eqe_state;
+ enum hinic_aeq_type event;
+ u32 aeqe_desc;
+ int i, size;
+
+ for (i = 0; i < eq->q_len; i++) {
+ aeqe_curr = GET_CURR_AEQ_ELEM(eq);
+
+ /* Data in HW is in Big endian Format */
+ aeqe_desc = be32_to_cpu(aeqe_curr->desc);
+
+ /* HW toggles the wrapped bit, when it adds eq element */
+ if (HINIC_EQ_ELEM_DESC_GET(aeqe_desc, WRAPPED) == eq->wrapped)
+ break;
+
+ event = HINIC_EQ_ELEM_DESC_GET(aeqe_desc, TYPE);
+
+ if (event >= HINIC_MAX_AEQ_EVENTS) {
+ dev_err(&pdev->dev, "Unknown AEQ Event %d\n", event);
+ return;
+ }
+
+ if (!HINIC_EQ_ELEM_DESC_GET(aeqe_desc, SRC)) {
+ hwe_cb = &aeqs->hwe_cb[event];
+
+ size = HINIC_EQ_ELEM_DESC_GET(aeqe_desc, SIZE);
+
+ eqe_state = cmpxchg(&hwe_cb->hwe_state,
+ HINIC_EQE_ENABLED,
+ HINIC_EQE_ENABLED |
+ HINIC_EQE_RUNNING);
+
+ if ((eqe_state == HINIC_EQE_ENABLED) &&
+ (hwe_cb->hwe_handler))
+ hwe_cb->hwe_handler(hwe_cb->handle,
+ aeqe_curr->data, size);
+ else
+ dev_err(&pdev->dev, "Unhandled AEQ Event %d\n",
+ event);
+
+ hwe_cb->hwe_state &= ~HINIC_EQE_RUNNING;
+ }
+
+ eq->cons_idx++;
+
+ if (eq->cons_idx == eq->q_len) {
+ eq->cons_idx = 0;
+ eq->wrapped = !eq->wrapped;
+ }
+ }
+}
+
+/**
+ * eq_irq_handler - handler for the EQ event
+ * @data: the Event Queue that received the event
+ **/
+static void eq_irq_handler(void *data)
+{
+ struct hinic_eq *eq = (struct hinic_eq *)data;
+
+ if (eq->type == HINIC_AEQ)
+ aeq_irq_handler(eq);
+
+ eq_update_ci(eq);
+}
+
+/**
+ * eq_irq_work - the work of the EQ that received the event
+ * @work: the work struct that is associated with the EQ
+ **/
+static void eq_irq_work(struct work_struct *work)
+{
+ struct hinic_eq_work *aeq_work = work_to_aeq_work(work);
+ struct hinic_eq *aeq = (struct hinic_eq *)aeq_work->data;
+
+ eq_irq_handler(aeq);
+}
+
+/**
+ * aeq_interrupt - aeq interrupt handler
+ * @irq: irq number
+ * @data: the Async Event Queue that collected the event
+ **/
+static irqreturn_t aeq_interrupt(int irq, void *data)
+{
+ struct hinic_eq *aeq = (struct hinic_eq *)data;
+ struct hinic_hwif *hwif = aeq->hwif;
+ struct hinic_aeqs *aeqs = aeq_to_aeqs(aeq);
+ struct workqueue_struct *workq = aeqs->workq;
+ struct hinic_eq_work *aeq_work;
+
+ /* clear resend timer cnt register */
+ hinic_msix_attr_cnt_clear(hwif, aeq->msix_entry.entry);
+
+ aeq_work = &aeq->aeq_work;
+ aeq_work->data = aeq;
+
+ queue_work(workq, &aeq_work->work);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * set_eq_ctrls - setting eq's ctrl registers
+ * @eq: the Event Queue for setting
+ **/
+static void set_eq_ctrls(struct hinic_eq *eq)
+{
+ struct hinic_hwif *hwif = eq->hwif;
+ struct msix_entry *msix_entry = &eq->msix_entry;
+ enum hinic_eq_type type = eq->type;
+ u8 pci_intf_idx = HINIC_HWIF_PCI_INTF(hwif);
+ u32 addr, val, ctrl0, ctrl1, page_size_val, elem_size;
+
+ if (type == HINIC_AEQ) {
+ /* RMW Ctrl0 */
+ addr = HINIC_CSR_AEQ_CTRL_0_ADDR(eq->q_id);
+
+ val = hinic_hwif_read_reg(hwif, addr);
+
+ val = HINIC_AEQ_CTRL_0_CLEAR(val, INT_IDX) &
+ HINIC_AEQ_CTRL_0_CLEAR(val, DMA_ATTR) &
+ HINIC_AEQ_CTRL_0_CLEAR(val, PCI_INTF_IDX) &
+ HINIC_AEQ_CTRL_0_CLEAR(val, INT_MODE);
+
+ ctrl0 = HINIC_AEQ_CTRL_0_SET(msix_entry->entry, INT_IDX) |
+ HINIC_AEQ_CTRL_0_SET(DMA_ATTR_AEQ_DEFAULT, DMA_ATTR) |
+ HINIC_AEQ_CTRL_0_SET(pci_intf_idx, PCI_INTF_IDX) |
+ HINIC_AEQ_CTRL_0_SET(EQ_INT_MODE_ARMED, INT_MODE);
+
+ val |= ctrl0;
+
+ hinic_hwif_write_reg(hwif, addr, val);
+
+ /* RMW Ctrl1 */
+ addr = HINIC_CSR_AEQ_CTRL_1_ADDR(eq->q_id);
+
+ page_size_val = EQ_SET_HW_PAGE_SIZE_VAL(eq);
+ elem_size = EQ_SET_HW_ELEM_SIZE_VAL(eq);
+
+ val = hinic_hwif_read_reg(hwif, addr);
+
+ val = HINIC_AEQ_CTRL_1_CLEAR(val, LEN) &
+ HINIC_AEQ_CTRL_1_CLEAR(val, ELEM_SIZE) &
+ HINIC_AEQ_CTRL_1_CLEAR(val, PAGE_SIZE);
+
+ ctrl1 = HINIC_AEQ_CTRL_1_SET(eq->q_len, LEN) |
+ HINIC_AEQ_CTRL_1_SET(elem_size, ELEM_SIZE) |
+ HINIC_AEQ_CTRL_1_SET(page_size_val, PAGE_SIZE);
+
+ val |= ctrl1;
+
+ hinic_hwif_write_reg(hwif, addr, val);
+ }
+}
+
+/**
+ * aeq_elements_init - initialize all the elements in the aeq
+ * @eq: the Async Event Queue
+ * @init_val: value to initialize the elements with it
+ **/
+static void aeq_elements_init(struct hinic_eq *eq, u32 init_val)
+{
+ struct hinic_aeq_elem *aeqe;
+ int i;
+
+ for (i = 0; i < eq->q_len; i++) {
+ aeqe = GET_AEQ_ELEM(eq, i);
+ aeqe->desc = cpu_to_be32(init_val);
+ }
+
+ wmb(); /* Write the initilzation values */
+}
+
+/**
+ * alloc_eq_pages - allocate the pages for the queue
+ * @eq: the event queue
+ *
+ * Return 0 - Success, Negative - Failure
+ **/
+static int alloc_eq_pages(struct hinic_eq *eq)
+{
+ struct hinic_hwif *hwif = eq->hwif;
+ struct pci_dev *pdev = hwif->pdev;
+ u32 init_val, addr, val;
+ size_t dma_addr_size, virt_addr_size;
+ int err, pg;
+
+ dma_addr_size = eq->num_pages * sizeof(*eq->dma_addr);
+ virt_addr_size = eq->num_pages * sizeof(*eq->virt_addr);
+
+ eq->dma_addr = devm_kzalloc(&pdev->dev, dma_addr_size, GFP_KERNEL);
+ if (!eq->dma_addr)
+ return -ENOMEM;
+
+ eq->virt_addr = devm_kzalloc(&pdev->dev, virt_addr_size, GFP_KERNEL);
+ if (!eq->virt_addr) {
+ err = -ENOMEM;
+ goto virt_addr_alloc_err;
+ }
+
+ for (pg = 0; pg < eq->num_pages; pg++) {
+ eq->virt_addr[pg] = dma_zalloc_coherent(&pdev->dev,
+ eq->page_size,
+ &eq->dma_addr[pg],
+ GFP_KERNEL);
+ if (!eq->virt_addr[pg]) {
+ err = -ENOMEM;
+ goto dma_alloc_err;
+ }
+
+ addr = EQ_HI_PHYS_ADDR_REG(eq, pg);
+ val = upper_32_bits(eq->dma_addr[pg]);
+
+ hinic_hwif_write_reg(hwif, addr, val);
+
+ addr = EQ_LO_PHYS_ADDR_REG(eq, pg);
+ val = lower_32_bits(eq->dma_addr[pg]);
+
+ hinic_hwif_write_reg(hwif, addr, val);
+ }
+
+ init_val = HINIC_EQ_ELEM_DESC_SET(eq->wrapped, WRAPPED);
+
+ if (eq->type == HINIC_AEQ)
+ aeq_elements_init(eq, init_val);
+
+ return 0;
+
+dma_alloc_err:
+ while (--pg >= 0)
+ dma_free_coherent(&pdev->dev, eq->page_size,
+ eq->virt_addr[pg],
+ eq->dma_addr[pg]);
+
+ devm_kfree(&pdev->dev, eq->virt_addr);
+
+virt_addr_alloc_err:
+ devm_kfree(&pdev->dev, eq->dma_addr);
+ return err;
+}
+
+/**
+ * free_eq_pages - free the pages of the queue
+ * @eq: the Event Queue
+ **/
+static void free_eq_pages(struct hinic_eq *eq)
+{
+ struct hinic_hwif *hwif = eq->hwif;
+ struct pci_dev *pdev = hwif->pdev;
+ int pg;
+
+ for (pg = 0; pg < eq->num_pages; pg++)
+ dma_free_coherent(&pdev->dev, eq->page_size,
+ eq->virt_addr[pg],
+ eq->dma_addr[pg]);
+
+ devm_kfree(&pdev->dev, eq->virt_addr);
+ devm_kfree(&pdev->dev, eq->dma_addr);
+}
+
/**
* init_eq - initialize Event Queue
* @eq: the event queue
@@ -77,8 +444,81 @@ static int init_eq(struct hinic_eq *eq, struct hinic_hwif *hwif,
enum hinic_eq_type type, int q_id, u32 q_len, u32 page_size,
struct msix_entry entry)
{
- /* should be implemented */
+ struct pci_dev *pdev = hwif->pdev;
+ int err;
+
+ eq->hwif = hwif;
+ eq->type = type;
+ eq->q_id = q_id;
+ eq->q_len = q_len;
+ eq->page_size = page_size;
+
+ /* Clear PI and CI, also clear the ARM bit */
+ hinic_hwif_write_reg(eq->hwif, EQ_CONS_IDX_REG_ADDR(eq), 0);
+ hinic_hwif_write_reg(eq->hwif, EQ_PROD_IDX_REG_ADDR(eq), 0);
+
+ eq->cons_idx = 0;
+ eq->wrapped = 0;
+
+ if (type == HINIC_AEQ) {
+ eq->elem_size = HINIC_AEQE_SIZE;
+ } else {
+ dev_err(&pdev->dev, "Invalid EQ type\n");
+ return -EINVAL;
+ }
+
+ eq->num_pages = GET_EQ_NUM_PAGES(eq, page_size);
+ eq->num_elem_in_pg = GET_EQ_NUM_ELEMS_IN_PG(eq, page_size);
+
+ eq->msix_entry = entry;
+
+ if (eq->num_elem_in_pg & (eq->num_elem_in_pg - 1)) {
+ dev_err(&pdev->dev, "num elements in eq page != power of 2\n");
+ return -EINVAL;
+ }
+
+ if (eq->num_pages > EQ_MAX_PAGES) {
+ dev_err(&pdev->dev, "too many pages for eq\n");
+ return -EINVAL;
+ }
+
+ set_eq_ctrls(eq);
+ eq_update_ci(eq);
+
+ err = alloc_eq_pages(eq);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to allocate pages for eq\n");
+ return err;
+ }
+
+ if (type == HINIC_AEQ) {
+ struct hinic_eq_work *aeq_work = &eq->aeq_work;
+
+ INIT_WORK(&aeq_work->work, eq_irq_work);
+ }
+
+ /* set the attributes of the msix entry */
+ hinic_msix_attr_set(eq->hwif, eq->msix_entry.entry,
+ HINIC_EQ_MSIX_PENDING_LIMIT_DEFAULT,
+ HINIC_EQ_MSIX_COALESC_TIMER_DEFAULT,
+ HINIC_EQ_MSIX_LLI_TIMER_DEFAULT,
+ HINIC_EQ_MSIX_LLI_CREDIT_LIMIT_DEFAULT,
+ HINIC_EQ_MSIX_RESEND_TIMER_DEFAULT);
+
+ if (type == HINIC_AEQ)
+ err = request_irq(entry.vector, aeq_interrupt, 0,
+ "hinic_aeq", eq);
+
+ if (err) {
+ dev_err(&pdev->dev, "Failed to request irq for the EQ\n");
+ goto req_irq_err;
+ }
+
return 0;
+
+req_irq_err:
+ free_eq_pages(eq);
+ return err;
}
/**
@@ -87,7 +527,17 @@ static int init_eq(struct hinic_eq *eq, struct hinic_hwif *hwif,
**/
static void remove_eq(struct hinic_eq *eq)
{
- /* should be implemented */
+ struct msix_entry *entry = &eq->msix_entry;
+
+ free_irq(entry->vector, eq);
+
+ if (eq->type == HINIC_AEQ) {
+ struct hinic_eq_work *aeq_work = &eq->aeq_work;
+
+ cancel_work_sync(&aeq_work->work);
+ }
+
+ free_eq_pages(eq);
}
/**
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.h
index ee72f1a..b406360 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.h
+++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.h
@@ -24,8 +24,84 @@
#include "hinic_hw_if.h"
+#define HINIC_AEQ_CTRL_0_INT_IDX_SHIFT 0
+#define HINIC_AEQ_CTRL_0_DMA_ATTR_SHIFT 12
+#define HINIC_AEQ_CTRL_0_PCI_INTF_IDX_SHIFT 20
+#define HINIC_AEQ_CTRL_0_INT_MODE_SHIFT 31
+
+#define HINIC_AEQ_CTRL_0_INT_IDX_MASK 0x3FF
+#define HINIC_AEQ_CTRL_0_DMA_ATTR_MASK 0x3F
+#define HINIC_AEQ_CTRL_0_PCI_INTF_IDX_MASK 0x3
+#define HINIC_AEQ_CTRL_0_INT_MODE_MASK 0x1
+
+#define HINIC_AEQ_CTRL_0_SET(val, member) \
+ (((u32)(val) & HINIC_AEQ_CTRL_0_##member##_MASK) << \
+ HINIC_AEQ_CTRL_0_##member##_SHIFT)
+
+#define HINIC_AEQ_CTRL_0_CLEAR(val, member) \
+ ((val) & (~(HINIC_AEQ_CTRL_0_##member##_MASK \
+ << HINIC_AEQ_CTRL_0_##member##_SHIFT)))
+
+#define HINIC_AEQ_CTRL_1_LEN_SHIFT 0
+#define HINIC_AEQ_CTRL_1_ELEM_SIZE_SHIFT 24
+#define HINIC_AEQ_CTRL_1_PAGE_SIZE_SHIFT 28
+
+#define HINIC_AEQ_CTRL_1_LEN_MASK 0x1FFFFF
+#define HINIC_AEQ_CTRL_1_ELEM_SIZE_MASK 0x3
+#define HINIC_AEQ_CTRL_1_PAGE_SIZE_MASK 0xF
+
+#define HINIC_AEQ_CTRL_1_SET(val, member) \
+ (((u32)(val) & HINIC_AEQ_CTRL_1_##member##_MASK) << \
+ HINIC_AEQ_CTRL_1_##member##_SHIFT)
+
+#define HINIC_AEQ_CTRL_1_CLEAR(val, member) \
+ ((val) & (~(HINIC_AEQ_CTRL_1_##member##_MASK \
+ << HINIC_AEQ_CTRL_1_##member##_SHIFT)))
+
+#define HINIC_EQ_ELEM_DESC_TYPE_SHIFT 0
+#define HINIC_EQ_ELEM_DESC_SRC_SHIFT 7
+#define HINIC_EQ_ELEM_DESC_SIZE_SHIFT 8
+#define HINIC_EQ_ELEM_DESC_WRAPPED_SHIFT 31
+
+#define HINIC_EQ_ELEM_DESC_TYPE_MASK 0x7F
+#define HINIC_EQ_ELEM_DESC_SRC_MASK 0x1
+#define HINIC_EQ_ELEM_DESC_SIZE_MASK 0xFF
+#define HINIC_EQ_ELEM_DESC_WRAPPED_MASK 0x1
+
+#define HINIC_EQ_ELEM_DESC_SET(val, member) \
+ (((u32)(val) & HINIC_EQ_ELEM_DESC_##member##_MASK) << \
+ HINIC_EQ_ELEM_DESC_##member##_SHIFT)
+
+#define HINIC_EQ_ELEM_DESC_GET(val, member) \
+ (((val) >> HINIC_EQ_ELEM_DESC_##member##_SHIFT) & \
+ HINIC_EQ_ELEM_DESC_##member##_MASK)
+
+#define HINIC_EQ_CI_IDX_SHIFT 0
+#define HINIC_EQ_CI_WRAPPED_SHIFT 20
+#define HINIC_EQ_CI_XOR_CHKSUM_SHIFT 24
+#define HINIC_EQ_CI_INT_ARMED_SHIFT 31
+
+#define HINIC_EQ_CI_IDX_MASK 0xFFFFF
+#define HINIC_EQ_CI_WRAPPED_MASK 0x1
+#define HINIC_EQ_CI_XOR_CHKSUM_MASK 0xF
+#define HINIC_EQ_CI_INT_ARMED_MASK 0x1
+
+#define HINIC_EQ_CI_SET(val, member) \
+ (((u32)(val) & HINIC_EQ_CI_##member##_MASK) << \
+ HINIC_EQ_CI_##member##_SHIFT)
+
+#define HINIC_EQ_CI_CLEAR(val, member) \
+ ((val) & (~(HINIC_EQ_CI_##member##_MASK \
+ << HINIC_EQ_CI_##member##_SHIFT)))
+
#define HINIC_MAX_AEQS 4
+#define HINIC_AEQE_SIZE 64
+
+#define HINIC_AEQE_DESC_SIZE 4
+#define HINIC_AEQE_DATA_SIZE \
+ (HINIC_AEQE_SIZE - HINIC_AEQE_DESC_SIZE)
+
#define HINIC_DEFAULT_AEQ_LEN 64
#define HINIC_EQ_PAGE_SIZE SZ_4K
@@ -45,6 +121,11 @@ enum hinic_eqe_state {
HINIC_EQE_RUNNING = BIT(1),
};
+struct hinic_aeq_elem {
+ u8 data[HINIC_AEQE_DATA_SIZE];
+ u32 desc;
+};
+
struct hinic_eq_work {
struct work_struct work;
void *data;
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_if.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.c
index dbb3346..c5bc98d 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_hw_if.c
+++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.c
@@ -25,6 +25,98 @@
#define PCIE_ATTR_ENTRY 0
+#define VALID_MSIX_IDX(attr, msix_index) ((msix_index) < (attr)->num_irqs)
+
+/**
+ * hinic_msix_attr_set - set message attribute for msix entry
+ * @hwif: the HW interface of a pci function device
+ * @msix_index: msix_index
+ * @pending_limit: the maximum pending interrupt events (unit 8)
+ * @coalesc_timer: coalesc period for interrupt (unit 8 us)
+ * @lli_timer: replenishing period for low latency credit (unit 8 us)
+ * @lli_credit_limit: maximum credits for low latency msix messages (unit 8)
+ * @resend_timer: maximum wait for resending msix (unit coalesc period)
+ *
+ * Return 0 - Success, negative - Failure
+ **/
+int hinic_msix_attr_set(struct hinic_hwif *hwif, u16 msix_index,
+ u8 pending_limit, u8 coalesc_timer,
+ u8 lli_timer, u8 lli_credit_limit,
+ u8 resend_timer)
+{
+ u32 msix_ctrl, addr;
+
+ if (!VALID_MSIX_IDX(&hwif->attr, msix_index))
+ return -EINVAL;
+
+ msix_ctrl = HINIC_MSIX_ATTR_SET(pending_limit, PENDING_LIMIT) |
+ HINIC_MSIX_ATTR_SET(coalesc_timer, COALESC_TIMER) |
+ HINIC_MSIX_ATTR_SET(lli_timer, LLI_TIMER) |
+ HINIC_MSIX_ATTR_SET(lli_credit_limit, LLI_CREDIT) |
+ HINIC_MSIX_ATTR_SET(resend_timer, RESEND_TIMER);
+
+ addr = HINIC_CSR_MSIX_CTRL_ADDR(msix_index);
+
+ hinic_hwif_write_reg(hwif, addr, msix_ctrl);
+ return 0;
+}
+
+/**
+ * hinic_msix_attr_get - get message attribute of msix entry
+ * @hwif: the HW interface of a pci function device
+ * @msix_index: msix_index
+ * @pending_limit: the maximum pending interrupt events (unit 8)
+ * @coalesc_timer: coalesc period for interrupt (unit 8 us)
+ * @lli_timer: replenishing period for low latency credit (unit 8 us)
+ * @lli_credit_limit: maximum credits for low latency msix messages (unit 8)
+ * @resend_timer: maximum wait for resending msix (unit coalesc period)
+ *
+ * Return 0 - Success, negative - Failure
+ **/
+int hinic_msix_attr_get(struct hinic_hwif *hwif, u16 msix_index,
+ u8 *pending_limit, u8 *coalesc_timer,
+ u8 *lli_timer, u8 *lli_credit_limit,
+ u8 *resend_timer)
+{
+ u32 addr, val;
+
+ if (!VALID_MSIX_IDX(&hwif->attr, msix_index))
+ return -EINVAL;
+
+ addr = HINIC_CSR_MSIX_CTRL_ADDR(msix_index);
+ val = hinic_hwif_read_reg(hwif, addr);
+
+ *pending_limit = HINIC_MSIX_ATTR_GET(val, PENDING_LIMIT);
+ *coalesc_timer = HINIC_MSIX_ATTR_GET(val, COALESC_TIMER);
+ *lli_timer = HINIC_MSIX_ATTR_GET(val, LLI_TIMER);
+ *lli_credit_limit = HINIC_MSIX_ATTR_GET(val, LLI_CREDIT);
+ *resend_timer = HINIC_MSIX_ATTR_GET(val, RESEND_TIMER);
+
+ return 0;
+}
+
+/**
+ * hinic_msix_attr_cnt_clear - clear message attribute counters for msix entry
+ * @hwif: the HW interface of a pci function device
+ * @msix_index: msix_index
+ *
+ * Return 0 - Success, negative - Failure
+ **/
+int hinic_msix_attr_cnt_clear(struct hinic_hwif *hwif, u16 msix_index)
+{
+ u32 msix_ctrl, addr;
+
+ if (!VALID_MSIX_IDX(&hwif->attr, msix_index))
+ return -EINVAL;
+
+ msix_ctrl = HINIC_MSIX_CNT_SET(1, RESEND_TIMER);
+
+ addr = HINIC_CSR_MSIX_CNT_ADDR(msix_index);
+
+ hinic_hwif_write_reg(hwif, addr, msix_ctrl);
+ return 0;
+}
+
/**
* 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 4cfe325..ccc6e23 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h
+++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h
@@ -88,6 +88,34 @@
((val) & (~(HINIC_PPF_ELECTION_##member##_MASK \
<< HINIC_PPF_ELECTION_##member##_SHIFT)))
+#define HINIC_MSIX_PENDING_LIMIT_SHIFT 0
+#define HINIC_MSIX_COALESC_TIMER_SHIFT 8
+#define HINIC_MSIX_LLI_TIMER_SHIFT 16
+#define HINIC_MSIX_LLI_CREDIT_SHIFT 24
+#define HINIC_MSIX_RESEND_TIMER_SHIFT 29
+
+#define HINIC_MSIX_PENDING_LIMIT_MASK 0xFF
+#define HINIC_MSIX_COALESC_TIMER_MASK 0xFF
+#define HINIC_MSIX_LLI_TIMER_MASK 0xFF
+#define HINIC_MSIX_LLI_CREDIT_MASK 0x1F
+#define HINIC_MSIX_RESEND_TIMER_MASK 0x7
+
+#define HINIC_MSIX_ATTR_SET(val, member) \
+ (((u32)(val) & HINIC_MSIX_##member##_MASK) << \
+ HINIC_MSIX_##member##_SHIFT)
+
+#define HINIC_MSIX_ATTR_GET(val, member) \
+ (((val) >> HINIC_MSIX_##member##_SHIFT) & \
+ HINIC_MSIX_##member##_MASK)
+
+#define HINIC_MSIX_CNT_RESEND_TIMER_SHIFT 29
+
+#define HINIC_MSIX_CNT_RESEND_TIMER_MASK 0x1
+
+#define HINIC_MSIX_CNT_SET(val, member) \
+ (((u32)(val) & HINIC_MSIX_CNT_##member##_MASK) << \
+ HINIC_MSIX_CNT_##member##_SHIFT)
+
#define HINIC_HWIF_NUM_AEQS(hwif) ((hwif)->attr.num_aeqs)
#define HINIC_HWIF_NUM_CEQS(hwif) ((hwif)->attr.num_ceqs)
#define HINIC_HWIF_NUM_IRQS(hwif) ((hwif)->attr.num_irqs)
@@ -105,19 +133,25 @@
#define HINIC_PCIE_AT_DISABLE 0
#define HINIC_PCIE_PH_DISABLE 0
+#define HINIC_EQ_MSIX_PENDING_LIMIT_DEFAULT 0 /* Disabled */
+#define HINIC_EQ_MSIX_COALESC_TIMER_DEFAULT 0xFF /* max */
+#define HINIC_EQ_MSIX_LLI_TIMER_DEFAULT 0 /* Disabled */
+#define HINIC_EQ_MSIX_LLI_CREDIT_LIMIT_DEFAULT 0 /* Disabled */
+#define HINIC_EQ_MSIX_RESEND_TIMER_DEFAULT 7 /* max */
+
enum hinic_pcie_nosnoop {
- HINIC_PCIE_SNOOP = 0,
- HINIC_PCIE_NO_SNOOP = 1,
+ HINIC_PCIE_SNOOP = 0,
+ HINIC_PCIE_NO_SNOOP = 1,
};
enum hinic_pcie_tph {
- HINIC_PCIE_TPH_DISABLE = 0,
- HINIC_PCIE_TPH_ENABLE = 1,
+ HINIC_PCIE_TPH_DISABLE = 0,
+ HINIC_PCIE_TPH_ENABLE = 1,
};
enum hinic_func_type {
- HINIC_PF = 0,
- HINIC_PPF = 2,
+ HINIC_PF = 0,
+ HINIC_PPF = 2,
};
enum hinic_mod_type {
@@ -166,6 +200,18 @@ static inline void hinic_hwif_write_reg(struct hinic_hwif *hwif, u32 reg,
writel(cpu_to_be32(val), hwif->cfg_regs_bar + reg);
}
+int hinic_msix_attr_set(struct hinic_hwif *hwif, u16 msix_index,
+ u8 pending_limit, u8 coalesc_timer,
+ u8 lli_timer_cfg, u8 lli_credit_limit,
+ u8 resend_timer);
+
+int hinic_msix_attr_get(struct hinic_hwif *hwif, u16 msix_index,
+ u8 *pending_limit, u8 *coalesc_timer_cfg,
+ u8 *lli_timer, u8 *lli_credit_limit,
+ u8 *resend_timer);
+
+int hinic_msix_attr_cnt_clear(struct hinic_hwif *hwif, u16 msix_index);
+
int hinic_init_hwif(struct hinic_hwif *hwif, struct pci_dev *pdev);
void hinic_free_hwif(struct hinic_hwif *hwif);
--
1.9.1
[toc] | [prev] | [next] | [standalone]
| From | Aviad Krawczyk <aviad.krawczyk@huawei.com> |
|---|---|
| Date | 2017-07-19 11:40 +0200 |
| Subject | [PATCH V2 net-next 09/21] net-next/hinic: Add Rx mode and link event handler |
| Message-ID | <u4TCz-6D1-35@gated-at.bofh.it> |
| In reply to | #1691183 |
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 <aviad.krawczyk@huawei.com>
Signed-off-by: Zhao Chen <zhaochen6@huawei.com>
---
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 | 120 +++++++++
drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h | 63 ++++-
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 | 69 +++++-
drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h | 28 +++
drivers/net/ethernet/huawei/hinic/hinic_main.c | 285 +++++++++++++++++++++-
drivers/net/ethernet/huawei/hinic/hinic_port.c | 101 ++++++++
drivers/net/ethernet/huawei/hinic/hinic_port.h | 66 +++++
11 files changed, 767 insertions(+), 18 deletions(-)
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_dev.h b/drivers/net/ethernet/huawei/hinic/hinic_dev.h
index dd540b4..91a3356 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_dev.h
+++ b/drivers/net/ethernet/huawei/hinic/hinic_dev.h
@@ -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
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h
index 52eb89c..1f57301 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 52d9bb7..288b4b0 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c
+++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c
@@ -232,6 +232,114 @@ 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 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 = (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
*
@@ -250,6 +358,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;
}
@@ -259,6 +372,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 b3325e7..616b2da 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 <linux/pci.h>
#include <linux/types.h>
+#include <linux/bitops.h>
#include "hinic_hw_if.h"
#include "hinic_hw_eqs.h"
@@ -25,34 +26,50 @@
#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;
};
enum hinic_port_cmd {
- HINIC_PORT_CMD_CHANGE_MTU = 2,
+ HINIC_PORT_CMD_CHANGE_MTU = 2,
+
+ HINIC_PORT_CMD_ADD_VLAN = 3,
+ HINIC_PORT_CMD_DEL_VLAN = 4,
- HINIC_PORT_CMD_ADD_VLAN = 3,
- HINIC_PORT_CMD_DEL_VLAN = 4,
+ HINIC_PORT_CMD_SET_MAC = 9,
+ HINIC_PORT_CMD_GET_MAC = 10,
+ HINIC_PORT_CMD_DEL_MAC = 11,
- HINIC_PORT_CMD_SET_MAC = 9,
- HINIC_PORT_CMD_GET_MAC = 10,
- HINIC_PORT_CMD_DEL_MAC = 11,
+ HINIC_PORT_CMD_SET_RX_MODE = 12,
- HINIC_PORT_CMD_SET_RX_MODE = 12,
+ HINIC_PORT_CMD_GET_LINK_STATE = 24,
- HINIC_PORT_CMD_GET_LINK_STATE = 24,
+ HINIC_PORT_CMD_SET_PORT_STATE = 41,
- HINIC_PORT_CMD_SET_PORT_STATE = 41,
+ HINIC_PORT_CMD_FWCTXT_INIT = 69,
- HINIC_PORT_CMD_FWCTXT_INIT = 69,
+ HINIC_PORT_CMD_SET_FUNC_STATE = 93,
- HINIC_PORT_CMD_SET_FUNC_STATE = 93,
+ HINIC_PORT_CMD_GET_GLOBAL_QPN = 102,
+
+ HINIC_PORT_CMD_GET_CAP = 170,
+};
- HINIC_PORT_CMD_GET_GLOBAL_QPN = 102,
+enum hinic_mgmt_msg_cmd {
+ HINIC_MGMT_MSG_CMD_BASE = 160,
+
+ HINIC_MGMT_MSG_CMD_LINK_STATUS = 160,
+
+ HINIC_MGMT_MSG_CMD_MAX,
+};
- HINIC_PORT_CMD_GET_CAP = 170,
+enum hinic_cb_state {
+ HINIC_CB_ENABLED = BIT(0),
+ HINIC_CB_RUNNING = BIT(1),
};
struct hinic_hwdev {
@@ -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 c5bc98d..9296758 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 ccc6e23..88df3c0 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 006aa3d..7433d67e 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c
+++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c
@@ -73,6 +73,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
@@ -150,8 +190,8 @@ static u16 mgmt_msg_len(u16 msg_data_len)
* @pf_to_mgmt: PF to MGMT channel
* @mod: module in the chip that will get the message
* @cmd: command of the message
- * @data: the msg data
- * @data_len: the msg data length
+ * @msg: the msg data
+ * @msg_len: the msg data length
* @ack_type: ask for response
* @direction: the direction of the original message
* @resp_msg_id: msg id to response for
@@ -338,11 +378,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 7f08ea2..4f678c8 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 <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 {
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 09d303b..34a1589 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_main.c
+++ b/drivers/net/ethernet/huawei/hinic/hinic_main.c
@@ -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 "hinic_pci_id_tbl.h"
#include "hinic_hw_dev.h"
@@ -37,14 +39,98 @@
MODULE_DESCRIPTION("Huawei Intelligent NIC driver");
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) {
+ dev_err(&netdev->dev, "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) {
+ dev_err(&netdev->dev, "Failed to get link state\n");
+ goto port_link_err;
+ }
+
+ 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:
+ up(&nic_dev->mgmt_lock);
+ ret = hinic_port_set_state(nic_dev, HINIC_PORT_DISABLE);
+ if (ret)
+ dev_warn(&netdev->dev, "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) {
+ dev_err(&netdev->dev, "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);
@@ -119,6 +205,80 @@ 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) {
+ dev_err(&netdev->dev, "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) {
+ dev_err(&netdev->dev, "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)
{
@@ -186,12 +346,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 */
};
@@ -205,6 +407,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
*
@@ -215,6 +469,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);
@@ -243,6 +498,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);
@@ -254,6 +510,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);
@@ -272,10 +534,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) {
dev_err(&netdev->dev, "Failed to register netdev\n");
@@ -285,10 +553,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:
vlan_bitmap_err:
free_netdev(netdev);
@@ -361,15 +635,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);
hinic_free_hwdev(nic_dev->hwdev);
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_port.c b/drivers/net/ethernet/huawei/hinic/hinic_port.c
index 5524320..fdf5483 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)) {
+ dev_err(&pdev->dev, "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)) {
+ 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;
+}
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_port.h b/drivers/net/ethernet/huawei/hinic/hinic_port.h
index 4cafb94..95fa29f 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_port.h
+++ b/drivers/net/ethernet/huawei/hinic/hinic_port.h
@@ -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 = 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
--
1.9.1
[toc] | [prev] | [next] | [standalone]
| From | Aviad Krawczyk <aviad.krawczyk@huawei.com> |
|---|---|
| Date | 2017-07-19 11:40 +0200 |
| Subject | [PATCH V2 net-next 06/21] net-next/hinic: Add api cmd commands |
| Message-ID | <u4TCA-6D1-53@gated-at.bofh.it> |
| In reply to | #1691183 |
Add the api cmd commands for sending management messages to the nic.
Signed-off-by: Aviad Krawczyk <aviad.krawczyk@huawei.com>
Signed-off-by: Zhao Chen <zhaochen6@huawei.com>
---
.../net/ethernet/huawei/hinic/hinic_hw_api_cmd.c | 331 ++++++++++++++++++++-
.../net/ethernet/huawei/hinic/hinic_hw_api_cmd.h | 65 ++++
drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h | 7 +
3 files changed, 401 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.c
index eacb33b..5627191 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.c
+++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.c
@@ -24,7 +24,9 @@
#include <linux/jiffies.h>
#include <linux/delay.h>
#include <linux/log2.h>
+#include <linux/semaphore.h>
#include <asm/byteorder.h>
+#include <asm/barrier.h>
#include "hinic_hw_csr.h"
#include "hinic_hw_if.h"
@@ -44,14 +46,314 @@
#define API_CMD_BUF_SIZE 2048
+/* Sizes of the members in hinic_api_cmd_cell */
+#define API_CMD_CELL_DESC_SIZE 8
+#define API_CMD_CELL_DATA_ADDR_SIZE 8
+
+#define API_CMD_CELL_ALIGNMENT 8
+
#define API_CMD_TIMEOUT 1000
+#define MASKED_IDX(chain, idx) ((idx) & ((chain)->num_cells - 1))
+
+#define SIZE_8BYTES(size) (ALIGN((size), 8) >> 3)
+#define SIZE_4BYTES(size) (ALIGN((size), 4) >> 2)
+
+#define RD_DMA_ATTR_DEFAULT 0
+#define WR_DMA_ATTR_DEFAULT 0
+
+enum api_cmd_data_format {
+ SGE_DATA = 1, /* cell data is passed by hw address */
+};
+
+enum api_cmd_type {
+ API_CMD_WRITE = 0,
+};
+
+enum api_cmd_bypass {
+ NO_BYPASS = 0,
+ BYPASS = 1,
+};
+
enum api_cmd_xor_chk_level {
XOR_CHK_DIS = 0,
XOR_CHK_ALL = 3,
};
+static u8 xor_chksum_set(void *data)
+{
+ int idx;
+ u8 *val, checksum = 0;
+
+ val = data;
+
+ for (idx = 0; idx < 7; idx++)
+ checksum ^= val[idx];
+
+ return checksum;
+}
+
+static void set_prod_idx(struct hinic_api_cmd_chain *chain)
+{
+ struct hinic_hwif *hwif = chain->hwif;
+ enum hinic_api_cmd_chain_type chain_type = chain->chain_type;
+ u32 hw_prod_idx_addr = HINIC_CSR_API_CMD_CHAIN_PI_ADDR(chain_type);
+ u32 prod_idx;
+
+ prod_idx = hinic_hwif_read_reg(hwif, hw_prod_idx_addr);
+
+ prod_idx = HINIC_API_CMD_PI_CLEAR(prod_idx, IDX);
+
+ prod_idx |= HINIC_API_CMD_PI_SET(chain->prod_idx, IDX);
+
+ hinic_hwif_write_reg(hwif, hw_prod_idx_addr, prod_idx);
+}
+
+static u32 get_hw_cons_idx(struct hinic_api_cmd_chain *chain)
+{
+ u32 addr, val;
+
+ addr = HINIC_CSR_API_CMD_STATUS_ADDR(chain->chain_type);
+ val = hinic_hwif_read_reg(chain->hwif, addr);
+
+ return HINIC_API_CMD_STATUS_GET(val, CONS_IDX);
+}
+
+/**
+ * chain_busy - check if the chain is still processing last requests
+ * @chain: chain to check
+ *
+ * Return 0 - Success, negative - Failure
+ **/
+static int chain_busy(struct hinic_api_cmd_chain *chain)
+{
+ struct hinic_hwif *hwif = chain->hwif;
+ struct pci_dev *pdev = hwif->pdev;
+ u32 prod_idx;
+
+ switch (chain->chain_type) {
+ case HINIC_API_CMD_WRITE_TO_MGMT_CPU:
+ chain->cons_idx = get_hw_cons_idx(chain);
+ prod_idx = chain->prod_idx;
+
+ /* check for a space for a new command */
+ if (chain->cons_idx == MASKED_IDX(chain, prod_idx + 1)) {
+ dev_err(&pdev->dev, "API CMD chain %d is busy\n",
+ chain->chain_type);
+ return -EBUSY;
+ }
+ break;
+
+ default:
+ dev_err(&pdev->dev, "Unknown API CMD Chain type\n");
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ * get_cell_data_size - get the data size of a specific cell type
+ * @type: chain type
+ *
+ * Return the data(Desc + Address) size in the cell
+ **/
+static u8 get_cell_data_size(enum hinic_api_cmd_chain_type type)
+{
+ u8 cell_data_size = 0;
+
+ switch (type) {
+ case HINIC_API_CMD_WRITE_TO_MGMT_CPU:
+ cell_data_size = ALIGN(API_CMD_CELL_DESC_SIZE +
+ API_CMD_CELL_DATA_ADDR_SIZE,
+ API_CMD_CELL_ALIGNMENT);
+ break;
+ default:
+ break;
+ }
+
+ return cell_data_size;
+}
+
+/**
+ * prepare_cell_ctrl - prepare the ctrl of the cell for the command
+ * @cell_ctrl: the control of the cell to set the control value into it
+ * @data_size: the size of the data in the cell
+ **/
+static void prepare_cell_ctrl(u64 *cell_ctrl, u16 data_size)
+{
+ u64 ctrl;
+ u8 chksum;
+
+ ctrl = HINIC_API_CMD_CELL_CTRL_SET(SIZE_8BYTES(data_size), DATA_SZ) |
+ HINIC_API_CMD_CELL_CTRL_SET(RD_DMA_ATTR_DEFAULT, RD_DMA_ATTR) |
+ HINIC_API_CMD_CELL_CTRL_SET(WR_DMA_ATTR_DEFAULT, WR_DMA_ATTR);
+
+ chksum = xor_chksum_set(&ctrl);
+
+ ctrl |= HINIC_API_CMD_CELL_CTRL_SET(chksum, XOR_CHKSUM);
+
+ /* The data in the HW should be in Big Endian Format */
+ *cell_ctrl = cpu_to_be64(ctrl);
+}
+
+/**
+ * prepare_api_cmd - prepare API CMD command
+ * @chain: chain for the command
+ * @cell: the cell of the command
+ * @dest: destination node on the card that will receive the command
+ * @cmd: command data
+ * @cmd_size: the command size
+ **/
+static void prepare_api_cmd(struct hinic_api_cmd_chain *chain,
+ struct hinic_api_cmd_cell *cell,
+ enum hinic_node_id dest,
+ void *cmd, u16 cmd_size)
+{
+ struct hinic_hwif *hwif = chain->hwif;
+ struct pci_dev *pdev = hwif->pdev;
+ struct hinic_api_cmd_cell_ctxt *cell_ctxt;
+
+ cell_ctxt = &chain->cell_ctxt[chain->prod_idx];
+
+ switch (chain->chain_type) {
+ case HINIC_API_CMD_WRITE_TO_MGMT_CPU:
+ cell->desc = HINIC_API_CMD_DESC_SET(SGE_DATA, API_TYPE) |
+ HINIC_API_CMD_DESC_SET(API_CMD_WRITE, RD_WR) |
+ HINIC_API_CMD_DESC_SET(NO_BYPASS, MGMT_BYPASS);
+ break;
+
+ default:
+ dev_err(&pdev->dev, "unknown Chain type\n");
+ return;
+ }
+
+ cell->desc |= HINIC_API_CMD_DESC_SET(dest, DEST) |
+ HINIC_API_CMD_DESC_SET(SIZE_4BYTES(cmd_size), SIZE);
+
+ cell->desc |= HINIC_API_CMD_DESC_SET(xor_chksum_set(&cell->desc),
+ XOR_CHKSUM);
+
+ /* The data in the HW should be in Big Endian Format */
+ cell->desc = cpu_to_be64(cell->desc);
+
+ memcpy(cell_ctxt->api_cmd_vaddr, cmd, cmd_size);
+}
+
+/**
+ * prepare_cell - prepare cell ctrl and cmd in the current cell
+ * @chain: chain for the command
+ * @dest: destination node on the card that will receive the command
+ * @cmd: command data
+ * @cmd_size: the command size
+ *
+ * Return 0 - Success, negative - Failure
+ **/
+static void prepare_cell(struct hinic_api_cmd_chain *chain,
+ enum hinic_node_id dest,
+ void *cmd, u16 cmd_size)
+{
+ struct hinic_api_cmd_cell *curr_node = chain->curr_node;
+ u16 data_size = get_cell_data_size(chain->chain_type);
+
+ prepare_cell_ctrl(&curr_node->ctrl, data_size);
+ prepare_api_cmd(chain, curr_node, dest, cmd, cmd_size);
+}
+
+static inline void cmd_chain_prod_idx_inc(struct hinic_api_cmd_chain *chain)
+{
+ chain->prod_idx = MASKED_IDX(chain, chain->prod_idx + 1);
+}
+
+/**
+ * api_cmd_status_update - update the status in the chain struct
+ * @chain: chain to update
+ **/
+static void api_cmd_status_update(struct hinic_api_cmd_chain *chain)
+{
+ struct hinic_hwif *hwif = chain->hwif;
+ struct pci_dev *pdev = hwif->pdev;
+ struct hinic_api_cmd_status *wb_status;
+ enum hinic_api_cmd_chain_type chain_type;
+ u64 status_header;
+ u32 status;
+
+ wb_status = chain->wb_status;
+ status_header = be64_to_cpu(wb_status->header);
+
+ status = be32_to_cpu(wb_status->status);
+ if (HINIC_API_CMD_STATUS_GET(status, CHKSUM_ERR)) {
+ dev_err(&pdev->dev, "API CMD status: Xor check error\n");
+ return;
+ }
+
+ chain_type = HINIC_API_CMD_STATUS_HEADER_GET(status_header, CHAIN_ID);
+ if (chain_type >= HINIC_API_CMD_MAX) {
+ dev_err(&pdev->dev, "unknown API CMD Chain %d\n", chain_type);
+ return;
+ }
+
+ chain->cons_idx = HINIC_API_CMD_STATUS_GET(status, CONS_IDX);
+}
+
+/**
+ * wait_for_status_poll - wait for write to api cmd command to complete
+ * @chain: the chain of the command
+ *
+ * Return 0 - Success, negative - Failure
+ **/
+static int wait_for_status_poll(struct hinic_api_cmd_chain *chain)
+{
+ unsigned long end;
+ int err = -ETIMEDOUT;
+
+ end = jiffies + msecs_to_jiffies(API_CMD_TIMEOUT);
+ do {
+ api_cmd_status_update(chain);
+
+ /* wait for CI to be updated - sign for completion */
+ if (chain->cons_idx == chain->prod_idx) {
+ err = 0;
+ break;
+ }
+
+ msleep(20);
+ } while (time_before(jiffies, end));
+
+ return err;
+}
+
+/**
+ * wait_for_api_cmd_completion - wait for command to complete
+ * @chain: chain for the command
+ *
+ * Return 0 - Success, negative - Failure
+ **/
+static int wait_for_api_cmd_completion(struct hinic_api_cmd_chain *chain)
+{
+ struct hinic_hwif *hwif = chain->hwif;
+ struct pci_dev *pdev = hwif->pdev;
+ int err;
+
+ switch (chain->chain_type) {
+ case HINIC_API_CMD_WRITE_TO_MGMT_CPU:
+ err = wait_for_status_poll(chain);
+ if (err) {
+ dev_err(&pdev->dev, "API CMD Poll status timeout\n");
+ break;
+ }
+ break;
+
+ default:
+ dev_err(&pdev->dev, "unknown API CMD Chain type\n");
+ err = -EINVAL;
+ break;
+ }
+
+ return err;
+}
+
/**
* api_cmd - API CMD command
* @chain: chain for the command
@@ -64,8 +366,31 @@ enum api_cmd_xor_chk_level {
static int api_cmd(struct hinic_api_cmd_chain *chain,
enum hinic_node_id dest, void *cmd, u16 cmd_size)
{
- /* should be implemented */
- return -EINVAL;
+ struct hinic_api_cmd_cell_ctxt *ctxt;
+ int err;
+
+ down(&chain->sem);
+ if (chain_busy(chain)) {
+ up(&chain->sem);
+ return -EBUSY;
+ }
+
+ prepare_cell(chain, dest, cmd, cmd_size);
+ cmd_chain_prod_idx_inc(chain);
+
+ wmb(); /* inc pi before issue the command */
+
+ set_prod_idx(chain); /* issue the command */
+
+ ctxt = &chain->cell_ctxt[chain->prod_idx];
+
+ chain->curr_node = ctxt->cell_vaddr;
+
+ err = wait_for_api_cmd_completion(chain);
+
+ up(&chain->sem);
+
+ return err;
}
/**
@@ -490,6 +815,8 @@ static int api_chain_init(struct hinic_api_cmd_chain *chain,
chain->prod_idx = 0;
chain->cons_idx = 0;
+ sema_init(&chain->sem, 1);
+
cell_ctxt_size = chain->num_cells * sizeof(*chain->cell_ctxt);
chain->cell_ctxt = devm_kzalloc(&pdev->dev, cell_ctxt_size, GFP_KERNEL);
if (!chain->cell_ctxt)
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.h
index c2c8b5f..2a6641ea 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.h
+++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.h
@@ -17,9 +17,22 @@
#define HINIC_HW_API_CMD_H
#include <linux/types.h>
+#include <linux/semaphore.h>
#include "hinic_hw_if.h"
+#define HINIC_API_CMD_PI_IDX_SHIFT 0
+
+#define HINIC_API_CMD_PI_IDX_MASK 0xFFFFFF
+
+#define HINIC_API_CMD_PI_SET(val, member) \
+ (((u32)(val) & HINIC_API_CMD_PI_##member##_MASK) << \
+ HINIC_API_CMD_PI_##member##_SHIFT)
+
+#define HINIC_API_CMD_PI_CLEAR(val, member) \
+ ((val) & (~(HINIC_API_CMD_PI_##member##_MASK \
+ << HINIC_API_CMD_PI_##member##_SHIFT)))
+
#define HINIC_API_CMD_CHAIN_REQ_RESTART_SHIFT 1
#define HINIC_API_CMD_CHAIN_REQ_RESTART_MASK 0x1
@@ -58,6 +71,56 @@
((val) & (~(HINIC_API_CMD_CHAIN_CTRL_##member##_MASK \
<< HINIC_API_CMD_CHAIN_CTRL_##member##_SHIFT)))
+#define HINIC_API_CMD_CELL_CTRL_DATA_SZ_SHIFT 0
+#define HINIC_API_CMD_CELL_CTRL_RD_DMA_ATTR_SHIFT 16
+#define HINIC_API_CMD_CELL_CTRL_WR_DMA_ATTR_SHIFT 24
+#define HINIC_API_CMD_CELL_CTRL_XOR_CHKSUM_SHIFT 56
+
+#define HINIC_API_CMD_CELL_CTRL_DATA_SZ_MASK 0x3F
+#define HINIC_API_CMD_CELL_CTRL_RD_DMA_ATTR_MASK 0x3F
+#define HINIC_API_CMD_CELL_CTRL_WR_DMA_ATTR_MASK 0x3F
+#define HINIC_API_CMD_CELL_CTRL_XOR_CHKSUM_MASK 0xFF
+
+#define HINIC_API_CMD_CELL_CTRL_SET(val, member) \
+ ((((u64)val) & HINIC_API_CMD_CELL_CTRL_##member##_MASK) << \
+ HINIC_API_CMD_CELL_CTRL_##member##_SHIFT)
+
+#define HINIC_API_CMD_DESC_API_TYPE_SHIFT 0
+#define HINIC_API_CMD_DESC_RD_WR_SHIFT 1
+#define HINIC_API_CMD_DESC_MGMT_BYPASS_SHIFT 2
+#define HINIC_API_CMD_DESC_DEST_SHIFT 32
+#define HINIC_API_CMD_DESC_SIZE_SHIFT 40
+#define HINIC_API_CMD_DESC_XOR_CHKSUM_SHIFT 56
+
+#define HINIC_API_CMD_DESC_API_TYPE_MASK 0x1
+#define HINIC_API_CMD_DESC_RD_WR_MASK 0x1
+#define HINIC_API_CMD_DESC_MGMT_BYPASS_MASK 0x1
+#define HINIC_API_CMD_DESC_DEST_MASK 0x1F
+#define HINIC_API_CMD_DESC_SIZE_MASK 0x7FF
+#define HINIC_API_CMD_DESC_XOR_CHKSUM_MASK 0xFF
+
+#define HINIC_API_CMD_DESC_SET(val, member) \
+ ((((u64)val) & HINIC_API_CMD_DESC_##member##_MASK) << \
+ HINIC_API_CMD_DESC_##member##_SHIFT)
+
+#define HINIC_API_CMD_STATUS_HEADER_CHAIN_ID_SHIFT 16
+
+#define HINIC_API_CMD_STATUS_HEADER_CHAIN_ID_MASK 0xFF
+
+#define HINIC_API_CMD_STATUS_HEADER_GET(val, member) \
+ (((val) >> HINIC_API_CMD_STATUS_HEADER_##member##_SHIFT) & \
+ HINIC_API_CMD_STATUS_HEADER_##member##_MASK)
+
+#define HINIC_API_CMD_STATUS_CONS_IDX_SHIFT 0
+#define HINIC_API_CMD_STATUS_CHKSUM_ERR_SHIFT 28
+
+#define HINIC_API_CMD_STATUS_CONS_IDX_MASK 0xFFFFFF
+#define HINIC_API_CMD_STATUS_CHKSUM_ERR_MASK 0x3
+
+#define HINIC_API_CMD_STATUS_GET(val, member) \
+ (((val) >> HINIC_API_CMD_STATUS_##member##_SHIFT) & \
+ HINIC_API_CMD_STATUS_##member##_MASK)
+
enum hinic_api_cmd_chain_type {
HINIC_API_CMD_WRITE_TO_MGMT_CPU = 2,
@@ -122,6 +185,8 @@ struct hinic_api_cmd_chain {
u32 prod_idx;
u32 cons_idx;
+ struct semaphore sem;
+
struct hinic_api_cmd_cell_ctxt *cell_ctxt;
dma_addr_t wb_status_paddr;
diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h
index 0c100ee..ebbf054 100644
--- a/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h
+++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h
@@ -56,6 +56,13 @@
#define HINIC_CSR_API_CMD_CHAIN_CTRL_ADDR(idx) \
(HINIC_CSR_API_CMD_BASE + 0x14 + (idx) * HINIC_CSR_API_CMD_STRIDE)
+#define HINIC_CSR_API_CMD_CHAIN_PI_ADDR(idx) \
+ (HINIC_CSR_API_CMD_BASE + 0x1C + (idx) * HINIC_CSR_API_CMD_STRIDE)
+
#define HINIC_CSR_API_CMD_CHAIN_REQ_ADDR(idx) \
(HINIC_CSR_API_CMD_BASE + 0x20 + (idx) * HINIC_CSR_API_CMD_STRIDE)
+
+#define HINIC_CSR_API_CMD_STATUS_ADDR(idx) \
+ (HINIC_CSR_API_CMD_BASE + 0x30 + (idx) * HINIC_CSR_API_CMD_STRIDE)
+
#endif
--
1.9.1
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web