]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/scsi/aic94xx/aic94xx_sds.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jbarnes...
[linux-2.6-omap-h63xx.git] / drivers / scsi / aic94xx / aic94xx_sds.c
index 83574b5b4e6948f0a30804d44e471763f1d72df1..4446e3d584dc932abd3e9844c2eb5c2f36fdd596 100644 (file)
@@ -30,6 +30,7 @@
 
 #include "aic94xx.h"
 #include "aic94xx_reg.h"
+#include "aic94xx_sds.h"
 
 /* ---------- OCM stuff ---------- */
 
@@ -64,7 +65,7 @@ struct asd_ocm_dir {
 
 #define OCM_INIT_DIR_ENTRIES   5
 /***************************************************************************
-*  OCM dircetory default
+*  OCM directory default
 ***************************************************************************/
 static struct asd_ocm_dir OCMDirInit =
 {
@@ -73,7 +74,7 @@ static struct asd_ocm_dir OCMDirInit =
 };
 
 /***************************************************************************
-*  OCM dircetory Entries default
+*  OCM directory Entries default
 ***************************************************************************/
 static struct asd_ocm_dir_ent OCMDirEntriesInit[OCM_INIT_DIR_ENTRIES] =
 {
@@ -377,7 +378,7 @@ out:
 
 #define FLASH_RESET                    0xF0
 
-#define FLASH_SIZE                      0x200000
+#define ASD_FLASH_SIZE                  0x200000
 #define FLASH_DIR_COOKIE                "*** ADAPTEC FLASH DIRECTORY *** "
 #define FLASH_NEXT_ENTRY_OFFS          0x2000
 #define FLASH_MAX_DIR_ENTRIES          32
@@ -427,7 +428,7 @@ struct asd_manuf_sec {
 
 struct asd_manuf_phy_desc {
        u8    state;         /* low 4 bits */
-#define MS_PHY_STATE_ENABLEABLE 0
+#define MS_PHY_STATE_ENABLED    0
 #define MS_PHY_STATE_REPORTED   1
 #define MS_PHY_STATE_HIDDEN     2
        u8    phy_id;
@@ -589,8 +590,8 @@ static int asd_reset_flash(struct asd_ha_struct *asd_ha)
        return err;
 }
 
-static inline int asd_read_flash_seg(struct asd_ha_struct *asd_ha,
-                                    void *buffer, u32 offs, int size)
+static int asd_read_flash_seg(struct asd_ha_struct *asd_ha,
+                             void *buffer, u32 offs, int size)
 {
        asd_read_reg_string(asd_ha, buffer, asd_ha->hw_prof.flash.bar+offs,
                            size);
@@ -609,7 +610,7 @@ static int asd_find_flash_dir(struct asd_ha_struct *asd_ha,
                              struct asd_flash_dir *flash_dir)
 {
        u32 v;
-       for (v = 0; v < FLASH_SIZE; v += FLASH_NEXT_ENTRY_OFFS) {
+       for (v = 0; v < ASD_FLASH_SIZE; v += FLASH_NEXT_ENTRY_OFFS) {
                asd_read_flash_seg(asd_ha, flash_dir, v,
                                   sizeof(FLASH_DIR_COOKIE)-1);
                if (memcmp(flash_dir->cookie, FLASH_DIR_COOKIE,
@@ -630,10 +631,6 @@ static int asd_flash_getid(struct asd_ha_struct *asd_ha)
 
        reg = asd_read_reg_dword(asd_ha, EXSICNFGR);
 
-       if (!(reg & FLASHEX)) {
-               ASD_DPRINTK("flash doesn't exist\n");
-               return -ENOENT;
-       }
        if (pci_read_config_dword(asd_ha->pcidev, PCI_CONF_FLSH_BAR,
                                  &asd_ha->hw_prof.flash.bar)) {
                asd_printk("couldn't read PCI_CONF_FLSH_BAR of %s\n",
@@ -760,11 +757,11 @@ static void *asd_find_ll_by_id(void * const start, const u8 id0, const u8 id1)
  *
  * HIDDEN phys do not count in the total count.  REPORTED phys cannot
  * be enabled but are reported and counted towards the total.
- * ENEBLEABLE phys are enabled by default and count towards the total.
+ * ENABLED phys are enabled by default and count towards the total.
  * The absolute total phy number is ASD_MAX_PHYS.  hw_prof->num_phys
  * merely specifies the number of phys the host adapter decided to
  * report.  E.g., it is possible for phys 0, 1 and 2 to be HIDDEN,
- * phys 3, 4 and 5 to be REPORTED and phys 6 and 7 to be ENEBLEABLE.
+ * phys 3, 4 and 5 to be REPORTED and phys 6 and 7 to be ENABLED.
  * In this case ASD_MAX_PHYS is 8, hw_prof->num_phys is 5, and only 2
  * are actually enabled (enabled by default, max number of phys
  * enableable in this case).
@@ -820,8 +817,8 @@ static int asd_ms_get_phy_params(struct asd_ha_struct *asd_ha,
                        asd_ha->hw_prof.enabled_phys &= ~(1 << i);
                        rep_phys++;
                        continue;
-               case MS_PHY_STATE_ENABLEABLE:
-                       ASD_DPRINTK("ms: phy%d: ENEBLEABLE\n", i);
+               case MS_PHY_STATE_ENABLED:
+                       ASD_DPRINTK("ms: phy%d: ENABLED\n", i);
                        asd_ha->hw_prof.enabled_phys |= (1 << i);
                        en_phys++;
                        break;
@@ -1087,3 +1084,391 @@ out:
        kfree(flash_dir);
        return err;
 }
+
+/**
+ * asd_verify_flash_seg - verify data with flash memory
+ * @asd_ha: pointer to the host adapter structure
+ * @src: pointer to the source data to be verified
+ * @dest_offset: offset from flash memory
+ * @bytes_to_verify: total bytes to verify
+ */
+int asd_verify_flash_seg(struct asd_ha_struct *asd_ha,
+               void *src, u32 dest_offset, u32 bytes_to_verify)
+{
+       u8 *src_buf;
+       u8 flash_char;
+       int err;
+       u32 nv_offset, reg, i;
+
+       reg = asd_ha->hw_prof.flash.bar;
+       src_buf = NULL;
+
+       err = FLASH_OK;
+       nv_offset = dest_offset;
+       src_buf = (u8 *)src;
+       for (i = 0; i < bytes_to_verify; i++) {
+               flash_char = asd_read_reg_byte(asd_ha, reg + nv_offset + i);
+               if (flash_char != src_buf[i]) {
+                       err = FAIL_VERIFY;
+                       break;
+               }
+       }
+       return err;
+}
+
+/**
+ * asd_write_flash_seg - write data into flash memory
+ * @asd_ha: pointer to the host adapter structure
+ * @src: pointer to the source data to be written
+ * @dest_offset: offset from flash memory
+ * @bytes_to_write: total bytes to write
+ */
+int asd_write_flash_seg(struct asd_ha_struct *asd_ha,
+               void *src, u32 dest_offset, u32 bytes_to_write)
+{
+       u8 *src_buf;
+       u32 nv_offset, reg, i;
+       int err;
+
+       reg = asd_ha->hw_prof.flash.bar;
+       src_buf = NULL;
+
+       err = asd_check_flash_type(asd_ha);
+       if (err) {
+               ASD_DPRINTK("couldn't find the type of flash. err=%d\n", err);
+               return err;
+       }
+
+       nv_offset = dest_offset;
+       err = asd_erase_nv_sector(asd_ha, nv_offset, bytes_to_write);
+       if (err) {
+               ASD_DPRINTK("Erase failed at offset:0x%x\n",
+                       nv_offset);
+               return err;
+       }
+
+       err = asd_reset_flash(asd_ha);
+       if (err) {
+               ASD_DPRINTK("couldn't reset flash. err=%d\n", err);
+               return err;
+       }
+
+       src_buf = (u8 *)src;
+       for (i = 0; i < bytes_to_write; i++) {
+               /* Setup program command sequence */
+               switch (asd_ha->hw_prof.flash.method) {
+               case FLASH_METHOD_A:
+               {
+                       asd_write_reg_byte(asd_ha,
+                                       (reg + 0xAAA), 0xAA);
+                       asd_write_reg_byte(asd_ha,
+                                       (reg + 0x555), 0x55);
+                       asd_write_reg_byte(asd_ha,
+                                       (reg + 0xAAA), 0xA0);
+                       asd_write_reg_byte(asd_ha,
+                                       (reg + nv_offset + i),
+                                       (*(src_buf + i)));
+                       break;
+               }
+               case FLASH_METHOD_B:
+               {
+                       asd_write_reg_byte(asd_ha,
+                                       (reg + 0x555), 0xAA);
+                       asd_write_reg_byte(asd_ha,
+                                       (reg + 0x2AA), 0x55);
+                       asd_write_reg_byte(asd_ha,
+                                       (reg + 0x555), 0xA0);
+                       asd_write_reg_byte(asd_ha,
+                                       (reg + nv_offset + i),
+                                       (*(src_buf + i)));
+                       break;
+               }
+               default:
+                       break;
+               }
+               if (asd_chk_write_status(asd_ha,
+                               (nv_offset + i), 0) != 0) {
+                       ASD_DPRINTK("aicx: Write failed at offset:0x%x\n",
+                               reg + nv_offset + i);
+                       return FAIL_WRITE_FLASH;
+               }
+       }
+
+       err = asd_reset_flash(asd_ha);
+       if (err) {
+               ASD_DPRINTK("couldn't reset flash. err=%d\n", err);
+               return err;
+       }
+       return 0;
+}
+
+int asd_chk_write_status(struct asd_ha_struct *asd_ha,
+        u32 sector_addr, u8 erase_flag)
+{
+       u32 reg;
+       u32 loop_cnt;
+       u8  nv_data1, nv_data2;
+       u8  toggle_bit1;
+
+       /*
+        * Read from DQ2 requires sector address
+        * while it's dont care for DQ6
+        */
+       reg = asd_ha->hw_prof.flash.bar;
+
+       for (loop_cnt = 0; loop_cnt < 50000; loop_cnt++) {
+               nv_data1 = asd_read_reg_byte(asd_ha, reg);
+               nv_data2 = asd_read_reg_byte(asd_ha, reg);
+
+               toggle_bit1 = ((nv_data1 & FLASH_STATUS_BIT_MASK_DQ6)
+                                ^ (nv_data2 & FLASH_STATUS_BIT_MASK_DQ6));
+
+               if (toggle_bit1 == 0) {
+                       return 0;
+               } else {
+                       if (nv_data2 & FLASH_STATUS_BIT_MASK_DQ5) {
+                               nv_data1 = asd_read_reg_byte(asd_ha,
+                                                               reg);
+                               nv_data2 = asd_read_reg_byte(asd_ha,
+                                                               reg);
+                               toggle_bit1 =
+                               ((nv_data1 & FLASH_STATUS_BIT_MASK_DQ6)
+                               ^ (nv_data2 & FLASH_STATUS_BIT_MASK_DQ6));
+
+                               if (toggle_bit1 == 0)
+                                       return 0;
+                       }
+               }
+
+               /*
+                * ERASE is a sector-by-sector operation and requires
+                * more time to finish while WRITE is byte-byte-byte
+                * operation and takes lesser time to finish.
+                *
+                * For some strange reason a reduced ERASE delay gives different
+                * behaviour across different spirit boards. Hence we set
+                * a optimum balance of 50mus for ERASE which works well
+                * across all boards.
+                */
+               if (erase_flag) {
+                       udelay(FLASH_STATUS_ERASE_DELAY_COUNT);
+               } else {
+                       udelay(FLASH_STATUS_WRITE_DELAY_COUNT);
+               }
+       }
+       return -1;
+}
+
+/**
+ * asd_hwi_erase_nv_sector - Erase the flash memory sectors.
+ * @asd_ha: pointer to the host adapter structure
+ * @flash_addr: pointer to offset from flash memory
+ * @size: total bytes to erase.
+ */
+int asd_erase_nv_sector(struct asd_ha_struct *asd_ha, u32 flash_addr, u32 size)
+{
+       u32 reg;
+       u32 sector_addr;
+
+       reg = asd_ha->hw_prof.flash.bar;
+
+       /* sector staring address */
+       sector_addr = flash_addr & FLASH_SECTOR_SIZE_MASK;
+
+       /*
+        * Erasing an flash sector needs to be done in six consecutive
+        * write cyles.
+        */
+       while (sector_addr < flash_addr+size) {
+               switch (asd_ha->hw_prof.flash.method) {
+               case FLASH_METHOD_A:
+                       asd_write_reg_byte(asd_ha, (reg + 0xAAA), 0xAA);
+                       asd_write_reg_byte(asd_ha, (reg + 0x555), 0x55);
+                       asd_write_reg_byte(asd_ha, (reg + 0xAAA), 0x80);
+                       asd_write_reg_byte(asd_ha, (reg + 0xAAA), 0xAA);
+                       asd_write_reg_byte(asd_ha, (reg + 0x555), 0x55);
+                       asd_write_reg_byte(asd_ha, (reg + sector_addr), 0x30);
+                       break;
+               case FLASH_METHOD_B:
+                       asd_write_reg_byte(asd_ha, (reg + 0x555), 0xAA);
+                       asd_write_reg_byte(asd_ha, (reg + 0x2AA), 0x55);
+                       asd_write_reg_byte(asd_ha, (reg + 0x555), 0x80);
+                       asd_write_reg_byte(asd_ha, (reg + 0x555), 0xAA);
+                       asd_write_reg_byte(asd_ha, (reg + 0x2AA), 0x55);
+                       asd_write_reg_byte(asd_ha, (reg + sector_addr), 0x30);
+                       break;
+               default:
+                       break;
+               }
+
+               if (asd_chk_write_status(asd_ha, sector_addr, 1) != 0)
+                       return FAIL_ERASE_FLASH;
+
+               sector_addr += FLASH_SECTOR_SIZE;
+       }
+
+       return 0;
+}
+
+int asd_check_flash_type(struct asd_ha_struct *asd_ha)
+{
+       u8 manuf_id;
+       u8 dev_id;
+       u8 sec_prot;
+       u32 inc;
+       u32 reg;
+       int err;
+
+       /* get Flash memory base address */
+       reg = asd_ha->hw_prof.flash.bar;
+
+       /* Determine flash info */
+       err = asd_reset_flash(asd_ha);
+       if (err) {
+               ASD_DPRINTK("couldn't reset flash. err=%d\n", err);
+               return err;
+       }
+
+       asd_ha->hw_prof.flash.method = FLASH_METHOD_UNKNOWN;
+       asd_ha->hw_prof.flash.manuf = FLASH_MANUF_ID_UNKNOWN;
+       asd_ha->hw_prof.flash.dev_id = FLASH_DEV_ID_UNKNOWN;
+
+       /* Get flash info. This would most likely be AMD Am29LV family flash.
+        * First try the sequence for word mode.  It is the same as for
+        * 008B (byte mode only), 160B (word mode) and 800D (word mode).
+        */
+       inc = asd_ha->hw_prof.flash.wide ? 2 : 1;
+       asd_write_reg_byte(asd_ha, reg + 0xAAA, 0xAA);
+       asd_write_reg_byte(asd_ha, reg + 0x555, 0x55);
+       asd_write_reg_byte(asd_ha, reg + 0xAAA, 0x90);
+       manuf_id = asd_read_reg_byte(asd_ha, reg);
+       dev_id = asd_read_reg_byte(asd_ha, reg + inc);
+       sec_prot = asd_read_reg_byte(asd_ha, reg + inc + inc);
+       /* Get out of autoselect mode. */
+       err = asd_reset_flash(asd_ha);
+       if (err) {
+               ASD_DPRINTK("couldn't reset flash. err=%d\n", err);
+               return err;
+       }
+       ASD_DPRINTK("Flash MethodA manuf_id(0x%x) dev_id(0x%x) "
+               "sec_prot(0x%x)\n", manuf_id, dev_id, sec_prot);
+       err = asd_reset_flash(asd_ha);
+       if (err != 0)
+               return err;
+
+       switch (manuf_id) {
+       case FLASH_MANUF_ID_AMD:
+               switch (sec_prot) {
+               case FLASH_DEV_ID_AM29LV800DT:
+               case FLASH_DEV_ID_AM29LV640MT:
+               case FLASH_DEV_ID_AM29F800B:
+                       asd_ha->hw_prof.flash.method = FLASH_METHOD_A;
+                       break;
+               default:
+                       break;
+               }
+               break;
+       case FLASH_MANUF_ID_ST:
+               switch (sec_prot) {
+               case FLASH_DEV_ID_STM29W800DT:
+               case FLASH_DEV_ID_STM29LV640:
+                       asd_ha->hw_prof.flash.method = FLASH_METHOD_A;
+                       break;
+               default:
+                       break;
+               }
+               break;
+       case FLASH_MANUF_ID_FUJITSU:
+               switch (sec_prot) {
+               case FLASH_DEV_ID_MBM29LV800TE:
+               case FLASH_DEV_ID_MBM29DL800TA:
+                       asd_ha->hw_prof.flash.method = FLASH_METHOD_A;
+                       break;
+               }
+               break;
+       case FLASH_MANUF_ID_MACRONIX:
+               switch (sec_prot) {
+               case FLASH_DEV_ID_MX29LV800BT:
+                       asd_ha->hw_prof.flash.method = FLASH_METHOD_A;
+                       break;
+               }
+               break;
+       }
+
+       if (asd_ha->hw_prof.flash.method == FLASH_METHOD_UNKNOWN) {
+               err = asd_reset_flash(asd_ha);
+               if (err) {
+                       ASD_DPRINTK("couldn't reset flash. err=%d\n", err);
+                       return err;
+               }
+
+               /* Issue Unlock sequence for AM29LV008BT */
+               asd_write_reg_byte(asd_ha, (reg + 0x555), 0xAA);
+               asd_write_reg_byte(asd_ha, (reg + 0x2AA), 0x55);
+               asd_write_reg_byte(asd_ha, (reg + 0x555), 0x90);
+               manuf_id = asd_read_reg_byte(asd_ha, reg);
+               dev_id = asd_read_reg_byte(asd_ha, reg + inc);
+               sec_prot = asd_read_reg_byte(asd_ha, reg + inc + inc);
+
+               ASD_DPRINTK("Flash MethodB manuf_id(0x%x) dev_id(0x%x) sec_prot"
+                       "(0x%x)\n", manuf_id, dev_id, sec_prot);
+
+               err = asd_reset_flash(asd_ha);
+               if (err != 0) {
+                       ASD_DPRINTK("couldn't reset flash. err=%d\n", err);
+                       return err;
+               }
+
+               switch (manuf_id) {
+               case FLASH_MANUF_ID_AMD:
+                       switch (dev_id) {
+                       case FLASH_DEV_ID_AM29LV008BT:
+                               asd_ha->hw_prof.flash.method = FLASH_METHOD_B;
+                               break;
+                       default:
+                               break;
+                       }
+                       break;
+               case FLASH_MANUF_ID_ST:
+                       switch (dev_id) {
+                       case FLASH_DEV_ID_STM29008:
+                               asd_ha->hw_prof.flash.method = FLASH_METHOD_B;
+                               break;
+                       default:
+                               break;
+                       }
+                       break;
+               case FLASH_MANUF_ID_FUJITSU:
+                       switch (dev_id) {
+                       case FLASH_DEV_ID_MBM29LV008TA:
+                               asd_ha->hw_prof.flash.method = FLASH_METHOD_B;
+                               break;
+                       }
+                       break;
+               case FLASH_MANUF_ID_INTEL:
+                       switch (dev_id) {
+                       case FLASH_DEV_ID_I28LV00TAT:
+                               asd_ha->hw_prof.flash.method = FLASH_METHOD_B;
+                               break;
+                       }
+                       break;
+               case FLASH_MANUF_ID_MACRONIX:
+                       switch (dev_id) {
+                       case FLASH_DEV_ID_I28LV00TAT:
+                               asd_ha->hw_prof.flash.method = FLASH_METHOD_B;
+                               break;
+                       }
+                       break;
+               default:
+                       return FAIL_FIND_FLASH_ID;
+               }
+       }
+
+       if (asd_ha->hw_prof.flash.method == FLASH_METHOD_UNKNOWN)
+             return FAIL_FIND_FLASH_ID;
+
+       asd_ha->hw_prof.flash.manuf = manuf_id;
+       asd_ha->hw_prof.flash.dev_id = dev_id;
+       asd_ha->hw_prof.flash.sec_prot = sec_prot;
+       return 0;
+}