qlcnic: Implement flash sysfs callback for 83xx adapter
authorHimanshu Madhani <himanshu.madhani@qlogic.com>
Tue, 12 Mar 2013 09:02:16 +0000 (09:02 +0000)
committerDavid S. Miller <davem@davemloft.net>
Wed, 13 Mar 2013 09:35:05 +0000 (05:35 -0400)
QLogic applications use these callbacks to perform

o  NIC Partitioning (NPAR) configuration and management
o  Diagnostic tests
o  Flash access and updates

Signed-off-by: Himanshu Madhani <himanshu.madhani@qlogic.com>
Signed-off-by: Shahed Shaikh <shahed.shaikh@qlogic.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h
drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c

index c08fa20dd5f07a2679905bbaf27668f6cf3ef741..56c3676bdbfeeee8ec93988267b03f66beedafbd 100644 (file)
@@ -2272,7 +2272,7 @@ static int qlcnic_83xx_poll_flash_status_reg(struct qlcnic_adapter *adapter)
        return 0;
 }
 
-static int qlcnic_83xx_enable_flash_write_op(struct qlcnic_adapter *adapter)
+int qlcnic_83xx_enable_flash_write(struct qlcnic_adapter *adapter)
 {
        int ret;
        u32 cmd;
@@ -2290,7 +2290,7 @@ static int qlcnic_83xx_enable_flash_write_op(struct qlcnic_adapter *adapter)
        return 0;
 }
 
-static int qlcnic_83xx_disable_flash_write_op(struct qlcnic_adapter *adapter)
+int qlcnic_83xx_disable_flash_write(struct qlcnic_adapter *adapter)
 {
        int ret;
 
@@ -2364,7 +2364,7 @@ int qlcnic_83xx_erase_flash_sector(struct qlcnic_adapter *adapter,
                return -EIO;
 
        if (adapter->ahw->fdt.mfg_id == adapter->flash_mfg_id) {
-               ret = qlcnic_83xx_enable_flash_write_op(adapter);
+               ret = qlcnic_83xx_enable_flash_write(adapter);
                if (ret) {
                        qlcnic_83xx_unlock_flash(adapter);
                        dev_err(&adapter->pdev->dev,
@@ -2406,7 +2406,7 @@ int qlcnic_83xx_erase_flash_sector(struct qlcnic_adapter *adapter,
        }
 
        if (adapter->ahw->fdt.mfg_id == adapter->flash_mfg_id) {
-               ret = qlcnic_83xx_disable_flash_write_op(adapter);
+               ret = qlcnic_83xx_disable_flash_write(adapter);
                if (ret) {
                        qlcnic_83xx_unlock_flash(adapter);
                        dev_err(&adapter->pdev->dev,
@@ -2446,8 +2446,8 @@ int qlcnic_83xx_flash_bulk_write(struct qlcnic_adapter *adapter, u32 addr,
        u32 temp;
        int ret = -EIO;
 
-       if ((count < QLC_83XX_FLASH_BULK_WRITE_MIN) ||
-           (count > QLC_83XX_FLASH_BULK_WRITE_MAX)) {
+       if ((count < QLC_83XX_FLASH_WRITE_MIN) ||
+           (count > QLC_83XX_FLASH_WRITE_MAX)) {
                dev_err(&adapter->pdev->dev,
                        "%s: Invalid word count\n", __func__);
                return -EIO;
index 648a73f904eedd40caea7cb19224f810830bd9a9..fbb3d1d9e55ceb8959c0c350281d5824e04df154 100644 (file)
@@ -12,6 +12,8 @@
 #include <linux/etherdevice.h>
 #include "qlcnic_hw.h"
 
+#define QLCNIC_83XX_BAR0_LENGTH 0x4000
+
 /* Directly mapped registers */
 #define QLC_83XX_CRB_WIN_BASE          0x3800
 #define QLC_83XX_CRB_WIN_FUNC(f)       (QLC_83XX_CRB_WIN_BASE+((f)*4))
@@ -257,8 +259,8 @@ struct qlc_83xx_idc {
 #define QLC_83XX_FLASH_BULK_WRITE_CMD          0xcadcadca
 #define QLC_83XX_FLASH_READ_RETRY_COUNT        5000
 #define QLC_83XX_FLASH_STATUS_READY            0x6
-#define QLC_83XX_FLASH_BULK_WRITE_MIN          2
-#define QLC_83XX_FLASH_BULK_WRITE_MAX          64
+#define QLC_83XX_FLASH_WRITE_MIN               2
+#define QLC_83XX_FLASH_WRITE_MAX               64
 #define QLC_83XX_FLASH_STATUS_REG_POLL_DELAY   1
 #define QLC_83XX_ERASE_MODE                    1
 #define QLC_83XX_WRITE_MODE                    2
@@ -451,4 +453,6 @@ int qlcnic_83xx_loopback_test(struct net_device *, u8);
 int qlcnic_83xx_interrupt_test(struct net_device *);
 int qlcnic_83xx_set_led(struct net_device *, enum ethtool_phys_id_state);
 int qlcnic_83xx_flash_test(struct qlcnic_adapter *);
+int qlcnic_83xx_enable_flash_write(struct qlcnic_adapter *);
+int qlcnic_83xx_disable_flash_write(struct qlcnic_adapter *);
 #endif
index 4e464dcfcb3fde3bd85b72e885272c82089d2ba0..c77675da671f546921c4932632eeddff8fd22173 100644 (file)
@@ -884,6 +884,244 @@ static ssize_t qlcnic_sysfs_read_pci_config(struct file *file,
        return size;
 }
 
+static ssize_t qlcnic_83xx_sysfs_flash_read_handler(struct file *filp,
+                                                   struct kobject *kobj,
+                                                   struct bin_attribute *attr,
+                                                   char *buf, loff_t offset,
+                                                   size_t size)
+{
+       unsigned char *p_read_buf;
+       int  ret, count;
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct qlcnic_adapter *adapter = dev_get_drvdata(dev);
+
+       if (!size)
+               return QL_STATUS_INVALID_PARAM;
+       if (!buf)
+               return QL_STATUS_INVALID_PARAM;
+
+       count = size / sizeof(u32);
+
+       if (size % sizeof(u32))
+               count++;
+
+       p_read_buf = kcalloc(size, sizeof(unsigned char), GFP_KERNEL);
+       if (!p_read_buf)
+               return -ENOMEM;
+       if (qlcnic_83xx_lock_flash(adapter) != 0) {
+               kfree(p_read_buf);
+               return -EIO;
+       }
+
+       ret = qlcnic_83xx_lockless_flash_read32(adapter, offset, p_read_buf,
+                                               count);
+
+       if (ret) {
+               qlcnic_83xx_unlock_flash(adapter);
+               kfree(p_read_buf);
+               return ret;
+       }
+
+       qlcnic_83xx_unlock_flash(adapter);
+       memcpy(buf, p_read_buf, size);
+       kfree(p_read_buf);
+
+       return size;
+}
+
+static int qlcnic_83xx_sysfs_flash_bulk_write(struct qlcnic_adapter *adapter,
+                                             char *buf, loff_t offset,
+                                             size_t size)
+{
+       int  i, ret, count;
+       unsigned char *p_cache, *p_src;
+
+       p_cache = kcalloc(size, sizeof(unsigned char), GFP_KERNEL);
+       if (!p_cache)
+               return -ENOMEM;
+
+       memcpy(p_cache, buf, size);
+       p_src = p_cache;
+       count = size / sizeof(u32);
+
+       if (qlcnic_83xx_lock_flash(adapter) != 0) {
+               kfree(p_cache);
+               return -EIO;
+       }
+
+       if (adapter->ahw->fdt.mfg_id == adapter->flash_mfg_id) {
+               ret = qlcnic_83xx_enable_flash_write(adapter);
+               if (ret) {
+                       kfree(p_cache);
+                       qlcnic_83xx_unlock_flash(adapter);
+                       return -EIO;
+               }
+       }
+
+       for (i = 0; i < count / QLC_83XX_FLASH_WRITE_MAX; i++) {
+               ret = qlcnic_83xx_flash_bulk_write(adapter, offset,
+                                                  (u32 *)p_src,
+                                                  QLC_83XX_FLASH_WRITE_MAX);
+
+               if (ret) {
+                       if (adapter->ahw->fdt.mfg_id == adapter->flash_mfg_id) {
+                               ret = qlcnic_83xx_disable_flash_write(adapter);
+                               if (ret) {
+                                       kfree(p_cache);
+                                       qlcnic_83xx_unlock_flash(adapter);
+                                       return -EIO;
+                               }
+                       }
+
+                       kfree(p_cache);
+                       qlcnic_83xx_unlock_flash(adapter);
+                       return -EIO;
+               }
+
+               p_src = p_src + sizeof(u32)*QLC_83XX_FLASH_WRITE_MAX;
+               offset = offset + sizeof(u32)*QLC_83XX_FLASH_WRITE_MAX;
+       }
+
+       if (adapter->ahw->fdt.mfg_id == adapter->flash_mfg_id) {
+               ret = qlcnic_83xx_disable_flash_write(adapter);
+               if (ret) {
+                       kfree(p_cache);
+                       qlcnic_83xx_unlock_flash(adapter);
+                       return -EIO;
+               }
+       }
+
+       kfree(p_cache);
+       qlcnic_83xx_unlock_flash(adapter);
+
+       return 0;
+}
+
+static int qlcnic_83xx_sysfs_flash_write(struct qlcnic_adapter *adapter,
+                                        char *buf, loff_t offset, size_t size)
+{
+       int  i, ret, count;
+       unsigned char *p_cache, *p_src;
+
+       p_cache = kcalloc(size, sizeof(unsigned char), GFP_KERNEL);
+       if (!p_cache)
+               return -ENOMEM;
+
+       memcpy(p_cache, buf, size);
+       p_src = p_cache;
+       count = size / sizeof(u32);
+
+       if (qlcnic_83xx_lock_flash(adapter) != 0) {
+               kfree(p_cache);
+               return -EIO;
+       }
+
+       if (adapter->ahw->fdt.mfg_id == adapter->flash_mfg_id) {
+               ret = qlcnic_83xx_enable_flash_write(adapter);
+               if (ret) {
+                       kfree(p_cache);
+                       qlcnic_83xx_unlock_flash(adapter);
+                       return -EIO;
+               }
+       }
+
+       for (i = 0; i < count; i++) {
+               ret = qlcnic_83xx_flash_write32(adapter, offset, (u32 *)p_src);
+               if (ret) {
+                       if (adapter->ahw->fdt.mfg_id == adapter->flash_mfg_id) {
+                               ret = qlcnic_83xx_disable_flash_write(adapter);
+                               if (ret) {
+                                       kfree(p_cache);
+                                       qlcnic_83xx_unlock_flash(adapter);
+                                       return -EIO;
+                               }
+                       }
+                       kfree(p_cache);
+                       qlcnic_83xx_unlock_flash(adapter);
+                       return -EIO;
+               }
+
+               p_src = p_src + sizeof(u32);
+               offset = offset + sizeof(u32);
+       }
+
+       if (adapter->ahw->fdt.mfg_id == adapter->flash_mfg_id) {
+               ret = qlcnic_83xx_disable_flash_write(adapter);
+               if (ret) {
+                       kfree(p_cache);
+                       qlcnic_83xx_unlock_flash(adapter);
+                       return -EIO;
+               }
+       }
+
+       kfree(p_cache);
+       qlcnic_83xx_unlock_flash(adapter);
+
+       return 0;
+}
+
+static ssize_t qlcnic_83xx_sysfs_flash_write_handler(struct file *filp,
+                                                    struct kobject *kobj,
+                                                    struct bin_attribute *attr,
+                                                    char *buf, loff_t offset,
+                                                    size_t size)
+{
+       int  ret;
+       static int flash_mode;
+       unsigned long data;
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct qlcnic_adapter *adapter = dev_get_drvdata(dev);
+
+       if (!buf)
+               return QL_STATUS_INVALID_PARAM;
+
+       ret = kstrtoul(buf, 16, &data);
+
+       switch (data) {
+       case QLC_83XX_FLASH_SECTOR_ERASE_CMD:
+               flash_mode = QLC_83XX_ERASE_MODE;
+               ret = qlcnic_83xx_erase_flash_sector(adapter, offset);
+               if (ret) {
+                       dev_err(&adapter->pdev->dev,
+                               "%s failed at %d\n", __func__, __LINE__);
+                       return -EIO;
+               }
+               break;
+
+       case QLC_83XX_FLASH_BULK_WRITE_CMD:
+               flash_mode = QLC_83XX_BULK_WRITE_MODE;
+               break;
+
+       case QLC_83XX_FLASH_WRITE_CMD:
+               flash_mode = QLC_83XX_WRITE_MODE;
+               break;
+       default:
+               if (flash_mode == QLC_83XX_BULK_WRITE_MODE) {
+                       ret = qlcnic_83xx_sysfs_flash_bulk_write(adapter, buf,
+                                                                offset, size);
+                       if (ret) {
+                               dev_err(&adapter->pdev->dev,
+                                       "%s failed at %d\n",
+                                       __func__, __LINE__);
+                               return -EIO;
+                       }
+               }
+
+               if (flash_mode == QLC_83XX_WRITE_MODE) {
+                       ret = qlcnic_83xx_sysfs_flash_write(adapter, buf,
+                                                           offset, size);
+                       if (ret) {
+                               dev_err(&adapter->pdev->dev,
+                                       "%s failed at %d\n", __func__,
+                                       __LINE__);
+                               return -EIO;
+                       }
+               }
+       }
+
+       return size;
+}
+
 static struct device_attribute dev_attr_bridged_mode = {
        .attr = {.name = "bridged_mode", .mode = (S_IRUGO | S_IWUSR)},
        .show = qlcnic_show_bridged_mode,
@@ -958,6 +1196,13 @@ static struct bin_attribute bin_attr_pm_config = {
        .write = qlcnic_sysfs_write_pm_config,
 };
 
+static struct bin_attribute bin_attr_flash = {
+       .attr = {.name = "flash", .mode = (S_IRUGO | S_IWUSR)},
+       .size = 0,
+       .read = qlcnic_83xx_sysfs_flash_read_handler,
+       .write = qlcnic_83xx_sysfs_flash_write_handler,
+};
+
 void qlcnic_create_sysfs_entries(struct qlcnic_adapter *adapter)
 {
        struct device *dev = &adapter->pdev->dev;
@@ -1046,10 +1291,18 @@ void qlcnic_82xx_remove_sysfs(struct qlcnic_adapter *adapter)
 
 void qlcnic_83xx_add_sysfs(struct qlcnic_adapter *adapter)
 {
+       struct device *dev = &adapter->pdev->dev;
+
        qlcnic_create_diag_entries(adapter);
+
+       if (sysfs_create_bin_file(&dev->kobj, &bin_attr_flash))
+               dev_info(dev, "failed to create flash sysfs entry\n");
 }
 
 void qlcnic_83xx_remove_sysfs(struct qlcnic_adapter *adapter)
 {
+       struct device *dev = &adapter->pdev->dev;
+
        qlcnic_remove_diag_entries(adapter);
+       sysfs_remove_bin_file(&dev->kobj, &bin_attr_flash);
 }