Merge branch 'linus' into x86/urgent
[pandora-kernel.git] / drivers / scsi / aic94xx / aic94xx_sds.c
index 06509bf..8630a75 100644 (file)
@@ -30,6 +30,7 @@
 
 #include "aic94xx.h"
 #include "aic94xx_reg.h"
+#include "aic94xx_sds.h"
 
 /* ---------- OCM stuff ---------- */
 
@@ -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);
@@ -1083,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,
+                        const void *src, u32 dest_offset, u32 bytes_to_verify)
+{
+       const 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 = (const 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,
+                       const void *src, u32 dest_offset, u32 bytes_to_write)
+{
+       const 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 = (const 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;
+}