]> bbs.cooldavid.org Git - net-next-2.6.git/blobdiff - drivers/net/qlcnic/qlcnic_main.c
qlcnic: NIC Partitioning - Add non privileged mode support
[net-next-2.6.git] / drivers / net / qlcnic / qlcnic_main.c
index 1003eb76fda33ed88a3ad53040f2eae6fe15cfcc..119bcae5e74bdcdcef9b69809addfb5ab8c3c677 100644 (file)
@@ -65,6 +65,10 @@ static int load_fw_file;
 module_param(load_fw_file, int, 0644);
 MODULE_PARM_DESC(load_fw_file, "Load firmware from (0=flash, 1=file");
 
+static int qlcnic_config_npars;
+module_param(qlcnic_config_npars, int, 0644);
+MODULE_PARM_DESC(qlcnic_config_npars, "Configure NPARs (0=disabled, 1=enabled");
+
 static int __devinit qlcnic_probe(struct pci_dev *pdev,
                const struct pci_device_id *ent);
 static void __devexit qlcnic_remove(struct pci_dev *pdev);
@@ -99,7 +103,14 @@ static irqreturn_t qlcnic_msix_intr(int irq, void *data);
 
 static struct net_device_stats *qlcnic_get_stats(struct net_device *netdev);
 static void qlcnic_config_indev_addr(struct net_device *dev, unsigned long);
-
+static int qlcnic_start_firmware(struct qlcnic_adapter *);
+
+static void qlcnic_dev_set_npar_ready(struct qlcnic_adapter *);
+static void qlcnicvf_clear_ilb_mode(struct qlcnic_adapter *);
+static int qlcnicvf_set_ilb_mode(struct qlcnic_adapter *);
+static int qlcnicvf_config_led(struct qlcnic_adapter *, u32, u32);
+static int qlcnicvf_config_bridged_mode(struct qlcnic_adapter *, u32);
+static int qlcnicvf_start_firmware(struct qlcnic_adapter *);
 /*  PCI Device ID Table  */
 #define ENTRY(device) \
        {PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, (device)), \
@@ -307,19 +318,14 @@ static void qlcnic_init_msix_entries(struct qlcnic_adapter *adapter, int count)
 static int
 qlcnic_read_mac_addr(struct qlcnic_adapter *adapter)
 {
-       int i;
-       unsigned char *p;
-       u64 mac_addr;
+       u8 mac_addr[ETH_ALEN];
        struct net_device *netdev = adapter->netdev;
        struct pci_dev *pdev = adapter->pdev;
 
-       if (qlcnic_get_mac_addr(adapter, &mac_addr) != 0)
+       if (adapter->nic_ops->get_mac_addr(adapter, mac_addr) != 0)
                return -EIO;
 
-       p = (unsigned char *)&mac_addr;
-       for (i = 0; i < 6; i++)
-               netdev->dev_addr[i] = *(p + 5 - i);
-
+       memcpy(netdev->dev_addr, mac_addr, ETH_ALEN);
        memcpy(netdev->perm_addr, netdev->dev_addr, netdev->addr_len);
        memcpy(adapter->mac_addr, netdev->dev_addr, netdev->addr_len);
 
@@ -371,6 +377,33 @@ static const struct net_device_ops qlcnic_netdev_ops = {
 #endif
 };
 
+static struct qlcnic_nic_template qlcnic_ops = {
+       .get_mac_addr = qlcnic_get_mac_addr,
+       .config_bridged_mode = qlcnic_config_bridged_mode,
+       .config_led = qlcnic_config_led,
+       .set_ilb_mode = qlcnic_set_ilb_mode,
+       .clear_ilb_mode = qlcnic_clear_ilb_mode,
+       .start_firmware = qlcnic_start_firmware
+};
+
+static struct qlcnic_nic_template qlcnic_pf_ops = {
+       .get_mac_addr = qlcnic_get_mac_address,
+       .config_bridged_mode = qlcnic_config_bridged_mode,
+       .config_led = qlcnic_config_led,
+       .set_ilb_mode = qlcnic_set_ilb_mode,
+       .clear_ilb_mode = qlcnic_clear_ilb_mode,
+       .start_firmware = qlcnic_start_firmware
+};
+
+static struct qlcnic_nic_template qlcnic_vf_ops = {
+       .get_mac_addr = qlcnic_get_mac_address,
+       .config_bridged_mode = qlcnicvf_config_bridged_mode,
+       .config_led = qlcnicvf_config_led,
+       .set_ilb_mode = qlcnicvf_set_ilb_mode,
+       .clear_ilb_mode = qlcnicvf_clear_ilb_mode,
+       .start_firmware = qlcnicvf_start_firmware
+};
+
 static void
 qlcnic_setup_intr(struct qlcnic_adapter *adapter)
 {
@@ -452,6 +485,132 @@ qlcnic_cleanup_pci_map(struct qlcnic_adapter *adapter)
                iounmap(adapter->ahw.pci_base0);
 }
 
+static int
+qlcnic_set_function_modes(struct qlcnic_adapter *adapter)
+{
+       u8 id;
+       u32 ref_count;
+       int i, ret = 1;
+       u32 data = QLCNIC_MGMT_FUNC;
+       void __iomem *priv_op = adapter->ahw.pci_base0 + QLCNIC_DRV_OP_MODE;
+
+       /* If other drivers are not in use set their privilege level */
+       ref_count = QLCRD32(adapter, QLCNIC_CRB_DEV_REF_COUNT);
+       ret = qlcnic_api_lock(adapter);
+       if (ret)
+               goto err_lock;
+       if (QLC_DEV_CLR_REF_CNT(ref_count, adapter->ahw.pci_func))
+               goto err_npar;
+
+       for (i = 0; i < QLCNIC_MAX_PCI_FUNC; i++) {
+               id = adapter->npars[i].id;
+               if (adapter->npars[i].type != QLCNIC_TYPE_NIC ||
+                       id == adapter->ahw.pci_func)
+                       continue;
+               data |= (qlcnic_config_npars & QLC_DEV_SET_DRV(0xf, id));
+       }
+       writel(data, priv_op);
+
+err_npar:
+       qlcnic_api_unlock(adapter);
+err_lock:
+       return ret;
+}
+
+static u8
+qlcnic_set_mgmt_driver(struct qlcnic_adapter *adapter)
+{
+       u8 i, ret = 0;
+
+       if (qlcnic_get_pci_info(adapter))
+               return ret;
+       /* Set the eswitch */
+       for (i = 0; i < QLCNIC_NIU_MAX_XG_PORTS; i++) {
+               if (!qlcnic_get_eswitch_capabilities(adapter, i,
+                       &adapter->eswitch[i])) {
+                       ret++;
+                       qlcnic_toggle_eswitch(adapter, i, ret);
+               }
+       }
+       return ret;
+}
+
+static u32
+qlcnic_get_driver_mode(struct qlcnic_adapter *adapter)
+{
+       void __iomem *msix_base_addr;
+       void __iomem *priv_op;
+       u32 func;
+       u32 msix_base;
+       u32 op_mode, priv_level;
+
+       /* Determine FW API version */
+       adapter->fw_hal_version = readl(adapter->ahw.pci_base0 + QLCNIC_FW_API);
+       if (adapter->fw_hal_version == ~0) {
+               adapter->nic_ops = &qlcnic_ops;
+               adapter->fw_hal_version = QLCNIC_FW_BASE;
+               adapter->ahw.pci_func = PCI_FUNC(adapter->pdev->devfn);
+               dev_info(&adapter->pdev->dev,
+                       "FW does not support nic partion\n");
+               return adapter->fw_hal_version;
+       }
+
+       /* Find PCI function number */
+       pci_read_config_dword(adapter->pdev, QLCNIC_MSIX_TABLE_OFFSET, &func);
+       msix_base_addr = adapter->ahw.pci_base0 + QLCNIC_MSIX_BASE;
+       msix_base = readl(msix_base_addr);
+       func = (func - msix_base)/QLCNIC_MSIX_TBL_PGSIZE;
+       adapter->ahw.pci_func = func;
+
+       /* Determine function privilege level */
+       priv_op = adapter->ahw.pci_base0 + QLCNIC_DRV_OP_MODE;
+       op_mode = readl(priv_op);
+       if (op_mode == QLC_DEV_DRV_DEFAULT) {
+               priv_level = QLCNIC_MGMT_FUNC;
+               if (qlcnic_api_lock(adapter))
+                       return 0;
+               op_mode = (op_mode & ~QLC_DEV_SET_DRV(0xf, func)) |
+                               (QLC_DEV_SET_DRV(QLCNIC_MGMT_FUNC, func));
+               writel(op_mode, priv_op);
+               qlcnic_api_unlock(adapter);
+
+       } else
+               priv_level = QLC_DEV_GET_DRV(op_mode, adapter->ahw.pci_func);
+
+       switch (priv_level) {
+       case QLCNIC_MGMT_FUNC:
+               adapter->op_mode = QLCNIC_MGMT_FUNC;
+               adapter->nic_ops = &qlcnic_pf_ops;
+               /* Set privilege level for other functions */
+               if (qlcnic_config_npars)
+                       qlcnic_set_function_modes(adapter);
+               qlcnic_dev_set_npar_ready(adapter);
+               dev_info(&adapter->pdev->dev,
+                       "HAL Version: %d, Management function\n",
+                       adapter->fw_hal_version);
+               break;
+       case QLCNIC_PRIV_FUNC:
+               adapter->op_mode = QLCNIC_PRIV_FUNC;
+               dev_info(&adapter->pdev->dev,
+                       "HAL Version: %d, Privileged function\n",
+                       adapter->fw_hal_version);
+               adapter->nic_ops = &qlcnic_pf_ops;
+               break;
+       case QLCNIC_NON_PRIV_FUNC:
+               adapter->op_mode = QLCNIC_NON_PRIV_FUNC;
+               dev_info(&adapter->pdev->dev,
+                       "HAL Version: %d Non Privileged function\n",
+                       adapter->fw_hal_version);
+               adapter->nic_ops = &qlcnic_vf_ops;
+               break;
+       default:
+               dev_info(&adapter->pdev->dev, "Unknown function mode: %d\n",
+                       priv_level);
+               return 0;
+       }
+       return adapter->fw_hal_version;
+}
+
 static int
 qlcnic_setup_pci_map(struct qlcnic_adapter *adapter)
 {
@@ -460,7 +619,6 @@ qlcnic_setup_pci_map(struct qlcnic_adapter *adapter)
        unsigned long mem_len, pci_len0 = 0;
 
        struct pci_dev *pdev = adapter->pdev;
-       int pci_func = adapter->ahw.pci_func;
 
        /* remap phys address */
        mem_base = pci_resource_start(pdev, 0); /* 0 is for BAR 0 */
@@ -483,8 +641,13 @@ qlcnic_setup_pci_map(struct qlcnic_adapter *adapter)
        adapter->ahw.pci_base0 = mem_ptr0;
        adapter->ahw.pci_len0 = pci_len0;
 
+       if (!qlcnic_get_driver_mode(adapter)) {
+               iounmap(adapter->ahw.pci_base0);
+               return -EIO;
+       }
+
        adapter->ahw.ocm_win_crb = qlcnic_get_ioaddr(adapter,
-               QLCNIC_PCIX_PS_REG(PCIX_OCM_WINDOW_REG(pci_func)));
+               QLCNIC_PCIX_PS_REG(PCIX_OCM_WINDOW_REG(adapter->ahw.pci_func)));
 
        return 0;
 }
@@ -553,7 +716,10 @@ qlcnic_check_options(struct qlcnic_adapter *adapter)
        dev_info(&pdev->dev, "firmware v%d.%d.%d\n",
                        fw_major, fw_minor, fw_build);
 
-       adapter->capabilities = QLCRD32(adapter, CRB_FW_CAPABILITIES_1);
+       if (adapter->fw_hal_version == QLCNIC_FW_NPAR)
+               qlcnic_get_nic_info(adapter, adapter->ahw.pci_func);
+       else
+               adapter->capabilities = QLCRD32(adapter, CRB_FW_CAPABILITIES_1);
 
        adapter->flags &= ~QLCNIC_LRO_ENABLED;
 
@@ -631,8 +797,14 @@ wait_init:
        QLCWR32(adapter, QLCNIC_CRB_DEV_STATE, QLCNIC_DEV_READY);
        qlcnic_idc_debug_info(adapter, 1);
 
+       qlcnic_dev_set_npar_ready(adapter);
+
        qlcnic_check_options(adapter);
 
+       if (adapter->fw_hal_version != QLCNIC_FW_BASE &&
+                       adapter->op_mode == QLCNIC_MGMT_FUNC)
+               qlcnic_set_mgmt_driver(adapter);
+
        adapter->need_fw_reset = 0;
 
        qlcnic_release_firmware(adapter);
@@ -977,12 +1149,11 @@ qlcnic_setup_netdev(struct qlcnic_adapter *adapter,
 
        SET_ETHTOOL_OPS(netdev, &qlcnic_ethtool_ops);
 
-       netdev->features |= (NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO);
-       netdev->features |= (NETIF_F_GRO);
-       netdev->vlan_features |= (NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO);
+       netdev->features |= (NETIF_F_SG | NETIF_F_IP_CSUM |
+               NETIF_F_IPV6_CSUM | NETIF_F_GRO | NETIF_F_TSO | NETIF_F_TSO6);
 
-       netdev->features |= (NETIF_F_IPV6_CSUM | NETIF_F_TSO6);
-       netdev->vlan_features |= (NETIF_F_IPV6_CSUM | NETIF_F_TSO6);
+       netdev->vlan_features |= (NETIF_F_SG | NETIF_F_IP_CSUM |
+               NETIF_F_IPV6_CSUM | NETIF_F_TSO | NETIF_F_TSO6);
 
        if (pci_using_dac) {
                netdev->features |= NETIF_F_HIGHDMA;
@@ -1036,7 +1207,6 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        struct net_device *netdev = NULL;
        struct qlcnic_adapter *adapter = NULL;
        int err;
-       int pci_func_id = PCI_FUNC(pdev->devfn);
        uint8_t revision_id;
        uint8_t pci_using_dac;
 
@@ -1072,7 +1242,6 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        adapter->netdev  = netdev;
        adapter->pdev    = pdev;
        adapter->dev_rst_time = jiffies;
-       adapter->ahw.pci_func  = pci_func_id;
 
        revision_id = pdev->revision;
        adapter->ahw.revision_id = revision_id;
@@ -1088,7 +1257,7 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
                goto err_out_free_netdev;
 
        /* This will be reset for mezz cards  */
-       adapter->portnum = pci_func_id;
+       adapter->portnum = adapter->ahw.pci_func;
 
        err = qlcnic_get_board_info(adapter);
        if (err) {
@@ -1102,7 +1271,7 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        if (qlcnic_setup_idc_param(adapter))
                goto err_out_iounmap;
 
-       err = qlcnic_start_firmware(adapter);
+       err = adapter->nic_ops->start_firmware(adapter);
        if (err) {
                dev_err(&pdev->dev, "Loading fw failed.Please Reboot\n");
                goto err_out_decr_ref;
@@ -1175,6 +1344,11 @@ static void __devexit qlcnic_remove(struct pci_dev *pdev)
 
        qlcnic_detach(adapter);
 
+       if (adapter->npars != NULL)
+               kfree(adapter->npars);
+       if (adapter->eswitch != NULL)
+               kfree(adapter->eswitch);
+
        qlcnic_clr_all_drv_state(adapter);
 
        clear_bit(__QLCNIC_RESETTING, &adapter->state);
@@ -1263,7 +1437,7 @@ qlcnic_resume(struct pci_dev *pdev)
        pci_set_master(pdev);
        pci_restore_state(pdev);
 
-       err = qlcnic_start_firmware(adapter);
+       err = adapter->nic_ops->start_firmware(adapter);
        if (err) {
                dev_err(&pdev->dev, "failed to start firmware\n");
                return err;
@@ -1340,11 +1514,11 @@ qlcnic_tso_check(struct net_device *netdev,
        u8 opcode = TX_ETHER_PKT;
        __be16 protocol = skb->protocol;
        u16 flags = 0, vid = 0;
-       u32 producer;
        int copied, offset, copy_len, hdr_len = 0, tso = 0, vlan_oob = 0;
        struct cmd_desc_type0 *hwdesc;
        struct vlan_ethhdr *vh;
        struct qlcnic_adapter *adapter = netdev_priv(netdev);
+       u32 producer = tx_ring->producer;
 
        if (protocol == cpu_to_be16(ETH_P_8021Q)) {
 
@@ -1360,6 +1534,11 @@ qlcnic_tso_check(struct net_device *netdev,
                vlan_oob = 1;
        }
 
+       if (*(skb->data) & BIT_0) {
+               flags |= BIT_0;
+               memcpy(&first_desc->eth_addr, skb->data, ETH_ALEN);
+       }
+
        if ((netdev->features & (NETIF_F_TSO | NETIF_F_TSO6)) &&
                        skb_shinfo(skb)->gso_size > 0) {
 
@@ -1409,7 +1588,6 @@ qlcnic_tso_check(struct net_device *netdev,
        /* For LSO, we need to copy the MAC/IP/TCP headers into
         * the descriptor ring
         */
-       producer = tx_ring->producer;
        copied = 0;
        offset = 2;
 
@@ -2109,7 +2287,7 @@ qlcnic_fwinit_work(struct work_struct *work)
 {
        struct qlcnic_adapter *adapter = container_of(work,
                        struct qlcnic_adapter, fw_work.work);
-       u32 dev_state = 0xf;
+       u32 dev_state = 0xf, npar_state;
 
        if (qlcnic_api_lock(adapter))
                goto err_ret;
@@ -2122,6 +2300,19 @@ qlcnic_fwinit_work(struct work_struct *work)
                return;
        }
 
+       if (adapter->op_mode == QLCNIC_NON_PRIV_FUNC) {
+               npar_state = QLCRD32(adapter, QLCNIC_CRB_DEV_NPAR_STATE);
+               if (npar_state == QLCNIC_DEV_NPAR_RDY) {
+                       qlcnic_api_unlock(adapter);
+                       goto wait_npar;
+               } else {
+                       qlcnic_schedule_work(adapter, qlcnic_fwinit_work,
+                               FW_POLL_DELAY);
+                       qlcnic_api_unlock(adapter);
+                       return;
+               }
+       }
+
        if (adapter->fw_wait_cnt++ > adapter->reset_ack_timeo) {
                dev_err(&adapter->pdev->dev, "Reset:Failed to get ack %d sec\n",
                                        adapter->reset_ack_timeo);
@@ -2154,7 +2345,7 @@ skip_ack_check:
 
                qlcnic_api_unlock(adapter);
 
-               if (!qlcnic_start_firmware(adapter)) {
+               if (!adapter->nic_ops->start_firmware(adapter)) {
                        qlcnic_schedule_work(adapter, qlcnic_attach_work, 0);
                        return;
                }
@@ -2163,6 +2354,7 @@ skip_ack_check:
 
        qlcnic_api_unlock(adapter);
 
+wait_npar:
        dev_state = QLCRD32(adapter, QLCNIC_CRB_DEV_STATE);
        QLCDB(adapter, HW, "Func waiting: Device state=%u\n", dev_state);
 
@@ -2177,7 +2369,7 @@ skip_ack_check:
                break;
 
        default:
-               if (!qlcnic_start_firmware(adapter)) {
+               if (!adapter->nic_ops->start_firmware(adapter)) {
                        qlcnic_schedule_work(adapter, qlcnic_attach_work, 0);
                        return;
                }
@@ -2251,6 +2443,30 @@ qlcnic_dev_request_reset(struct qlcnic_adapter *adapter)
        qlcnic_api_unlock(adapter);
 }
 
+/* Transit to NPAR READY state from NPAR NOT READY state */
+static void
+qlcnic_dev_set_npar_ready(struct qlcnic_adapter *adapter)
+{
+       u32 state;
+
+       if (adapter->op_mode == QLCNIC_NON_PRIV_FUNC ||
+               adapter->fw_hal_version == QLCNIC_FW_BASE)
+               return;
+
+       if (qlcnic_api_lock(adapter))
+               return;
+
+       state = QLCRD32(adapter, QLCNIC_CRB_DEV_NPAR_STATE);
+
+       if (state != QLCNIC_DEV_NPAR_RDY) {
+               QLCWR32(adapter, QLCNIC_CRB_DEV_NPAR_STATE,
+                       QLCNIC_DEV_NPAR_RDY);
+               QLCDB(adapter, DRV, "NPAR READY state set\n");
+       }
+
+       qlcnic_api_unlock(adapter);
+}
+
 static void
 qlcnic_schedule_work(struct qlcnic_adapter *adapter,
                work_func_t func, int delay)
@@ -2382,7 +2598,7 @@ qlcnic_store_bridged_mode(struct device *dev,
        if (strict_strtoul(buf, 2, &new))
                goto err_out;
 
-       if (!qlcnic_config_bridged_mode(adapter, !!new))
+       if (!adapter->nic_ops->config_bridged_mode(adapter, !!new))
                ret = len;
 
 err_out:
@@ -2464,7 +2680,8 @@ qlcnic_sysfs_validate_crb(struct qlcnic_adapter *adapter,
 }
 
 static ssize_t
-qlcnic_sysfs_read_crb(struct kobject *kobj, struct bin_attribute *attr,
+qlcnic_sysfs_read_crb(struct file *filp, struct kobject *kobj,
+               struct bin_attribute *attr,
                char *buf, loff_t offset, size_t size)
 {
        struct device *dev = container_of(kobj, struct device, kobj);
@@ -2488,7 +2705,8 @@ qlcnic_sysfs_read_crb(struct kobject *kobj, struct bin_attribute *attr,
 }
 
 static ssize_t
-qlcnic_sysfs_write_crb(struct kobject *kobj, struct bin_attribute *attr,
+qlcnic_sysfs_write_crb(struct file *filp, struct kobject *kobj,
+               struct bin_attribute *attr,
                char *buf, loff_t offset, size_t size)
 {
        struct device *dev = container_of(kobj, struct device, kobj);
@@ -2525,7 +2743,8 @@ qlcnic_sysfs_validate_mem(struct qlcnic_adapter *adapter,
 }
 
 static ssize_t
-qlcnic_sysfs_read_mem(struct kobject *kobj, struct bin_attribute *attr,
+qlcnic_sysfs_read_mem(struct file *filp, struct kobject *kobj,
+               struct bin_attribute *attr,
                char *buf, loff_t offset, size_t size)
 {
        struct device *dev = container_of(kobj, struct device, kobj);
@@ -2546,7 +2765,8 @@ qlcnic_sysfs_read_mem(struct kobject *kobj, struct bin_attribute *attr,
 }
 
 static ssize_t
-qlcnic_sysfs_write_mem(struct kobject *kobj, struct bin_attribute *attr,
+qlcnic_sysfs_write_mem(struct file *filp, struct kobject *kobj,
+               struct bin_attribute *attr,
                char *buf, loff_t offset, size_t size)
 {
        struct device *dev = container_of(kobj, struct device, kobj);
@@ -2734,6 +2954,47 @@ done:
        return NOTIFY_DONE;
 }
 
+static int
+qlcnicvf_start_firmware(struct qlcnic_adapter *adapter)
+{
+       int err;
+
+       err = qlcnic_can_start_firmware(adapter);
+       if (err)
+               return err;
+
+       qlcnic_check_options(adapter);
+
+       adapter->need_fw_reset = 0;
+
+       return err;
+}
+
+static int
+qlcnicvf_config_bridged_mode(struct qlcnic_adapter *adapter, u32 enable)
+{
+       return -EOPNOTSUPP;
+}
+
+static int
+qlcnicvf_config_led(struct qlcnic_adapter *adapter, u32 state, u32 rate)
+{
+       return -EOPNOTSUPP;
+}
+
+static int
+qlcnicvf_set_ilb_mode(struct qlcnic_adapter *adapter)
+{
+       return -EOPNOTSUPP;
+}
+
+static void
+qlcnicvf_clear_ilb_mode(struct qlcnic_adapter *adapter)
+{
+       return;
+}
+
+
 static struct notifier_block   qlcnic_netdev_cb = {
        .notifier_call = qlcnic_netdev_event,
 };