bnx2x: Support of PF driver of a VF init request
authorAriel Elior <ariele@broadcom.com>
Tue, 1 Jan 2013 05:22:35 +0000 (05:22 +0000)
committerDavid S. Miller <davem@davemloft.net>
Wed, 2 Jan 2013 09:45:06 +0000 (01:45 -0800)
The VF driver will send an 'init' request as part of its nic load
flow. This message is used by the VF to publish the GPA's of its
status blocks, slow path ring and statistics buffer.
The PF driver notes all this down in the VF database, and also uses
this message to transfer the VF to VF_INIT state internally.

Signed-off-by: Ariel Elior <ariele@broadcom.com>
Signed-off-by: Eilon Greenstein <eilong@broadcom.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_reg.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_vfpf.c

index 5219373..d096e3e 100644 (file)
@@ -1852,6 +1852,8 @@ int bnx2x_del_all_macs(struct bnx2x *bp,
 
 /* Init Function API  */
 void bnx2x_func_init(struct bnx2x *bp, struct bnx2x_func_init_params *p);
+void bnx2x_init_sb(struct bnx2x *bp, dma_addr_t mapping, int vfid,
+                   u8 vf_valid, int fw_sb_id, int igu_sb_id);
 u32 bnx2x_get_pretend_reg(struct bnx2x *bp);
 int bnx2x_get_gpio(struct bnx2x *bp, int gpio_num, u8 port);
 int bnx2x_set_gpio(struct bnx2x *bp, int gpio_num, u32 mode, u8 port);
index e6738f6..0fae563 100644 (file)
@@ -5420,7 +5420,7 @@ static void bnx2x_map_sb_state_machines(struct hc_index_data *index_data)
                SM_TX_ID << HC_INDEX_DATA_SM_ID_SHIFT;
 }
 
-static void bnx2x_init_sb(struct bnx2x *bp, dma_addr_t mapping, int vfid,
+void bnx2x_init_sb(struct bnx2x *bp, dma_addr_t mapping, int vfid,
                          u8 vf_valid, int fw_sb_id, int igu_sb_id)
 {
        int igu_seg_id;
index cefe448..1008d3f 100644 (file)
 #define PXP_REG_HST_DISCARD_INTERNAL_WRITES_STATUS              0x10309c
 /* [WB 160] Used for initialization of the inbound interrupts memory */
 #define PXP_REG_HST_INBOUND_INT                                 0x103800
+/* [RW 7] Indirect access to the permission table. The fields are : {Valid;
+ * VFID[5:0]}
+ */
+#define PXP_REG_HST_ZONE_PERMISSION_TABLE                       0x103400
 /* [RW 32] Interrupt mask register #0 read/write */
 #define PXP_REG_PXP_INT_MASK_0                                  0x103074
 #define PXP_REG_PXP_INT_MASK_1                                  0x103084
index de42f66..14e49bc 100644 (file)
@@ -66,6 +66,41 @@ struct bnx2x_virtf *bnx2x_vf_by_abs_fid(struct bnx2x *bp, u16 abs_vfid)
        return (idx < BNX2X_NR_VIRTFN(bp)) ? BP_VF(bp, idx) : NULL;
 }
 
+static void bnx2x_vf_igu_ack_sb(struct bnx2x *bp, struct bnx2x_virtf *vf,
+                               u8 igu_sb_id, u8 segment, u16 index, u8 op,
+                               u8 update)
+{
+       /* acking a VF sb through the PF - use the GRC */
+       u32 ctl;
+       u32 igu_addr_data = IGU_REG_COMMAND_REG_32LSB_DATA;
+       u32 igu_addr_ctl = IGU_REG_COMMAND_REG_CTRL;
+       u32 func_encode = vf->abs_vfid;
+       u32 addr_encode = IGU_CMD_E2_PROD_UPD_BASE + igu_sb_id;
+       struct igu_regular cmd_data = {0};
+
+       cmd_data.sb_id_and_flags =
+                       ((index << IGU_REGULAR_SB_INDEX_SHIFT) |
+                        (segment << IGU_REGULAR_SEGMENT_ACCESS_SHIFT) |
+                        (update << IGU_REGULAR_BUPDATE_SHIFT) |
+                        (op << IGU_REGULAR_ENABLE_INT_SHIFT));
+
+       ctl = addr_encode << IGU_CTRL_REG_ADDRESS_SHIFT         |
+             func_encode << IGU_CTRL_REG_FID_SHIFT             |
+             IGU_CTRL_CMD_TYPE_WR << IGU_CTRL_REG_TYPE_SHIFT;
+
+       DP(NETIF_MSG_HW, "write 0x%08x to IGU(via GRC) addr 0x%x\n",
+          cmd_data.sb_id_and_flags, igu_addr_data);
+       REG_WR(bp, igu_addr_data, cmd_data.sb_id_and_flags);
+       mmiowb();
+       barrier();
+
+       DP(NETIF_MSG_HW, "write 0x%08x to IGU(via GRC) addr 0x%x\n",
+          ctl, igu_addr_ctl);
+       REG_WR(bp, igu_addr_ctl, ctl);
+       mmiowb();
+       barrier();
+}
+
 static int bnx2x_ari_enabled(struct pci_dev *dev)
 {
        return dev->bus->self && dev->bus->self->ari_enabled;
@@ -364,6 +399,52 @@ static void bnx2x_vf_pglue_clear_err(struct bnx2x *bp, u8 abs_vfid)
        REG_WR(bp, was_err_reg, 1 << (abs_vfid & 0x1f));
 }
 
+static void bnx2x_vf_igu_reset(struct bnx2x *bp, struct bnx2x_virtf *vf)
+{
+       int i;
+       u32 val;
+
+       /* Set VF masks and configuration - pretend */
+       bnx2x_pretend_func(bp, HW_VF_HANDLE(bp, vf->abs_vfid));
+
+       REG_WR(bp, IGU_REG_SB_INT_BEFORE_MASK_LSB, 0);
+       REG_WR(bp, IGU_REG_SB_INT_BEFORE_MASK_MSB, 0);
+       REG_WR(bp, IGU_REG_SB_MASK_LSB, 0);
+       REG_WR(bp, IGU_REG_SB_MASK_MSB, 0);
+       REG_WR(bp, IGU_REG_PBA_STATUS_LSB, 0);
+       REG_WR(bp, IGU_REG_PBA_STATUS_MSB, 0);
+
+       val = REG_RD(bp, IGU_REG_VF_CONFIGURATION);
+       val |= (IGU_VF_CONF_FUNC_EN | IGU_VF_CONF_MSI_MSIX_EN);
+       if (vf->cfg_flags & VF_CFG_INT_SIMD)
+               val |= IGU_VF_CONF_SINGLE_ISR_EN;
+       val &= ~IGU_VF_CONF_PARENT_MASK;
+       val |= BP_FUNC(bp) << IGU_VF_CONF_PARENT_SHIFT; /* parent PF */
+       REG_WR(bp, IGU_REG_VF_CONFIGURATION, val);
+
+       DP(BNX2X_MSG_IOV,
+          "value in IGU_REG_VF_CONFIGURATION of vf %d after write %x\n",
+          vf->abs_vfid, REG_RD(bp, IGU_REG_VF_CONFIGURATION));
+
+       bnx2x_pretend_func(bp, BP_ABS_FUNC(bp));
+
+       /* iterate over all queues, clear sb consumer */
+       for (i = 0; i < vf_sb_count(vf); i++) {
+               u8 igu_sb_id = vf_igu_sb(vf, i);
+
+               /* zero prod memory */
+               REG_WR(bp, IGU_REG_PROD_CONS_MEMORY + igu_sb_id * 4, 0);
+
+               /* clear sb state machine */
+               bnx2x_igu_clear_sb_gen(bp, vf->abs_vfid, igu_sb_id,
+                                      false /* VF */);
+
+               /* disable + update */
+               bnx2x_vf_igu_ack_sb(bp, vf, igu_sb_id, USTORM_ID, 0,
+                                   IGU_INT_DISABLE, 1);
+       }
+}
+
 void bnx2x_vf_enable_access(struct bnx2x *bp, u8 abs_vfid)
 {
        /* set the VF-PF association in the FW */
@@ -381,6 +462,17 @@ void bnx2x_vf_enable_access(struct bnx2x *bp, u8 abs_vfid)
        bnx2x_pretend_func(bp, BP_ABS_FUNC(bp));
 }
 
+static void bnx2x_vf_enable_traffic(struct bnx2x *bp, struct bnx2x_virtf *vf)
+{
+       /* Reset vf in IGU  interrupts are still disabled */
+       bnx2x_vf_igu_reset(bp, vf);
+
+       /* pretend to enable the vf with the PBF */
+       bnx2x_pretend_func(bp, HW_VF_HANDLE(bp, vf->abs_vfid));
+       REG_WR(bp, PBF_REG_DISABLE_VF, 0);
+       bnx2x_pretend_func(bp, BP_ABS_FUNC(bp));
+}
+
 static u8 bnx2x_vf_is_pcie_pending(struct bnx2x *bp, u8 abs_vfid)
 {
        struct pci_dev *dev;
@@ -997,6 +1089,14 @@ void bnx2x_iov_sp_task(struct bnx2x *bp)
                }
        }
 }
+static void bnx2x_vf_qtbl_set_q(struct bnx2x *bp, u8 abs_vfid, u8 qid,
+                               u8 enable)
+{
+       u32 reg = PXP_REG_HST_ZONE_PERMISSION_TABLE + qid * 4;
+       u32 val = enable ? (abs_vfid | (1 << 6)) : 0;
+
+       REG_WR(bp, reg, val);
+}
 
 u8 bnx2x_vf_max_queue_cnt(struct bnx2x *bp, struct bnx2x_virtf *vf)
 {
@@ -1108,6 +1208,65 @@ int bnx2x_vf_acquire(struct bnx2x *bp, struct bnx2x_virtf *vf,
        return 0;
 }
 
+int bnx2x_vf_init(struct bnx2x *bp, struct bnx2x_virtf *vf, dma_addr_t *sb_map)
+{
+       struct bnx2x_func_init_params func_init = {0};
+       u16 flags = 0;
+       int i;
+
+       /* the sb resources are initialized at this point, do the
+        * FW/HW initializations
+        */
+       for_each_vf_sb(vf, i)
+               bnx2x_init_sb(bp, (dma_addr_t)sb_map[i], vf->abs_vfid, true,
+                             vf_igu_sb(vf, i), vf_igu_sb(vf, i));
+
+       /* Sanity checks */
+       if (vf->state != VF_ACQUIRED) {
+               DP(BNX2X_MSG_IOV, "VF[%d] is not in VF_ACQUIRED, but %d\n",
+                  vf->abs_vfid, vf->state);
+               return -EINVAL;
+       }
+       /* FLR cleanup epilogue */
+       if (bnx2x_vf_flr_clnup_epilog(bp, vf->abs_vfid))
+               return -EBUSY;
+
+       /* reset IGU VF statistics: MSIX */
+       REG_WR(bp, IGU_REG_STATISTIC_NUM_MESSAGE_SENT + vf->abs_vfid * 4 , 0);
+
+       /* vf init */
+       if (vf->cfg_flags & VF_CFG_STATS)
+               flags |= (FUNC_FLG_STATS | FUNC_FLG_SPQ);
+
+       if (vf->cfg_flags & VF_CFG_TPA)
+               flags |= FUNC_FLG_TPA;
+
+       if (is_vf_multi(vf))
+               flags |= FUNC_FLG_RSS;
+
+       /* function setup */
+       func_init.func_flgs = flags;
+       func_init.pf_id = BP_FUNC(bp);
+       func_init.func_id = FW_VF_HANDLE(vf->abs_vfid);
+       func_init.fw_stat_map = vf->fw_stat_map;
+       func_init.spq_map = vf->spq_map;
+       func_init.spq_prod = 0;
+       bnx2x_func_init(bp, &func_init);
+
+       /* Enable the vf */
+       bnx2x_vf_enable_access(bp, vf->abs_vfid);
+       bnx2x_vf_enable_traffic(bp, vf);
+
+       /* queue protection table */
+       for_each_vfq(vf, i)
+               bnx2x_vf_qtbl_set_q(bp, vf->abs_vfid,
+                                   vfq_qzone_id(vf, vfq_get(vf, i)), true);
+
+       vf->state = VF_ENABLED;
+
+       return 0;
+}
+
 void bnx2x_lock_vf_pf_channel(struct bnx2x *bp, struct bnx2x_virtf *vf,
                              enum channel_tlvs tlv)
 {
index 5248ebe..6dac57e 100644 (file)
@@ -267,6 +267,8 @@ struct bnx2x_virtf {
 #define for_each_vf_sb(vf, var) \
                for ((var) = 0; (var) < vf_sb_count(vf); (var)++)
 
+#define is_vf_multi(vf)        (vf_rxq_count(vf) > 1)
+
 #define HW_VF_HANDLE(bp, abs_vfid) \
        (u16)(BP_ABS_FUNC((bp)) | (1<<3) |  ((u16)(abs_vfid) << 4))
 
@@ -427,8 +429,10 @@ void bnx2x_vf_mbx(struct bnx2x *bp, struct vf_pf_event_data *vfpf_event);
 void bnx2x_vf_enable_mbx(struct bnx2x *bp, u8 abs_vfid);
 /* acquire */
 int bnx2x_vf_acquire(struct bnx2x *bp, struct bnx2x_virtf *vf,
-                     struct vf_pf_resc_request *resc);
-
+                    struct vf_pf_resc_request *resc);
+/* init */
+int bnx2x_vf_init(struct bnx2x *bp, struct bnx2x_virtf *vf,
+                 dma_addr_t *sb_map);
 static inline struct bnx2x_vfop *bnx2x_vfop_cur(struct bnx2x *bp,
                                                struct bnx2x_virtf *vf)
 {
index 3b6b577..7aa0e4f 100644 (file)
@@ -19,6 +19,7 @@
 
 #include "bnx2x.h"
 #include "bnx2x_sriov.h"
+#include <linux/crc32.h>
 
 /* place a given tlv on the tlv buffer at a given offset */
 void bnx2x_add_tlv(struct bnx2x *bp, void *tlvs_list, u16 offset, u16 type,
@@ -355,6 +356,20 @@ static void bnx2x_vf_mbx_acquire(struct bnx2x *bp, struct bnx2x_virtf *vf,
        bnx2x_vf_mbx_acquire_resp(bp, vf, mbx, rc);
 }
 
+static void bnx2x_vf_mbx_init_vf(struct bnx2x *bp, struct bnx2x_virtf *vf,
+                             struct bnx2x_vf_mbx *mbx)
+{
+       struct vfpf_init_tlv *init = &mbx->msg->req.init;
+
+       /* record ghost addresses from vf message */
+       vf->spq_map = init->spq_addr;
+       vf->fw_stat_map = init->stats_addr;
+       vf->op_rc = bnx2x_vf_init(bp, vf, (dma_addr_t *)init->sb_addr);
+
+       /* response */
+       bnx2x_vf_mbx_resp(bp, vf);
+}
+
 /* dispatch request */
 static void bnx2x_vf_mbx_request(struct bnx2x *bp, struct bnx2x_virtf *vf,
                                  struct bnx2x_vf_mbx *mbx)
@@ -373,6 +388,9 @@ static void bnx2x_vf_mbx_request(struct bnx2x *bp, struct bnx2x_virtf *vf,
                case CHANNEL_TLV_ACQUIRE:
                        bnx2x_vf_mbx_acquire(bp, vf, mbx);
                        break;
+               case CHANNEL_TLV_INIT:
+                       bnx2x_vf_mbx_init_vf(bp, vf, mbx);
+                       break;
                }
        } else {
                /* unknown TLV - this may belong to a VF driver from the future