obj-$(CONFIG_BNX2X) += bnx2x.o
 
-bnx2x-objs := bnx2x_main.o bnx2x_link.o bnx2x_cmn.o bnx2x_ethtool.o bnx2x_stats.o bnx2x_dcb.o bnx2x_sp.o
+bnx2x-objs := bnx2x_main.o bnx2x_link.o bnx2x_cmn.o bnx2x_ethtool.o bnx2x_stats.o bnx2x_dcb.o bnx2x_sp.o bnx2x_vfpf.o
 
 #include "bnx2x_sp.h"
 #include "bnx2x_dcb.h"
 #include "bnx2x_stats.h"
+#include "bnx2x_vfpf.h"
 
 enum bnx2x_int_mode {
        BNX2X_INT_MODE_MSIX,
        struct bnx2x_vf_mbx_msg *vf2pf_mbox;
        dma_addr_t              vf2pf_mbox_mapping;
 
+       /* we set aside a copy of the acquire response */
+       struct pfvf_acquire_resp_tlv acquire_resp;
+
        struct net_device       *dev;
        struct pci_dev          *pdev;
 
 #define BNX2X_VPD_LEN                  128
 #define VENDOR_ID_LEN                  4
 
+#define VF_ACQUIRE_THRESH              3
+#define VF_ACQUIRE_MAC_FILTERS         1
+#define VF_ACQUIRE_MC_FILTERS          10
+
+#define GOOD_ME_REG(me_reg) (((me_reg) & ME_REG_VF_VALID) && \
+                           (!((me_reg) & ME_REG_VF_ERR)))
+int bnx2x_get_vf_id(struct bnx2x *bp, u32 *vf_id);
+int bnx2x_send_msg2pf(struct bnx2x *bp, u8 *done, dma_addr_t msg_mapping);
+int bnx2x_vfpf_acquire(struct bnx2x *bp, u8 tx_count, u8 rx_count);
 /* Congestion management fairness mode */
 #define CMNG_FNS_NONE          0
 #define CMNG_FNS_MINMAX                1
 
                goto init_one_exit;
        }
 
+       if (IS_VF(bp)) {
+               rc = bnx2x_vfpf_acquire(bp, tx_count, rx_count);
+               if (rc)
+                       goto init_one_exit;
+       }
+
        /* calc qm_cid_count */
        bp->qm_cid_count = bnx2x_set_qm_cid_count(bp);
        BNX2X_DEV_INFO("qm_cid_count %d\n", bp->qm_cid_count);
        return cp;
 }
 
+int bnx2x_send_msg2pf(struct bnx2x *bp, u8 *done, dma_addr_t msg_mapping)
+{
+       struct cstorm_vf_zone_data __iomem *zone_data =
+               REG_ADDR(bp, PXP_VF_ADDR_CSDM_GLOBAL_START);
+       int tout = 600, interval = 100; /* wait for 60 seconds */
+
+       if (*done) {
+               BNX2X_ERR("done was non zero before message to pf was sent\n");
+               WARN_ON(true);
+               return -EINVAL;
+       }
+
+       /* Write message address */
+       writel(U64_LO(msg_mapping),
+              &zone_data->non_trigger.vf_pf_channel.msg_addr_lo);
+       writel(U64_HI(msg_mapping),
+              &zone_data->non_trigger.vf_pf_channel.msg_addr_hi);
+
+       /* make sure the address is written before FW accesses it */
+       wmb();
+
+       /* Trigger the PF FW */
+       writeb(1, &zone_data->trigger.vf_pf_channel.addr_valid);
+
+       /* Wait for PF to complete */
+       while ((tout >= 0) && (!*done)) {
+               msleep(interval);
+               tout -= 1;
+
+               /* progress indicator - HV can take its own sweet time in
+                * answering VFs...
+                */
+               DP_CONT(BNX2X_MSG_IOV, ".");
+       }
+
+       if (!*done) {
+               BNX2X_ERR("PF response has timed out\n");
+               return -EAGAIN;
+       }
+       DP(BNX2X_MSG_SP, "Got a response from PF\n");
+       return 0;
+}
+
+int bnx2x_get_vf_id(struct bnx2x *bp, u32 *vf_id)
+{
+       u32 me_reg;
+       int tout = 10, interval = 100; /* Wait for 1 sec */
+
+       do {
+               /* pxp traps vf read of doorbells and returns me reg value */
+               me_reg = readl(bp->doorbells);
+               if (GOOD_ME_REG(me_reg))
+                       break;
+
+               msleep(interval);
+
+               BNX2X_ERR("Invalid ME register value: 0x%08x\n. Is pf driver up?",
+                         me_reg);
+       } while (tout-- > 0);
+
+       if (!GOOD_ME_REG(me_reg)) {
+               BNX2X_ERR("Invalid ME register value: 0x%08x\n", me_reg);
+               return -EINVAL;
+       }
+
+       BNX2X_ERR("valid ME register value: 0x%08x\n", me_reg);
+
+       *vf_id = (me_reg & ME_REG_VF_NUM_MASK) >> ME_REG_VF_NUM_SHIFT;
 
+       return 0;
+}
+
+int bnx2x_vfpf_acquire(struct bnx2x *bp, u8 tx_count, u8 rx_count)
+{
+       int rc = 0, attempts = 0;
+       struct vfpf_acquire_tlv *req = &bp->vf2pf_mbox->req.acquire;
+       struct pfvf_acquire_resp_tlv *resp = &bp->vf2pf_mbox->resp.acquire_resp;
+       u32 vf_id;
+       bool resources_acquired = false;
+
+       /* clear mailbox and prep first tlv */
+       bnx2x_vfpf_prep(bp, &req->first_tlv, CHANNEL_TLV_ACQUIRE, sizeof(*req));
+
+       if (bnx2x_get_vf_id(bp, &vf_id))
+               return -EAGAIN;
+
+       req->vfdev_info.vf_id = vf_id;
+       req->vfdev_info.vf_os = 0;
+
+       req->resc_request.num_rxqs = rx_count;
+       req->resc_request.num_txqs = tx_count;
+       req->resc_request.num_sbs = bp->igu_sb_cnt;
+       req->resc_request.num_mac_filters = VF_ACQUIRE_MAC_FILTERS;
+       req->resc_request.num_mc_filters = VF_ACQUIRE_MC_FILTERS;
+
+       /* add list termination tlv */
+       bnx2x_add_tlv(bp, req, req->first_tlv.tl.length, CHANNEL_TLV_LIST_END,
+                     sizeof(struct channel_list_end_tlv));
+
+       /* output tlvs list */
+       bnx2x_dp_tlv_list(bp, req);
+
+       while (!resources_acquired) {
+               DP(BNX2X_MSG_SP, "attempting to acquire resources\n");
+
+               /* send acquire request */
+               rc = bnx2x_send_msg2pf(bp,
+                                      &resp->hdr.status,
+                                      bp->vf2pf_mbox_mapping);
+
+               /* PF timeout */
+               if (rc)
+                       return rc;
+
+               /* copy acquire response from buffer to bp */
+               memcpy(&bp->acquire_resp, resp, sizeof(bp->acquire_resp));
+
+               attempts++;
+
+               /* test whether the PF accepted our request. If not, humble the
+                * the request and try again.
+                */
+               if (bp->acquire_resp.hdr.status == PFVF_STATUS_SUCCESS) {
+                       DP(BNX2X_MSG_SP, "resources acquired\n");
+                       resources_acquired = true;
+               } else if (bp->acquire_resp.hdr.status ==
+                          PFVF_STATUS_NO_RESOURCE &&
+                          attempts < VF_ACQUIRE_THRESH) {
+                       DP(BNX2X_MSG_SP,
+                          "PF unwilling to fulfill resource request. Try PF recommended amount\n");
+
+                       /* humble our request */
+                       req->resc_request.num_txqs =
+                               bp->acquire_resp.resc.num_txqs;
+                       req->resc_request.num_rxqs =
+                               bp->acquire_resp.resc.num_rxqs;
+                       req->resc_request.num_sbs =
+                               bp->acquire_resp.resc.num_sbs;
+                       req->resc_request.num_mac_filters =
+                               bp->acquire_resp.resc.num_mac_filters;
+                       req->resc_request.num_vlan_filters =
+                               bp->acquire_resp.resc.num_vlan_filters;
+                       req->resc_request.num_mc_filters =
+                               bp->acquire_resp.resc.num_mc_filters;
+
+                       /* Clear response buffer */
+                       memset(&bp->vf2pf_mbox->resp, 0,
+                              sizeof(union pfvf_tlvs));
+               } else {
+                       /* PF reports error */
+                       BNX2X_ERR("Failed to get the requested amount of resources: %d. Breaking...\n",
+                                 bp->acquire_resp.hdr.status);
+                       return -EAGAIN;
+               }
+       }
+
+       /* get HW info */
+       bp->common.chip_id |= (bp->acquire_resp.pfdev_info.chip_num & 0xffff);
+       bp->link_params.chip_id = bp->common.chip_id;
+       bp->db_size = bp->acquire_resp.pfdev_info.db_size;
+       bp->common.int_block = INT_BLOCK_IGU;
+       bp->common.chip_port_mode = CHIP_2_PORT_MODE;
+       bp->igu_dsb_id = -1;
+       bp->mf_ov = 0;
+       bp->mf_mode = 0;
+       bp->common.flash_size = 0;
+       bp->flags |=
+               NO_WOL_FLAG | NO_ISCSI_OOO_FLAG | NO_ISCSI_FLAG | NO_FCOE_FLAG;
+       bp->igu_sb_cnt = 1;
+       bp->igu_base_sb = bp->acquire_resp.resc.hw_sbs[0].hw_sb_id;
+       strlcpy(bp->fw_ver, bp->acquire_resp.pfdev_info.fw_ver,
+               sizeof(bp->fw_ver));
+
+       if (is_valid_ether_addr(bp->acquire_resp.resc.current_mac_addr))
+               memcpy(bp->dev->dev_addr,
+                      bp->acquire_resp.resc.current_mac_addr,
+                      ETH_ALEN);
+
+       return 0;
+}
 
 #define PXP_VF_ADDR_IGU_SIZE                           0x3000
 #define PXP_VF_ADDR_IGU_END\
        ((PXP_VF_ADDR_IGU_START) + (PXP_VF_ADDR_IGU_SIZE) - 1)
+
+#define PXP_VF_ADDR_CSDM_GLOBAL_START                  0x7600
+#define PXP_VF_ADDR_CSDM_GLOBAL_SIZE                   (PXP_ADDR_REG_SIZE)
+#define PXP_VF_ADDR_CSDM_GLOBAL_END\
+       ((PXP_VF_ADDR_CSDM_GLOBAL_START) + (PXP_VF_ADDR_CSDM_GLOBAL_SIZE) - 1)
+
 #define PXP_VF_ADDR_DB_START                           0x7c00
 #define PXP_VF_ADDR_DB_SIZE                            0x200
 #define PXP_VF_ADDR_DB_END\
 
        union pfvf_tlvs resp;
 };
 
+void bnx2x_add_tlv(struct bnx2x *bp, void *tlvs_list, u16 offset, u16 type,
+                  u16 length);
+void bnx2x_vfpf_prep(struct bnx2x *bp, struct vfpf_first_tlv *first_tlv,
+                    u16 type, u16 length);
+void bnx2x_dp_tlv_list(struct bnx2x *bp, void *tlvs_list);
 #endif /* bnx2x_sriov.h */
 
--- /dev/null
+/* bnx2x_vfpf.c: Broadcom Everest network driver.
+ *
+ * Copyright 2009-2012 Broadcom Corporation
+ *
+ * Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2, available
+ * at http://www.gnu.org/licenses/old-licenses/gpl-2.0.html (the "GPL").
+ *
+ * Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a
+ * license other than the GPL, without Broadcom's express prior written
+ * consent.
+ *
+ * Maintained by: Eilon Greenstein <eilong@broadcom.com>
+ * Written by: Shmulik Ravid <shmulikr@broadcom.com>
+ *            Ariel Elior <ariele@broadcom.com>
+ */
+
+#include "bnx2x.h"
+#include "bnx2x_sriov.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,
+                  u16 length)
+{
+       struct channel_tlv *tl =
+               (struct channel_tlv *)(tlvs_list + offset);
+
+       tl->type = type;
+       tl->length = length;
+}
+
+/* Clear the mailbox and init the header of the first tlv */
+void bnx2x_vfpf_prep(struct bnx2x *bp, struct vfpf_first_tlv *first_tlv,
+                    u16 type, u16 length)
+{
+       DP(BNX2X_MSG_IOV, "preparing to send %d tlv over vf pf channel\n",
+          type);
+
+       /* Clear mailbox */
+       memset(bp->vf2pf_mbox, 0, sizeof(struct bnx2x_vf_mbx_msg));
+
+       /* init type and length */
+       bnx2x_add_tlv(bp, &first_tlv->tl, 0, type, length);
+
+       /* init first tlv header */
+       first_tlv->resp_msg_offset = sizeof(bp->vf2pf_mbox->req);
+}
+
+/* list the types and lengths of the tlvs on the buffer */
+void bnx2x_dp_tlv_list(struct bnx2x *bp, void *tlvs_list)
+{
+       int i = 1;
+       struct channel_tlv *tlv = (struct channel_tlv *)tlvs_list;
+
+       while (tlv->type != CHANNEL_TLV_LIST_END) {
+               /* output tlv */
+               DP(BNX2X_MSG_IOV, "TLV number %d: type %d, length %d\n", i,
+                  tlv->type, tlv->length);
+
+               /* advance to next tlv */
+               tlvs_list += tlv->length;
+
+               /* cast general tlv list pointer to channel tlv header*/
+               tlv = (struct channel_tlv *)tlvs_list;
+
+               i++;
+
+               /* break condition for this loop */
+               if (i > MAX_TLVS_IN_LIST) {
+                       WARN(true, "corrupt tlvs");
+                       return;
+               }
+       }
+
+       /* output last tlv */
+       DP(BNX2X_MSG_IOV, "TLV number %d: type %d, length %d\n", i,
+          tlv->type, tlv->length);
+}
 
 #ifndef VF_PF_IF_H
 #define VF_PF_IF_H
 
+/* Common definitions for all HVs */
+struct vf_pf_resc_request {
+       u8  num_rxqs;
+       u8  num_txqs;
+       u8  num_sbs;
+       u8  num_mac_filters;
+       u8  num_vlan_filters;
+       u8  num_mc_filters; /* No limit  so superfluous */
+};
+
+struct hw_sb_info {
+       u8 hw_sb_id;    /* aka absolute igu id, used to ack the sb */
+       u8 sb_qid;      /* used to update DHC for sb */
+};
+
 /* HW VF-PF channel definitions
  * A.K.A VF-PF mailbox
  */
 #define TLV_BUFFER_SIZE                        1024
 
+enum {
+       PFVF_STATUS_WAITING = 0,
+       PFVF_STATUS_SUCCESS,
+       PFVF_STATUS_FAILURE,
+       PFVF_STATUS_NOT_SUPPORTED,
+       PFVF_STATUS_NO_RESOURCE
+};
+
+/* vf pf channel tlvs */
+/* general tlv header (used for both vf->pf request and pf->vf response) */
+struct channel_tlv {
+       u16 type;
+       u16 length;
+};
+
+/* header of first vf->pf tlv carries the offset used to calculate response
+ * buffer address
+ */
+struct vfpf_first_tlv {
+       struct channel_tlv tl;
+       u32 resp_msg_offset;
+};
+
+/* header of pf->vf tlvs, carries the status of handling the request */
+struct pfvf_tlv {
+       struct channel_tlv tl;
+       u8 status;
+       u8 padding[3];
+};
+
+/* used to terminate and pad a tlv list */
+struct channel_list_end_tlv {
+       struct channel_tlv tl;
+       u8 padding[4];
+};
+
+/* Acquire */
+struct vfpf_acquire_tlv {
+       struct vfpf_first_tlv first_tlv;
+
+       struct vf_pf_vfdev_info {
+               /* the following fields are for debug purposes */
+               u8  vf_id;              /* ME register value */
+               u8  vf_os;              /* e.g. Linux, W2K8 */
+               u8 padding[2];
+       } vfdev_info;
+
+       struct vf_pf_resc_request resc_request;
+
+       aligned_u64 bulletin_addr;
+};
+
+/* acquire response tlv - carries the allocated resources */
+struct pfvf_acquire_resp_tlv {
+       struct pfvf_tlv hdr;
+       struct pf_vf_pfdev_info {
+               u32 chip_num;
+               u32 pf_cap;
+#define PFVF_CAP_RSS           0x00000001
+#define PFVF_CAP_DHC           0x00000002
+#define PFVF_CAP_TPA           0x00000004
+               char fw_ver[32];
+               u16 db_size;
+               u8  indices_per_sb;
+               u8  padding;
+       } pfdev_info;
+       struct pf_vf_resc {
+               /* in case of status NO_RESOURCE in message hdr, pf will fill
+                * this struct with suggested amount of resources for next
+                * acquire request
+                */
+#define PFVF_MAX_QUEUES_PER_VF         16
+#define PFVF_MAX_SBS_PER_VF            16
+               struct hw_sb_info hw_sbs[PFVF_MAX_SBS_PER_VF];
+               u8      hw_qid[PFVF_MAX_QUEUES_PER_VF];
+               u8      num_rxqs;
+               u8      num_txqs;
+               u8      num_sbs;
+               u8      num_mac_filters;
+               u8      num_vlan_filters;
+               u8      num_mc_filters;
+               u8      permanent_mac_addr[ETH_ALEN];
+               u8      current_mac_addr[ETH_ALEN];
+               u8      padding[2];
+       } resc;
+};
+
 struct tlv_buffer_size {
        u8 tlv_buffer[TLV_BUFFER_SIZE];
 };
 
 union vfpf_tlvs {
+       struct vfpf_first_tlv           first_tlv;
+       struct vfpf_acquire_tlv         acquire;
+       struct channel_list_end_tlv     list_end;
        struct tlv_buffer_size          tlv_buf_size;
 };
 
 union pfvf_tlvs {
+       struct pfvf_acquire_resp_tlv    acquire_resp;
+       struct channel_list_end_tlv     list_end;
        struct tlv_buffer_size          tlv_buf_size;
 };
+
+#define MAX_TLVS_IN_LIST 50
+
+enum channel_tlvs {
+       CHANNEL_TLV_NONE,
+       CHANNEL_TLV_ACQUIRE,
+       CHANNEL_TLV_LIST_END,
+       CHANNEL_TLV_MAX
+};
+
 #endif /* VF_PF_IF_H */