netxen: sysfs control for auto firmware recovery
[safe/jmp/linux-2.6] / drivers / net / netxen / netxen_nic_main.c
index 9b9eab1..1071f09 100644 (file)
@@ -52,6 +52,8 @@ static int use_msi = 1;
 
 static int use_msi_x = 1;
 
+static unsigned long auto_fw_reset = AUTO_FW_RESET_ENABLED;
+
 /* Local functions to NetXen NIC driver */
 static int __devinit netxen_nic_probe(struct pci_dev *pdev,
                const struct pci_device_id *ent);
@@ -73,6 +75,8 @@ static void netxen_nic_poll_controller(struct net_device *netdev);
 
 static void netxen_create_sysfs_entries(struct netxen_adapter *adapter);
 static void netxen_remove_sysfs_entries(struct netxen_adapter *adapter);
+static void netxen_create_diag_entries(struct netxen_adapter *adapter);
+static void netxen_remove_diag_entries(struct netxen_adapter *adapter);
 
 static int nx_decr_dev_ref_cnt(struct netxen_adapter *adapter);
 static int netxen_can_start_firmware(struct netxen_adapter *adapter);
@@ -595,7 +599,8 @@ netxen_setup_pci_map(struct netxen_adapter *adapter)
        void __iomem *mem_ptr2 = NULL;
        void __iomem *db_ptr = NULL;
 
-       unsigned long mem_base, mem_len, db_base, db_len = 0, pci_len0 = 0;
+       resource_size_t mem_base, db_base;
+       unsigned long mem_len, db_len = 0, pci_len0 = 0;
 
        struct pci_dev *pdev = adapter->pdev;
        int pci_func = adapter->ahw.pci_func;
@@ -606,14 +611,12 @@ netxen_setup_pci_map(struct netxen_adapter *adapter)
         * Set the CRB window to invalid. If any register in window 0 is
         * accessed it should set the window to 0 and then reset it to 1.
         */
-       adapter->curr_window = 255;
-       adapter->ahw.qdr_sn_window = -1;
-       adapter->ahw.ddr_mn_window = -1;
+       adapter->ahw.crb_win = -1;
+       adapter->ahw.ocm_win = -1;
 
        /* remap phys address */
        mem_base = pci_resource_start(pdev, 0); /* 0 is for BAR 0 */
        mem_len = pci_resource_len(pdev, 0);
-       pci_len0 = 0;
 
        /* 128 Meg of memory */
        if (mem_len == NETXEN_PCI_128MB_SIZE) {
@@ -622,6 +625,7 @@ netxen_setup_pci_map(struct netxen_adapter *adapter)
                                SECOND_PAGE_GROUP_SIZE);
                mem_ptr2 = ioremap(mem_base + THIRD_PAGE_GROUP_START,
                                THIRD_PAGE_GROUP_SIZE);
+               pci_len0 = FIRST_PAGE_GROUP_SIZE;
        } else if (mem_len == NETXEN_PCI_32MB_SIZE) {
                mem_ptr1 = ioremap(mem_base, SECOND_PAGE_GROUP_SIZE);
                mem_ptr2 = ioremap(mem_base + THIRD_PAGE_GROUP_START -
@@ -634,19 +638,6 @@ netxen_setup_pci_map(struct netxen_adapter *adapter)
                        return -EIO;
                }
                pci_len0 = mem_len;
-
-               adapter->ahw.ddr_mn_window = 0;
-               adapter->ahw.qdr_sn_window = 0;
-
-               adapter->ahw.mn_win_crb = NETXEN_PCI_CRBSPACE +
-                       0x100000 + PCIX_MN_WINDOW + (pci_func * 0x20);
-               adapter->ahw.ms_win_crb = NETXEN_PCI_CRBSPACE +
-                       0x100000 + PCIX_SN_WINDOW;
-               if (pci_func < 4)
-                       adapter->ahw.ms_win_crb += (pci_func * 0x20);
-               else
-                       adapter->ahw.ms_win_crb +=
-                                       0xA0 + ((pci_func - 4) * 0x10);
        } else {
                return -EIO;
        }
@@ -660,6 +651,15 @@ netxen_setup_pci_map(struct netxen_adapter *adapter)
        adapter->ahw.pci_base1 = mem_ptr1;
        adapter->ahw.pci_base2 = mem_ptr2;
 
+       if (NX_IS_REVISION_P3P(adapter->ahw.revision_id)) {
+               adapter->ahw.ocm_win_crb = netxen_get_ioaddr(adapter,
+                       NETXEN_PCIX_PS_REG(PCIX_OCM_WINDOW_REG(pci_func)));
+
+       } else if (NX_IS_REVISION_P3(adapter->ahw.revision_id)) {
+               adapter->ahw.ocm_win_crb = netxen_get_ioaddr(adapter,
+                       NETXEN_PCIX_PS_REG(PCIE_MN_WINDOW_REG(pci_func)));
+       }
+
        if (NX_IS_REVISION_P3(adapter->ahw.revision_id))
                goto skip_doorbell;
 
@@ -816,7 +816,7 @@ netxen_start_firmware(struct netxen_adapter *adapter)
 
        if (first_boot != 0x55555555) {
                NXWR32(adapter, CRB_CMDPEG_STATE, 0);
-               netxen_pinit_from_rom(adapter, 0);
+               netxen_pinit_from_rom(adapter);
                msleep(1);
        }
 
@@ -1249,7 +1249,9 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        revision_id = pdev->revision;
        adapter->ahw.revision_id = revision_id;
 
-       rwlock_init(&adapter->adapter_lock);
+       rwlock_init(&adapter->ahw.crb_lock);
+       spin_lock_init(&adapter->ahw.mem_lock);
+
        spin_lock_init(&adapter->tx_clean_lock);
        INIT_LIST_HEAD(&adapter->mac_list);
 
@@ -1314,6 +1316,8 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
                break;
        }
 
+       netxen_create_diag_entries(adapter);
+
        return 0;
 
 err_out_disable_msi:
@@ -1366,6 +1370,8 @@ static void __devexit netxen_nic_remove(struct pci_dev *pdev)
 
        netxen_teardown_intr(adapter);
 
+       netxen_remove_diag_entries(adapter);
+
        netxen_cleanup_pci_map(adapter);
 
        netxen_release_firmware(adapter);
@@ -1446,7 +1452,8 @@ netxen_nic_resume(struct pci_dev *pdev)
        if (err)
                return err;
 
-       adapter->curr_window = 255;
+       adapter->ahw.crb_win = -1;
+       adapter->ahw.ocm_win = -1;
 
        err = netxen_start_firmware(adapter);
        if (err) {
@@ -1923,7 +1930,7 @@ request_reset:
 struct net_device_stats *netxen_nic_get_stats(struct net_device *netdev)
 {
        struct netxen_adapter *adapter = netdev_priv(netdev);
-       struct net_device_stats *stats = &adapter->net_stats;
+       struct net_device_stats *stats = &netdev->stats;
 
        memset(stats, 0, sizeof(*stats));
 
@@ -2259,7 +2266,8 @@ netxen_check_health(struct netxen_adapter *adapter)
        dev_info(&netdev->dev, "firmware hang detected\n");
 
 detach:
-       if (!test_and_set_bit(__NX_RESETTING, &adapter->state))
+       if ((auto_fw_reset == AUTO_FW_RESET_ENABLED) &&
+                       !test_and_set_bit(__NX_RESETTING, &adapter->state))
                netxen_schedule_work(adapter, netxen_detach_work, 0);
        return 1;
 }
@@ -2337,6 +2345,195 @@ static struct device_attribute dev_attr_bridged_mode = {
        .store = netxen_store_bridged_mode,
 };
 
+static ssize_t
+netxen_store_diag_mode(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t len)
+{
+       struct netxen_adapter *adapter = dev_get_drvdata(dev);
+       unsigned long new;
+
+       if (strict_strtoul(buf, 2, &new))
+               return -EINVAL;
+
+       if (!!new != !!(adapter->flags & NETXEN_NIC_DIAG_ENABLED))
+               adapter->flags ^= NETXEN_NIC_DIAG_ENABLED;
+
+       return len;
+}
+
+static ssize_t
+netxen_show_diag_mode(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct netxen_adapter *adapter = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%d\n",
+                       !!(adapter->flags & NETXEN_NIC_DIAG_ENABLED));
+}
+
+static struct device_attribute dev_attr_diag_mode = {
+       .attr = {.name = "diag_mode", .mode = (S_IRUGO | S_IWUSR)},
+       .show = netxen_show_diag_mode,
+       .store = netxen_store_diag_mode,
+};
+
+static int
+netxen_sysfs_validate_crb(struct netxen_adapter *adapter,
+               loff_t offset, size_t size)
+{
+       if (!(adapter->flags & NETXEN_NIC_DIAG_ENABLED))
+               return -EIO;
+
+       if ((size != 4) || (offset & 0x3))
+               return  -EINVAL;
+
+       if (offset < NETXEN_PCI_CRBSPACE)
+               return -EINVAL;
+
+       return 0;
+}
+
+static ssize_t
+netxen_sysfs_read_crb(struct kobject *kobj, struct bin_attribute *attr,
+               char *buf, loff_t offset, size_t size)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct netxen_adapter *adapter = dev_get_drvdata(dev);
+       u32 data;
+       int ret;
+
+       ret = netxen_sysfs_validate_crb(adapter, offset, size);
+       if (ret != 0)
+               return ret;
+
+       data = NXRD32(adapter, offset);
+       memcpy(buf, &data, size);
+       return size;
+}
+
+static ssize_t
+netxen_sysfs_write_crb(struct kobject *kobj, struct bin_attribute *attr,
+               char *buf, loff_t offset, size_t size)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct netxen_adapter *adapter = dev_get_drvdata(dev);
+       u32 data;
+       int ret;
+
+       ret = netxen_sysfs_validate_crb(adapter, offset, size);
+       if (ret != 0)
+               return ret;
+
+       memcpy(&data, buf, size);
+       NXWR32(adapter, offset, data);
+       return size;
+}
+
+static int
+netxen_sysfs_validate_mem(struct netxen_adapter *adapter,
+               loff_t offset, size_t size)
+{
+       if (!(adapter->flags & NETXEN_NIC_DIAG_ENABLED))
+               return -EIO;
+
+       if ((size != 8) || (offset & 0x7))
+               return  -EIO;
+
+       return 0;
+}
+
+static ssize_t
+netxen_sysfs_read_mem(struct kobject *kobj, struct bin_attribute *attr,
+               char *buf, loff_t offset, size_t size)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct netxen_adapter *adapter = dev_get_drvdata(dev);
+       u64 data;
+       int ret;
+
+       ret = netxen_sysfs_validate_mem(adapter, offset, size);
+       if (ret != 0)
+               return ret;
+
+       if (adapter->pci_mem_read(adapter, offset, &data))
+               return -EIO;
+
+       memcpy(buf, &data, size);
+
+       return size;
+}
+
+ssize_t netxen_sysfs_write_mem(struct kobject *kobj,
+               struct bin_attribute *attr, char *buf,
+               loff_t offset, size_t size)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct netxen_adapter *adapter = dev_get_drvdata(dev);
+       u64 data;
+       int ret;
+
+       ret = netxen_sysfs_validate_mem(adapter, offset, size);
+       if (ret != 0)
+               return ret;
+
+       memcpy(&data, buf, size);
+
+       if (adapter->pci_mem_write(adapter, offset, data))
+               return -EIO;
+
+       return size;
+}
+
+
+static struct bin_attribute bin_attr_crb = {
+       .attr = {.name = "crb", .mode = (S_IRUGO | S_IWUSR)},
+       .size = 0,
+       .read = netxen_sysfs_read_crb,
+       .write = netxen_sysfs_write_crb,
+};
+
+static struct bin_attribute bin_attr_mem = {
+       .attr = {.name = "mem", .mode = (S_IRUGO | S_IWUSR)},
+       .size = 0,
+       .read = netxen_sysfs_read_mem,
+       .write = netxen_sysfs_write_mem,
+};
+
+static ssize_t
+netxen_store_auto_fw_reset(struct module_attribute *mattr,
+               struct module *mod, const char *buf, size_t count)
+
+{
+       unsigned long new;
+
+       if (strict_strtoul(buf, 16, &new))
+               return -EINVAL;
+
+       if ((new == AUTO_FW_RESET_ENABLED) || (new == AUTO_FW_RESET_DISABLED)) {
+               auto_fw_reset = new;
+               return count;
+       }
+
+       return -EINVAL;
+}
+
+static ssize_t
+netxen_show_auto_fw_reset(struct module_attribute *mattr,
+               struct module *mod, char *buf)
+
+{
+       if (auto_fw_reset == AUTO_FW_RESET_ENABLED)
+               return sprintf(buf, "enabled\n");
+       else
+               return sprintf(buf, "disabled\n");
+}
+
+static struct module_attribute mod_attr_fw_reset = {
+       .attr = {.name = "auto_fw_reset", .mode = (S_IRUGO | S_IWUSR)},
+       .show = netxen_show_auto_fw_reset,
+       .store = netxen_store_auto_fw_reset,
+};
+
 static void
 netxen_create_sysfs_entries(struct netxen_adapter *adapter)
 {
@@ -2362,6 +2559,33 @@ netxen_remove_sysfs_entries(struct netxen_adapter *adapter)
                device_remove_file(dev, &dev_attr_bridged_mode);
 }
 
+static void
+netxen_create_diag_entries(struct netxen_adapter *adapter)
+{
+       struct pci_dev *pdev = adapter->pdev;
+       struct device *dev;
+
+       dev = &pdev->dev;
+       if (device_create_file(dev, &dev_attr_diag_mode))
+               dev_info(dev, "failed to create diag_mode sysfs entry\n");
+       if (device_create_bin_file(dev, &bin_attr_crb))
+               dev_info(dev, "failed to create crb sysfs entry\n");
+       if (device_create_bin_file(dev, &bin_attr_mem))
+               dev_info(dev, "failed to create mem sysfs entry\n");
+}
+
+
+static void
+netxen_remove_diag_entries(struct netxen_adapter *adapter)
+{
+       struct pci_dev *pdev = adapter->pdev;
+       struct device *dev = &pdev->dev;
+
+       device_remove_file(dev, &dev_attr_diag_mode);
+       device_remove_bin_file(dev, &bin_attr_crb);
+       device_remove_bin_file(dev, &bin_attr_mem);
+}
+
 #ifdef CONFIG_INET
 
 #define is_netxen_netdev(dev) (dev->netdev_ops == &netxen_netdev_ops)
@@ -2514,6 +2738,8 @@ static struct pci_driver netxen_driver = {
 
 static int __init netxen_init_module(void)
 {
+       struct module *mod = THIS_MODULE;
+
        printk(KERN_INFO "%s\n", netxen_nic_driver_string);
 
 #ifdef CONFIG_INET
@@ -2521,6 +2747,10 @@ static int __init netxen_init_module(void)
        register_inetaddr_notifier(&netxen_inetaddr_cb);
 #endif
 
+       if (sysfs_create_file(&mod->mkobj.kobj, &mod_attr_fw_reset.attr))
+               printk(KERN_ERR "%s: Failed to create auto_fw_reset "
+                               "sysfs entry.", netxen_nic_driver_name);
+
        return pci_register_driver(&netxen_driver);
 }
 
@@ -2528,6 +2758,10 @@ module_init(netxen_init_module);
 
 static void __exit netxen_exit_module(void)
 {
+       struct module *mod = THIS_MODULE;
+
+       sysfs_remove_file(&mod->mkobj.kobj, &mod_attr_fw_reset.attr);
+
        pci_unregister_driver(&netxen_driver);
 
 #ifdef CONFIG_INET