.release = seq_release_private
 };
 
+static int resources_show(struct seq_file *seq, void *v)
+{
+       struct adapter *adapter = seq->private;
+       struct pf_resources *pfres = &adapter->params.pfres;
+
+       #define S(desc, fmt, var) \
+               seq_printf(seq, "%-60s " fmt "\n", \
+                          desc " (" #var "):", pfres->var)
+
+       S("Virtual Interfaces", "%d", nvi);
+       S("Egress Queues", "%d", neq);
+       S("Ethernet Control", "%d", nethctrl);
+       S("Ingress Queues/w Free Lists/Interrupts", "%d", niqflint);
+       S("Ingress Queues", "%d", niq);
+       S("Traffic Class", "%d", tc);
+       S("Port Access Rights Mask", "%#x", pmask);
+       S("MAC Address Filters", "%d", nexactf);
+       S("Firmware Command Read Capabilities", "%#x", r_caps);
+       S("Firmware Command Write/Execute Capabilities", "%#x", wx_caps);
+
+       #undef S
+
+       return 0;
+}
+
+static int resources_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, resources_show, inode->i_private);
+}
+
+static const struct file_operations resources_debugfs_fops = {
+       .owner   = THIS_MODULE,
+       .open    = resources_open,
+       .read    = seq_read,
+       .llseek  = seq_lseek,
+       .release = seq_release,
+};
+
 /**
  * ethqset2pinfo - return port_info of an Ethernet Queue Set
  * @adap: the adapter
                { "rss_key", &rss_key_debugfs_fops, 0400, 0 },
                { "rss_pf_config", &rss_pf_config_debugfs_fops, 0400, 0 },
                { "rss_vf_config", &rss_vf_config_debugfs_fops, 0400, 0 },
+               { "resources", &resources_debugfs_fops, 0400, 0 },
                { "sge_qinfo", &sge_qinfo_debugfs_fops, 0400, 0 },
                { "ibq_tp0",  &cim_ibq_fops, 0400, 0 },
                { "ibq_tp1",  &cim_ibq_fops, 0400, 1 },
 
                     QUEUENUMBER_V(s->ethrxq[0].rspq.abs_id));
        return 0;
 freeout:
+       dev_err(adap->pdev_dev, "Can't allocate queues, err=%d\n", -err);
        t4_free_sge_resources(adap);
        return err;
 }
        u32 v;
        int ret;
 
+       /* Now that we've successfully configured and initialized the adapter
+        * can ask the Firmware what resources it has provisioned for us.
+        */
+       ret = t4_get_pfres(adap);
+       if (ret) {
+               dev_err(adap->pdev_dev,
+                       "Unable to retrieve resource provisioning information\n");
+               return ret;
+       }
+
        /* get device capabilities */
        memset(c, 0, sizeof(*c));
        c->op_to_write = htonl(FW_CMD_OP_V(FW_CAPS_CONFIG_CMD) |
                        goto bye;
        }
 
-       /*
-        * Grab VPD parameters.  This should be done after we establish a
-        * connection to the firmware since some of the VPD parameters
-        * (notably the Core Clock frequency) are retrieved via requests to
-        * the firmware.  On the other hand, we need these fairly early on
-        * so we do this right after getting ahold of the firmware.
-        */
-       ret = t4_get_vpd_params(adap, &adap->params.vpd);
-       if (ret < 0)
-               goto bye;
-
-       /*
-        * Find out what ports are available to us.  Note that we need to do
-        * this before calling adap_init0_no_config() since it needs nports
-        * and portvec ...
-        */
-       v =
-           FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DEV) |
-           FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_DEV_PORTVEC);
-       ret = t4_query_params(adap, adap->mbox, adap->pf, 0, 1, &v, &port_vec);
-       if (ret < 0)
-               goto bye;
-
-       adap->params.nports = hweight32(port_vec);
-       adap->params.portvec = port_vec;
-
        /* If the firmware is initialized already, emit a simply note to that
         * effect. Otherwise, it's time to try initializing the adapter.
         */
                }
        }
 
+       /* Now that we've successfully configured and initialized the adapter
+        * (or found it already initialized), we can ask the Firmware what
+        * resources it has provisioned for us.
+        */
+       ret = t4_get_pfres(adap);
+       if (ret) {
+               dev_err(adap->pdev_dev,
+                       "Unable to retrieve resource provisioning information\n");
+               goto bye;
+       }
+
+       /* Grab VPD parameters.  This should be done after we establish a
+        * connection to the firmware since some of the VPD parameters
+        * (notably the Core Clock frequency) are retrieved via requests to
+        * the firmware.  On the other hand, we need these fairly early on
+        * so we do this right after getting ahold of the firmware.
+        *
+        * We need to do this after initializing the adapter because someone
+        * could have FLASHed a new VPD which won't be read by the firmware
+        * until we do the RESET ...
+        */
+       ret = t4_get_vpd_params(adap, &adap->params.vpd);
+       if (ret < 0)
+               goto bye;
+
+       /* Find out what ports are available to us.  Note that we need to do
+        * this before calling adap_init0_no_config() since it needs nports
+        * and portvec ...
+        */
+       v =
+           FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DEV) |
+           FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_DEV_PORTVEC);
+       ret = t4_query_params(adap, adap->mbox, adap->pf, 0, 1, &v, &port_vec);
+       if (ret < 0)
+               goto bye;
+
+       adap->params.nports = hweight32(port_vec);
+       adap->params.portvec = port_vec;
+
        /* Give the SGE code a chance to pull in anything that it needs ...
         * Note that this must be called after we retrieve our VPD parameters
         * in order to know how to convert core ticks to seconds, etc.
  * of ports we found and the number of available CPUs.  Most settings can be
  * modified by the admin prior to actual use.
  */
-static void cfg_queues(struct adapter *adap)
+static int cfg_queues(struct adapter *adap)
 {
        struct sge *s = &adap->sge;
-       int i = 0, n10g = 0, qidx = 0;
+       int i, n10g = 0, qidx = 0;
+       int niqflint, neq, avail_eth_qsets;
+       int max_eth_qsets = 32;
 #ifndef CONFIG_CHELSIO_T4_DCB
        int q10g = 0;
 #endif
                adap->params.crypto = 0;
        }
 
-       n10g += is_x_10g_port(&adap2pinfo(adap, i)->link_cfg);
+       /* Calculate the number of Ethernet Queue Sets available based on
+        * resources provisioned for us.  We always have an Asynchronous
+        * Firmware Event Ingress Queue.  If we're operating in MSI or Legacy
+        * IRQ Pin Interrupt mode, then we'll also have a Forwarded Interrupt
+        * Ingress Queue.  Meanwhile, we need two Egress Queues for each
+        * Queue Set: one for the Free List and one for the Ethernet TX Queue.
+        *
+        * Note that we should also take into account all of the various
+        * Offload Queues.  But, in any situation where we're operating in
+        * a Resource Constrained Provisioning environment, doing any Offload
+        * at all is problematic ...
+        */
+       niqflint = adap->params.pfres.niqflint - 1;
+       if (!(adap->flags & USING_MSIX))
+               niqflint--;
+       neq = adap->params.pfres.neq / 2;
+       avail_eth_qsets = min(niqflint, neq);
+
+       if (avail_eth_qsets > max_eth_qsets)
+               avail_eth_qsets = max_eth_qsets;
+
+       if (avail_eth_qsets < adap->params.nports) {
+               dev_err(adap->pdev_dev, "avail_eth_qsets=%d < nports=%d\n",
+                       avail_eth_qsets, adap->params.nports);
+               return -ENOMEM;
+       }
+
+       /* Count the number of 10Gb/s or better ports */
+       for_each_port(adap, i)
+               n10g += is_x_10g_port(&adap2pinfo(adap, i)->link_cfg);
+
 #ifdef CONFIG_CHELSIO_T4_DCB
        /* For Data Center Bridging support we need to be able to support up
         * to 8 Traffic Priorities; each of which will be assigned to its
         * own TX Queue in order to prevent Head-Of-Line Blocking.
         */
-       if (adap->params.nports * 8 > MAX_ETH_QSETS) {
-               dev_err(adap->pdev_dev, "MAX_ETH_QSETS=%d < %d!\n",
-                       MAX_ETH_QSETS, adap->params.nports * 8);
-               BUG_ON(1);
+       if (adap->params.nports * 8 > avail_eth_qsets) {
+               dev_err(adap->pdev_dev, "DCB avail_eth_qsets=%d < %d!\n",
+                       avail_eth_qsets, adap->params.nports * 8);
+               return -ENOMEM;
        }
 
        for_each_port(adap, i) {
         * per 10G port.
         */
        if (n10g)
-               q10g = (MAX_ETH_QSETS - (adap->params.nports - n10g)) / n10g;
+               q10g = (avail_eth_qsets - (adap->params.nports - n10g)) / n10g;
        if (q10g > netif_get_num_default_rss_queues())
                q10g = netif_get_num_default_rss_queues();
 
 
        init_rspq(adap, &s->fw_evtq, 0, 1, 1024, 64);
        init_rspq(adap, &s->intrq, 0, 1, 512, 64);
+
+       return 0;
 }
 
 /*
                }
        }
 
+       if (!(adapter->flags & FW_OK))
+               goto fw_attach_fail;
+
        /* Configure queues and allocate tables now, they can be needed as
         * soon as the first register_netdev completes.
         */
-       cfg_queues(adapter);
+       err = cfg_queues(adapter);
+       if (err)
+               goto out_free_dev;
 
        adapter->smt = t4_init_smt();
        if (!adapter->smt) {
                goto out_free_dev;
        }
 
+fw_attach_fail:
        /*
         * The card is now ready to go.  If any errors occur during device
         * registration we do not fail the whole card but rather proceed only
 
        return 0;
 }
 
+/**
+ *     t4_get_pfres - retrieve VF resource limits
+ *     @adapter: the adapter
+ *
+ *     Retrieves configured resource limits and capabilities for a physical
+ *     function.  The results are stored in @adapter->pfres.
+ */
+int t4_get_pfres(struct adapter *adapter)
+{
+       struct pf_resources *pfres = &adapter->params.pfres;
+       struct fw_pfvf_cmd cmd, rpl;
+       int v;
+       u32 word;
+
+       /* Execute PFVF Read command to get VF resource limits; bail out early
+        * with error on command failure.
+        */
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.op_to_vfn = cpu_to_be32(FW_CMD_OP_V(FW_PFVF_CMD) |
+                                   FW_CMD_REQUEST_F |
+                                   FW_CMD_READ_F |
+                                   FW_PFVF_CMD_PFN_V(adapter->pf) |
+                                   FW_PFVF_CMD_VFN_V(0));
+       cmd.retval_len16 = cpu_to_be32(FW_LEN16(cmd));
+       v = t4_wr_mbox(adapter, adapter->mbox, &cmd, sizeof(cmd), &rpl);
+       if (v != FW_SUCCESS)
+               return v;
+
+       /* Extract PF resource limits and return success.
+        */
+       word = be32_to_cpu(rpl.niqflint_niq);
+       pfres->niqflint = FW_PFVF_CMD_NIQFLINT_G(word);
+       pfres->niq = FW_PFVF_CMD_NIQ_G(word);
+
+       word = be32_to_cpu(rpl.type_to_neq);
+       pfres->neq = FW_PFVF_CMD_NEQ_G(word);
+       pfres->pmask = FW_PFVF_CMD_PMASK_G(word);
+
+       word = be32_to_cpu(rpl.tc_to_nexactf);
+       pfres->tc = FW_PFVF_CMD_TC_G(word);
+       pfres->nvi = FW_PFVF_CMD_NVI_G(word);
+       pfres->nexactf = FW_PFVF_CMD_NEXACTF_G(word);
+
+       word = be32_to_cpu(rpl.r_caps_to_nethctrl);
+       pfres->r_caps = FW_PFVF_CMD_R_CAPS_G(word);
+       pfres->wx_caps = FW_PFVF_CMD_WX_CAPS_G(word);
+       pfres->nethctrl = FW_PFVF_CMD_NETHCTRL_G(word);
+
+       return 0;
+}
+
 /* serial flash and firmware constants */
 enum {
        SF_ATTEMPTS = 10,             /* max retries for SF operations */