net-next/hinic: Add api cmd commands
authorAviad Krawczyk <aviad.krawczyk@huawei.com>
Mon, 21 Aug 2017 15:55:52 +0000 (23:55 +0800)
committerDavid S. Miller <davem@davemloft.net>
Tue, 22 Aug 2017 17:48:53 +0000 (10:48 -0700)
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>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.c
drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.h
drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h

index 7e7a76e7d049d9fb50c5df2411c09548f871d16a..8901801fe4265eb6b9328c6cf7871551952a7d75 100644 (file)
@@ -25,7 +25,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"
 
 #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)
+{
+       enum hinic_api_cmd_chain_type chain_type = chain->chain_type;
+       struct hinic_hwif *hwif = chain->hwif;
+       u32 addr, prod_idx;
+
+       addr = HINIC_CSR_API_CMD_CHAIN_PI_ADDR(chain_type);
+       prod_idx = hinic_hwif_read_reg(hwif, 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, 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)
+{
+       u8 chksum;
+       u64 ctrl;
+
+       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
+ * @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,
+                           enum hinic_node_id dest,
+                           void *cmd, u16 cmd_size)
+{
+       struct hinic_api_cmd_cell *cell = chain->curr_node;
+       struct hinic_api_cmd_cell_ctxt *cell_ctxt;
+       struct hinic_hwif *hwif = chain->hwif;
+       struct pci_dev *pdev = hwif->pdev;
+
+       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, 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)
+{
+       enum hinic_api_cmd_chain_type chain_type;
+       struct hinic_api_cmd_status *wb_status;
+       struct hinic_hwif *hwif = chain->hwif;
+       struct pci_dev *pdev = hwif->pdev;
+       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)
+{
+       int err = -ETIMEDOUT;
+       unsigned long end;
+
+       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
@@ -65,8 +366,30 @@ enum api_cmd_xor_chk_level {
 static int api_cmd(struct hinic_api_cmd_chain *chain,
                   enum hinic_node_id dest, u8 *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;
 }
 
 /**
@@ -491,6 +814,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)
index e8865d627b5867f58d647d4161d30c67c9b07804..31b94d5d47f775b772f4996ddd5f35f2249e43ce 100644 (file)
 #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
        ((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;
index a9ece6db3414837c08bd917ea5826e26d2d3fdb3..ebbf0549404cd625908a1efe194a910809fb8c81 100644 (file)
 #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