Index: ecos/packages/devs/flash/amd/am29xxxxxv2/current/src/am29xxxxx_aux.c =================================================================== --- ecos.orig/packages/devs/flash/amd/am29xxxxxv2/current/src/am29xxxxx_aux.c 2006-11-06 20:00:26.660773335 +0100 +++ ecos/packages/devs/flash/amd/am29xxxxxv2/current/src/am29xxxxx_aux.c 2006-11-06 20:00:47.856654298 +0100 @@ -100,6 +100,11 @@ # define AM29_PARALLEL(_cmd_) (_cmd_) #endif +// for spansion bit-toggle code +#define DQ6_TGL_DQ1_MASK (dq6_toggles >> 5) +#define DQ6_TGL_DQ3_MASK (dq6_toggles >> 3) +#define DQ6_TGL_DQ5_MASK (dq6_toggles >> 1) + // ---------------------------------------------------------------------------- // When performing the various low-level operations like erase the flash // chip can no longer support ordinary data reads. Obviously this is a @@ -216,6 +221,7 @@ // each sector, so there is no opportunity inside the driver for // erasing multiple sectors in a single call. The address argument // points at the start of the sector. + static void AM29_FNNAME(am29_hw_erase)(volatile AM29_TYPE* addr) { @@ -230,41 +236,34 @@ // There is now a 50us window in which we could send additional // ERASE_SECTOR commands, but the driver API does not allow this - // All chips are now erasing in parallel. Loop until all have - // completed. This can be detected in a number of ways. The DQ7 - // bit will be 0 until the erase is complete, but there is a - // problem if something went wrong (e.g. the sector is locked), - // the erase has not actually started, and the relevant bit was 0 - // already. More useful is DQ6. This will toggle during the 50us - // window and while the erase is in progress, then stop toggling. - // If the erase does not actually start then the bit won't toggle - // at all so the operation completes rather quickly. - // - // If at any time DQ5 is set (indicating a timeout inside the - // chip) then a reset command must be issued and the erase is - // aborted. It is not clear this can actually happen during an - // erase, but just in case. do { - AM29_TYPE datum1, datum2; + AM29_TYPE datum1, datum2, dq6_toggles = 0; datum1 = addr[AM29_OFFSET_COMMAND]; datum2 = addr[AM29_OFFSET_COMMAND]; if ((datum1 & AM29_STATUS_DQ6) == (datum2 & AM29_STATUS_DQ6)) { // The bits have stopped toggling, so finished. break; } - // If DQ6 toggled, then check if DQ5 was set in datum1 - // (not datum2 as that may indicate a successful 0->1 transition - // which can happen for one part of parallel devices before they - // all complete the erase) - if ((((datum1 ^ datum2) & AM29_STATUS_DQ6) >> 1) & datum1) { - // Hardware error. The calling code will always verify - // that the erase really was successful, so we don't need - // to distinguish - addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_RESET; - break; - } - } while (retries-- > 0); + /* Checking Timeout condition: only check on the device that has DQ6 toggling */ + if (DQ6_TGL_DQ5_MASK & datum2) + { + /* read again to make sure Timeout Error is correct */ + datum1 = addr[AM29_OFFSET_COMMAND]; + datum2 = addr[AM29_OFFSET_COMMAND]; + + dq6_toggles = (datum1 ^ datum2) & AM29_STATUS_DQ6; + + if ((dq6_toggles && (DQ6_TGL_DQ5_MASK & datum2)) && + !(DQ6_TGL_DQ5_MASK ^ (datum2 & AM29_STATUS_DQ5))) { + // Hardware error. The calling code will always verify + // that the erase really was successful, so we don't need + // to distinguish + addr[AM29_OFFSET_COMMAND] = AM29_COMMAND_RESET; + break; + } + } + } while (retries-- > 0); // The calling code will verify that the erase was successful, // and generate an error code. } @@ -281,9 +280,10 @@ int i; for (i = 0; i < count; i++) { - AM29_TYPE datum; - AM29_TYPE current, current2, masked_datum; - + AM29_TYPE datum, datum1, datum2; + AM29_TYPE dq6_toggles = 0; + AM29_TYPE current; + // We can only clear bits, not set them, so any bits that were // already clear need to be preserved. current = addr[i]; @@ -298,36 +298,32 @@ block_start[AM29_OFFSET_COMMAND] = AM29_COMMAND_PROGRAM; addr[i] = datum; - // The data is now being written. While the write is in progress - // DQ7 will have an inverted value from what was written, so we - // can poll, comparing just this bit. Again, if DQ5 is set then - // an error has occurred. - masked_datum = datum & AM29_STATUS_DQ7; retries = CYGNUM_DEVS_FLASH_AMD_AM29XXXXX_V2_PROGRAM_TIMEOUT; - do { - current = addr[i]; - if ((current & AM29_STATUS_DQ7) == masked_datum) { - break; - } - if (0 != (current & AM29_STATUS_DQ5)) { - // It's possible that one device can finish before - // another. To deal with this we look at the DQ6 - // toggle bit, and only consider this to be an error - // if it is still toggling for the device that's - // reporting DQ5 set. This is similar to the checking - // for erase timeouts above. This is unnecessary - // before DQ5 gets set, so we don't do the double read - // all the time. - current2 = addr[i]; - if ((((current ^ current2) & AM29_STATUS_DQ6) >> 1) & current) { - // A timeout has occurred inside the hardware and - // the system is in a strange state. Reset but don't - // try to write any more of the data. + do + { + datum1 = addr[AM29_OFFSET_COMMAND]; + datum2 = addr[AM29_OFFSET_COMMAND]; + if ((datum1 & AM29_STATUS_DQ6) == (datum2 & AM29_STATUS_DQ6)) { + // The bits have stopped toggling, so finished. + break; + } + + /* only check on the device that has DQ6 toggling */ + if (DQ6_TGL_DQ1_MASK & datum2) + { + /* read again to make sure error is correct */ + datum1 = addr[AM29_OFFSET_COMMAND]; + datum2 = addr[AM29_OFFSET_COMMAND]; + + dq6_toggles = (datum1 ^ datum2) & AM29_STATUS_DQ6; + + if ((dq6_toggles && (DQ6_TGL_DQ1_MASK & datum2)) && + !(DQ6_TGL_DQ1_MASK ^ (datum2 & AM29_STATUS_DQ1))) { block_start[AM29_OFFSET_COMMAND] = AM29_COMMAND_RESET; return; - } - } - } while (retries-- > 0); + } + } + } while (retries-- > 0); if (0 == retries) { // Failed to write this word, no point in trying to write the rest.