Merge branch 'avr32-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/hskinnemo...
[sfrench/cifs-2.6.git] / drivers / scsi / aic94xx / aic94xx_init.c
index b8c6810090d54a4981bc68db13b274090c79eacc..88d1e731b65e15b04e5e1a71f281ba1d2035a8d7 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/kernel.h>
 #include <linux/pci.h>
 #include <linux/delay.h>
+#include <linux/firmware.h>
 
 #include <scsi/scsi_host.h>
 
@@ -36,6 +37,7 @@
 #include "aic94xx_reg.h"
 #include "aic94xx_hwi.h"
 #include "aic94xx_seq.h"
+#include "aic94xx_sds.h"
 
 /* The format is "version.release.patchlevel" */
 #define ASD_DRIVER_VERSION "1.0.3"
@@ -81,6 +83,9 @@ static struct scsi_host_template aic94xx_sht = {
        .use_clustering         = ENABLE_CLUSTERING,
        .eh_device_reset_handler        = sas_eh_device_reset_handler,
        .eh_bus_reset_handler   = sas_eh_bus_reset_handler,
+       .slave_alloc            = sas_slave_alloc,
+       .target_destroy         = sas_target_destroy,
+       .ioctl                  = sas_ioctl,
 };
 
 static int __devinit asd_map_memio(struct asd_ha_struct *asd_ha)
@@ -131,7 +136,7 @@ Err:
        return err;
 }
 
-static void __devexit asd_unmap_memio(struct asd_ha_struct *asd_ha)
+static void asd_unmap_memio(struct asd_ha_struct *asd_ha)
 {
        struct asd_ha_addrspace *io_handle;
 
@@ -168,7 +173,7 @@ static int __devinit asd_map_ioport(struct asd_ha_struct *asd_ha)
        return err;
 }
 
-static void __devexit asd_unmap_ioport(struct asd_ha_struct *asd_ha)
+static void asd_unmap_ioport(struct asd_ha_struct *asd_ha)
 {
        pci_release_region(asd_ha->pcidev, PCI_IOBAR_OFFSET);
 }
@@ -205,7 +210,7 @@ Err:
        return err;
 }
 
-static void __devexit asd_unmap_ha(struct asd_ha_struct *asd_ha)
+static void asd_unmap_ha(struct asd_ha_struct *asd_ha)
 {
        if (asd_ha->iospace)
                asd_unmap_ioport(asd_ha);
@@ -310,6 +315,181 @@ static ssize_t asd_show_dev_pcba_sn(struct device *dev,
 }
 static DEVICE_ATTR(pcba_sn, S_IRUGO, asd_show_dev_pcba_sn, NULL);
 
+#define FLASH_CMD_NONE      0x00
+#define FLASH_CMD_UPDATE    0x01
+#define FLASH_CMD_VERIFY    0x02
+
+struct flash_command {
+     u8      command[8];
+     int     code;
+};
+
+static struct flash_command flash_command_table[] =
+{
+     {"verify",      FLASH_CMD_VERIFY},
+     {"update",      FLASH_CMD_UPDATE},
+     {"",            FLASH_CMD_NONE}      /* Last entry should be NULL. */
+};
+
+struct error_bios {
+     char    *reason;
+     int     err_code;
+};
+
+static struct error_bios flash_error_table[] =
+{
+     {"Failed to open bios image file",      FAIL_OPEN_BIOS_FILE},
+     {"PCI ID mismatch",                     FAIL_CHECK_PCI_ID},
+     {"Checksum mismatch",                   FAIL_CHECK_SUM},
+     {"Unknown Error",                       FAIL_UNKNOWN},
+     {"Failed to verify.",                   FAIL_VERIFY},
+     {"Failed to reset flash chip.",         FAIL_RESET_FLASH},
+     {"Failed to find flash chip type.",     FAIL_FIND_FLASH_ID},
+     {"Failed to erash flash chip.",         FAIL_ERASE_FLASH},
+     {"Failed to program flash chip.",       FAIL_WRITE_FLASH},
+     {"Flash in progress",                   FLASH_IN_PROGRESS},
+     {"Image file size Error",               FAIL_FILE_SIZE},
+     {"Input parameter error",               FAIL_PARAMETERS},
+     {"Out of memory",                       FAIL_OUT_MEMORY},
+     {"OK", 0} /* Last entry err_code = 0. */
+};
+
+static ssize_t asd_store_update_bios(struct device *dev,
+       struct device_attribute *attr,
+       const char *buf, size_t count)
+{
+       struct asd_ha_struct *asd_ha = dev_to_asd_ha(dev);
+       char *cmd_ptr, *filename_ptr;
+       struct bios_file_header header, *hdr_ptr;
+       int res, i;
+       u32 csum = 0;
+       int flash_command = FLASH_CMD_NONE;
+       int err = 0;
+
+       cmd_ptr = kzalloc(count*2, GFP_KERNEL);
+
+       if (!cmd_ptr) {
+               err = FAIL_OUT_MEMORY;
+               goto out;
+       }
+
+       filename_ptr = cmd_ptr + count;
+       res = sscanf(buf, "%s %s", cmd_ptr, filename_ptr);
+       if (res != 2) {
+               err = FAIL_PARAMETERS;
+               goto out1;
+       }
+
+       for (i = 0; flash_command_table[i].code != FLASH_CMD_NONE; i++) {
+               if (!memcmp(flash_command_table[i].command,
+                                cmd_ptr, strlen(cmd_ptr))) {
+                       flash_command = flash_command_table[i].code;
+                       break;
+               }
+       }
+       if (flash_command == FLASH_CMD_NONE) {
+               err = FAIL_PARAMETERS;
+               goto out1;
+       }
+
+       if (asd_ha->bios_status == FLASH_IN_PROGRESS) {
+               err = FLASH_IN_PROGRESS;
+               goto out1;
+       }
+       err = request_firmware(&asd_ha->bios_image,
+                                  filename_ptr,
+                                  &asd_ha->pcidev->dev);
+       if (err) {
+               asd_printk("Failed to load bios image file %s, error %d\n",
+                          filename_ptr, err);
+               err = FAIL_OPEN_BIOS_FILE;
+               goto out1;
+       }
+
+       hdr_ptr = (struct bios_file_header *)asd_ha->bios_image->data;
+
+       if ((hdr_ptr->contrl_id.vendor != asd_ha->pcidev->vendor ||
+               hdr_ptr->contrl_id.device != asd_ha->pcidev->device) &&
+               (hdr_ptr->contrl_id.sub_vendor != asd_ha->pcidev->vendor ||
+               hdr_ptr->contrl_id.sub_device != asd_ha->pcidev->device)) {
+
+               ASD_DPRINTK("The PCI vendor or device id does not match\n");
+               ASD_DPRINTK("vendor=%x dev=%x sub_vendor=%x sub_dev=%x"
+               " pci vendor=%x pci dev=%x\n",
+               hdr_ptr->contrl_id.vendor,
+               hdr_ptr->contrl_id.device,
+               hdr_ptr->contrl_id.sub_vendor,
+               hdr_ptr->contrl_id.sub_device,
+               asd_ha->pcidev->vendor,
+               asd_ha->pcidev->device);
+               err = FAIL_CHECK_PCI_ID;
+               goto out2;
+       }
+
+       if (hdr_ptr->filelen != asd_ha->bios_image->size) {
+               err = FAIL_FILE_SIZE;
+               goto out2;
+       }
+
+       /* calculate checksum */
+       for (i = 0; i < hdr_ptr->filelen; i++)
+               csum += asd_ha->bios_image->data[i];
+
+       if ((csum & 0x0000ffff) != hdr_ptr->checksum) {
+               ASD_DPRINTK("BIOS file checksum mismatch\n");
+               err = FAIL_CHECK_SUM;
+               goto out2;
+       }
+       if (flash_command == FLASH_CMD_UPDATE) {
+               asd_ha->bios_status = FLASH_IN_PROGRESS;
+               err = asd_write_flash_seg(asd_ha,
+                       &asd_ha->bios_image->data[sizeof(*hdr_ptr)],
+                       0, hdr_ptr->filelen-sizeof(*hdr_ptr));
+               if (!err)
+                       err = asd_verify_flash_seg(asd_ha,
+                               &asd_ha->bios_image->data[sizeof(*hdr_ptr)],
+                               0, hdr_ptr->filelen-sizeof(*hdr_ptr));
+       } else {
+               asd_ha->bios_status = FLASH_IN_PROGRESS;
+               err = asd_verify_flash_seg(asd_ha,
+                       &asd_ha->bios_image->data[sizeof(header)],
+                       0, hdr_ptr->filelen-sizeof(header));
+       }
+
+out2:
+       release_firmware(asd_ha->bios_image);
+out1:
+       kfree(cmd_ptr);
+out:
+       asd_ha->bios_status = err;
+
+       if (!err)
+               return count;
+       else
+               return -err;
+}
+
+static ssize_t asd_show_update_bios(struct device *dev,
+                                   struct device_attribute *attr, char *buf)
+{
+       int i;
+       struct asd_ha_struct *asd_ha = dev_to_asd_ha(dev);
+
+       for (i = 0; flash_error_table[i].err_code != 0; i++) {
+               if (flash_error_table[i].err_code == asd_ha->bios_status)
+                       break;
+       }
+       if (asd_ha->bios_status != FLASH_IN_PROGRESS)
+               asd_ha->bios_status = FLASH_OK;
+
+       return snprintf(buf, PAGE_SIZE, "status=%x %s\n",
+                       flash_error_table[i].err_code,
+                       flash_error_table[i].reason);
+}
+
+static DEVICE_ATTR(update_bios, S_IRUGO|S_IWUGO,
+       asd_show_update_bios, asd_store_update_bios);
+
 static int asd_create_dev_attrs(struct asd_ha_struct *asd_ha)
 {
        int err;
@@ -325,9 +505,14 @@ static int asd_create_dev_attrs(struct asd_ha_struct *asd_ha)
        err = device_create_file(&asd_ha->pcidev->dev, &dev_attr_pcba_sn);
        if (err)
                goto err_biosb;
+       err = device_create_file(&asd_ha->pcidev->dev, &dev_attr_update_bios);
+       if (err)
+               goto err_update_bios;
 
        return 0;
 
+err_update_bios:
+       device_remove_file(&asd_ha->pcidev->dev, &dev_attr_pcba_sn);
 err_biosb:
        device_remove_file(&asd_ha->pcidev->dev, &dev_attr_bios_build);
 err_rev:
@@ -340,6 +525,7 @@ static void asd_remove_dev_attrs(struct asd_ha_struct *asd_ha)
        device_remove_file(&asd_ha->pcidev->dev, &dev_attr_revision);
        device_remove_file(&asd_ha->pcidev->dev, &dev_attr_bios_build);
        device_remove_file(&asd_ha->pcidev->dev, &dev_attr_pcba_sn);
+       device_remove_file(&asd_ha->pcidev->dev, &dev_attr_update_bios);
 }
 
 /* The first entry, 0, is used for dynamic ids, the rest for devices
@@ -580,12 +766,13 @@ static int __devinit asd_pci_probe(struct pci_dev *dev,
        asd_ha = kzalloc(sizeof(*asd_ha), GFP_KERNEL);
        if (!asd_ha) {
                asd_printk("out of memory\n");
-               goto Err;
+               goto Err_put;
        }
        asd_ha->pcidev = dev;
-       asd_ha->sas_ha.pcidev = asd_ha->pcidev;
+       asd_ha->sas_ha.dev = &asd_ha->pcidev->dev;
        asd_ha->sas_ha.lldd_ha = asd_ha;
 
+       asd_ha->bios_status = FLASH_OK;
        asd_ha->name = asd_dev->name;
        asd_printk("found %s, device %s\n", asd_ha->name, pci_name(dev));
 
@@ -597,16 +784,12 @@ static int __devinit asd_pci_probe(struct pci_dev *dev,
        shost->max_cmd_len = 16;
 
        err = scsi_add_host(shost, &dev->dev);
-       if (err) {
-               scsi_host_put(shost);
+       if (err)
                goto Err_free;
-       }
-
-
 
        err = asd_dev->setup(asd_ha);
        if (err)
-               goto Err_free;
+               goto Err_remove;
 
        err = -ENODEV;
        if (!pci_set_dma_mask(dev, DMA_64BIT_MASK)
@@ -617,14 +800,14 @@ static int __devinit asd_pci_probe(struct pci_dev *dev,
                ;
        else {
                asd_printk("no suitable DMA mask for %s\n", pci_name(dev));
-               goto Err_free;
+               goto Err_remove;
        }
 
        pci_set_drvdata(dev, asd_ha);
 
        err = asd_map_ha(asd_ha);
        if (err)
-               goto Err_free;
+               goto Err_remove;
 
        err = asd_create_ha_caches(asd_ha);
         if (err)
@@ -691,9 +874,12 @@ Err_free_cache:
        asd_destroy_ha_caches(asd_ha);
 Err_unmap:
        asd_unmap_ha(asd_ha);
+Err_remove:
+       scsi_remove_host(shost);
 Err_free:
        kfree(asd_ha);
-       scsi_remove_host(shost);
+Err_put:
+       scsi_host_put(shost);
 Err:
        pci_disable_device(dev);
        return err;
@@ -817,7 +1003,7 @@ static struct sas_domain_function_template aic94xx_transport_functions = {
        .lldd_abort_task_set    = asd_abort_task_set,
        .lldd_clear_aca         = asd_clear_aca,
        .lldd_clear_task_set    = asd_clear_task_set,
-       .lldd_I_T_nexus_reset   = NULL,
+       .lldd_I_T_nexus_reset   = asd_I_T_nexus_reset,
        .lldd_lu_reset          = asd_lu_reset,
        .lldd_query_task        = asd_query_task,
 
@@ -828,22 +1014,15 @@ static struct sas_domain_function_template aic94xx_transport_functions = {
 };
 
 static const struct pci_device_id aic94xx_pci_table[] __devinitdata = {
-       {PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_RAZOR10),
-        0, 0, 1},
-       {PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_RAZOR12),
-        0, 0, 1},
-       {PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_RAZOR1E),
-        0, 0, 1},
-       {PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_RAZOR1F),
-        0, 0, 1},
-       {PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_RAZOR30),
-        0, 0, 2},
-       {PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_RAZOR32),
-        0, 0, 2},
-       {PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_RAZOR3E),
-        0, 0, 2},
-       {PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_RAZOR3F),
-        0, 0, 2},
+       {PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, 0x410),0, 0, 1},
+       {PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, 0x412),0, 0, 1},
+       {PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, 0x416),0, 0, 1},
+       {PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, 0x41E),0, 0, 1},
+       {PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, 0x41F),0, 0, 1},
+       {PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, 0x430),0, 0, 2},
+       {PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, 0x432),0, 0, 2},
+       {PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, 0x43E),0, 0, 2},
+       {PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, 0x43F),0, 0, 2},
        {}
 };