-
- cfi_info->pri_id = cfi_query_u16(bank, 0, 0x13);
- cfi_info->pri_addr = cfi_query_u16(bank, 0, 0x15);
- cfi_info->alt_id = cfi_query_u16(bank, 0, 0x17);
- cfi_info->alt_addr = cfi_query_u16(bank, 0, 0x19);
-
- DEBUG("qry: '%c%c%c', pri_id: 0x%4.4x, pri_addr: 0x%4.4x, alt_id: 0x%4.4x, alt_addr: 0x%4.4x", cfi_info->qry[0], cfi_info->qry[1], cfi_info->qry[2], cfi_info->pri_id, cfi_info->pri_addr, cfi_info->alt_id, cfi_info->alt_addr);
-
- cfi_info->vcc_min = cfi_query_u8(bank, 0, 0x1b);
- cfi_info->vcc_max = cfi_query_u8(bank, 0, 0x1c);
- cfi_info->vpp_min = cfi_query_u8(bank, 0, 0x1d);
- cfi_info->vpp_max = cfi_query_u8(bank, 0, 0x1e);
- cfi_info->word_write_timeout_typ = cfi_query_u8(bank, 0, 0x1f);
- cfi_info->buf_write_timeout_typ = cfi_query_u8(bank, 0, 0x20);
- cfi_info->block_erase_timeout_typ = cfi_query_u8(bank, 0, 0x21);
- cfi_info->chip_erase_timeout_typ = cfi_query_u8(bank, 0, 0x22);
- cfi_info->word_write_timeout_max = cfi_query_u8(bank, 0, 0x23);
- cfi_info->buf_write_timeout_max = cfi_query_u8(bank, 0, 0x24);
- cfi_info->block_erase_timeout_max = cfi_query_u8(bank, 0, 0x25);
- cfi_info->chip_erase_timeout_max = cfi_query_u8(bank, 0, 0x26);
-
- DEBUG("Vcc min: %1.1x.%1.1x, Vcc max: %1.1x.%1.1x, Vpp min: %1.1x.%1.1x, Vpp max: %1.1x.%1.1x",
- (cfi_info->vcc_min & 0xf0) >> 4, cfi_info->vcc_min & 0x0f,
- (cfi_info->vcc_max & 0xf0) >> 4, cfi_info->vcc_max & 0x0f,
- (cfi_info->vpp_min & 0xf0) >> 4, cfi_info->vpp_min & 0x0f,
- (cfi_info->vpp_max & 0xf0) >> 4, cfi_info->vpp_max & 0x0f);
- DEBUG("typ. word write timeout: %u, typ. buf write timeout: %u, typ. block erase timeout: %u, typ. chip erase timeout: %u", 1 << cfi_info->word_write_timeout_typ, 1 << cfi_info->buf_write_timeout_typ,
- 1 << cfi_info->block_erase_timeout_typ, 1 << cfi_info->chip_erase_timeout_typ);
- DEBUG("max. word write timeout: %u, max. buf write timeout: %u, max. block erase timeout: %u, max. chip erase timeout: %u", (1 << cfi_info->word_write_timeout_max) * (1 << cfi_info->word_write_timeout_typ),
- (1 << cfi_info->buf_write_timeout_max) * (1 << cfi_info->buf_write_timeout_typ),
- (1 << cfi_info->block_erase_timeout_max) * (1 << cfi_info->block_erase_timeout_typ),
- (1 << cfi_info->chip_erase_timeout_max) * (1 << cfi_info->chip_erase_timeout_typ));
-
- cfi_info->dev_size = cfi_query_u8(bank, 0, 0x27);
- cfi_info->interface_desc = cfi_query_u16(bank, 0, 0x28);
- cfi_info->max_buf_write_size = cfi_query_u16(bank, 0, 0x2a);
- cfi_info->num_erase_regions = cfi_query_u8(bank, 0, 0x2c);
-
- DEBUG("size: 0x%x, interface desc: %i, max buffer write size: %x", 1 << cfi_info->dev_size, cfi_info->interface_desc, (1 << cfi_info->max_buf_write_size));
-
- if (((1 << cfi_info->dev_size) * bank->bus_width / bank->chip_width) != bank->size)
- {
- WARNING("configuration specifies 0x%x size, but a 0x%x size flash was found", bank->size, 1 << cfi_info->dev_size);
- }
-
- if (cfi_info->num_erase_regions)
- {
- int i;
- int num_sectors = 0;
- int sector = 0;
- u32 offset = 0;
- cfi_info->erase_region_info = malloc(4 * cfi_info->num_erase_regions);
-
- for (i = 0; i < cfi_info->num_erase_regions; i++)
+
+ return ERROR_OK;
+}
+
+static int cfi_intel_write_words(struct flash_bank *bank, uint8_t *word, uint32_t wordcount, uint32_t address)
+{
+ int retval;
+ struct cfi_flash_bank *cfi_info = bank->driver_priv;
+ struct target *target = bank->target;
+ uint8_t command[8];
+
+ /* Calculate buffer size and boundary mask */
+ uint32_t buffersize = (1UL << cfi_info->max_buf_write_size) * (bank->bus_width / bank->chip_width);
+ uint32_t buffermask = buffersize-1;
+ uint32_t bufferwsize;
+
+ /* Check for valid range */
+ if (address & buffermask)
+ {
+ LOG_ERROR("Write address at base 0x%" PRIx32 ", address %" PRIx32 " not aligned to 2^%d boundary",
+ bank->base, address, cfi_info->max_buf_write_size);
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+ switch (bank->chip_width)
+ {
+ case 4 : bufferwsize = buffersize / 4; break;
+ case 2 : bufferwsize = buffersize / 2; break;
+ case 1 : bufferwsize = buffersize; break;
+ default:
+ LOG_ERROR("Unsupported chip width %d", bank->chip_width);
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+
+ bufferwsize/=(bank->bus_width / bank->chip_width);
+
+
+ /* Check for valid size */
+ if (wordcount > bufferwsize)
+ {
+ LOG_ERROR("Number of data words %" PRId32 " exceeds available buffersize %" PRId32 , wordcount, buffersize);
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+
+ /* Write to flash buffer */
+ cfi_intel_clear_status_register(bank);
+
+ /* Initiate buffer operation _*/
+ cfi_command(bank, 0xE8, command);
+ if ((retval = target_write_memory(target, address, bank->bus_width, 1, command)) != ERROR_OK)
+ {
+ return retval;
+ }
+ if (cfi_intel_wait_status_busy(bank, 1000 * (1 << cfi_info->buf_write_timeout_max)) != 0x80)
+ {
+ cfi_command(bank, 0xff, command);
+ if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command)) != ERROR_OK)
+ {
+ return retval;
+ }
+
+ LOG_ERROR("couldn't start buffer write operation at base 0x%" PRIx32 ", address %" PRIx32 , bank->base, address);
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+
+ /* Write buffer wordcount-1 and data words */
+ cfi_command(bank, bufferwsize-1, command);
+ if ((retval = target_write_memory(target, address, bank->bus_width, 1, command)) != ERROR_OK)
+ {
+ return retval;
+ }
+
+ if ((retval = target_write_memory(target, address, bank->bus_width, bufferwsize, word)) != ERROR_OK)
+ {
+ return retval;
+ }
+
+ /* Commit write operation */
+ cfi_command(bank, 0xd0, command);
+ if ((retval = target_write_memory(target, address, bank->bus_width, 1, command)) != ERROR_OK)
+ {
+ return retval;
+ }
+ if (cfi_intel_wait_status_busy(bank, 1000 * (1 << cfi_info->buf_write_timeout_max)) != 0x80)
+ {
+ cfi_command(bank, 0xff, command);
+ if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command)) != ERROR_OK)
+ {
+ return retval;
+ }
+
+ LOG_ERROR("Buffer write at base 0x%" PRIx32 ", address %" PRIx32 " failed.", bank->base, address);
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+
+ return ERROR_OK;
+}
+
+static int cfi_spansion_write_word(struct flash_bank *bank, uint8_t *word, uint32_t address)
+{
+ int retval;
+ struct cfi_flash_bank *cfi_info = bank->driver_priv;
+ struct cfi_spansion_pri_ext *pri_ext = cfi_info->pri_ext;
+ struct target *target = bank->target;
+ uint8_t command[8];
+
+ cfi_command(bank, 0xaa, command);
+ if ((retval = target_write_memory(target, flash_address(bank, 0, pri_ext->_unlock1), bank->bus_width, 1, command)) != ERROR_OK)
+ {
+ return retval;
+ }
+
+ cfi_command(bank, 0x55, command);
+ if ((retval = target_write_memory(target, flash_address(bank, 0, pri_ext->_unlock2), bank->bus_width, 1, command)) != ERROR_OK)
+ {
+ return retval;
+ }
+
+ cfi_command(bank, 0xa0, command);
+ if ((retval = target_write_memory(target, flash_address(bank, 0, pri_ext->_unlock1), bank->bus_width, 1, command)) != ERROR_OK)
+ {
+ return retval;
+ }
+
+ if ((retval = target_write_memory(target, address, bank->bus_width, 1, word)) != ERROR_OK)
+ {
+ return retval;
+ }
+
+ if (cfi_spansion_wait_status_busy(bank, 1000 * (1 << cfi_info->word_write_timeout_max)) != ERROR_OK)
+ {
+ cfi_command(bank, 0xf0, command);
+ if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command)) != ERROR_OK)
+ {
+ return retval;
+ }
+
+ LOG_ERROR("couldn't write word at base 0x%" PRIx32 ", address %" PRIx32 , bank->base, address);
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+
+ return ERROR_OK;
+}
+
+static int cfi_spansion_write_words(struct flash_bank *bank, uint8_t *word, uint32_t wordcount, uint32_t address)
+{
+ int retval;
+ struct cfi_flash_bank *cfi_info = bank->driver_priv;
+ struct target *target = bank->target;
+ uint8_t command[8];
+ struct cfi_spansion_pri_ext *pri_ext = cfi_info->pri_ext;
+
+ /* Calculate buffer size and boundary mask */
+ uint32_t buffersize = (1UL << cfi_info->max_buf_write_size) * (bank->bus_width / bank->chip_width);
+ uint32_t buffermask = buffersize-1;
+ uint32_t bufferwsize;
+
+ /* Check for valid range */
+ if (address & buffermask)
+ {
+ LOG_ERROR("Write address at base 0x%" PRIx32 ", address %" PRIx32 " not aligned to 2^%d boundary", bank->base, address, cfi_info->max_buf_write_size);
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+ switch (bank->chip_width)
+ {
+ case 4 : bufferwsize = buffersize / 4; break;
+ case 2 : bufferwsize = buffersize / 2; break;
+ case 1 : bufferwsize = buffersize; break;
+ default:
+ LOG_ERROR("Unsupported chip width %d", bank->chip_width);
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+
+ bufferwsize/=(bank->bus_width / bank->chip_width);
+
+ /* Check for valid size */
+ if (wordcount > bufferwsize)
+ {
+ LOG_ERROR("Number of data words %" PRId32 " exceeds available buffersize %" PRId32, wordcount, buffersize);
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+
+ // Unlock
+ cfi_command(bank, 0xaa, command);
+ if ((retval = target_write_memory(target, flash_address(bank, 0, pri_ext->_unlock1), bank->bus_width, 1, command)) != ERROR_OK)
+ {
+ return retval;
+ }
+
+ cfi_command(bank, 0x55, command);
+ if ((retval = target_write_memory(target, flash_address(bank, 0, pri_ext->_unlock2), bank->bus_width, 1, command)) != ERROR_OK)
+ {
+ return retval;
+ }
+
+ // Buffer load command
+ cfi_command(bank, 0x25, command);
+ if ((retval = target_write_memory(target, address, bank->bus_width, 1, command)) != ERROR_OK)
+ {
+ return retval;
+ }
+
+ /* Write buffer wordcount-1 and data words */
+ cfi_command(bank, bufferwsize-1, command);
+ if ((retval = target_write_memory(target, address, bank->bus_width, 1, command)) != ERROR_OK)
+ {
+ return retval;
+ }
+
+ if ((retval = target_write_memory(target, address, bank->bus_width, bufferwsize, word)) != ERROR_OK)
+ {
+ return retval;
+ }
+
+ /* Commit write operation */
+ cfi_command(bank, 0x29, command);
+ if ((retval = target_write_memory(target, address, bank->bus_width, 1, command)) != ERROR_OK)
+ {
+ return retval;
+ }
+
+ if (cfi_spansion_wait_status_busy(bank, 1000 * (1 << cfi_info->word_write_timeout_max)) != ERROR_OK)
+ {
+ cfi_command(bank, 0xf0, command);
+ if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command)) != ERROR_OK)
+ {
+ return retval;
+ }
+
+ LOG_ERROR("couldn't write block at base 0x%" PRIx32 ", address %" PRIx32 ", size %" PRIx32 , bank->base, address, bufferwsize);
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+
+ return ERROR_OK;
+}
+
+static int cfi_write_word(struct flash_bank *bank, uint8_t *word, uint32_t address)
+{
+ struct cfi_flash_bank *cfi_info = bank->driver_priv;
+
+ switch (cfi_info->pri_id)
+ {
+ case 1:
+ case 3:
+ return cfi_intel_write_word(bank, word, address);
+ break;
+ case 2:
+ return cfi_spansion_write_word(bank, word, address);
+ break;
+ default:
+ LOG_ERROR("cfi primary command set %i unsupported", cfi_info->pri_id);
+ break;
+ }
+
+ return ERROR_FLASH_OPERATION_FAILED;
+}
+
+static int cfi_write_words(struct flash_bank *bank, uint8_t *word, uint32_t wordcount, uint32_t address)
+{
+ struct cfi_flash_bank *cfi_info = bank->driver_priv;
+
+ switch (cfi_info->pri_id)
+ {
+ case 1:
+ case 3:
+ return cfi_intel_write_words(bank, word, wordcount, address);
+ break;
+ case 2:
+ return cfi_spansion_write_words(bank, word, wordcount, address);
+ break;
+ default:
+ LOG_ERROR("cfi primary command set %i unsupported", cfi_info->pri_id);
+ break;
+ }
+
+ return ERROR_FLASH_OPERATION_FAILED;
+}
+
+int cfi_write(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count)
+{
+ struct cfi_flash_bank *cfi_info = bank->driver_priv;
+ struct target *target = bank->target;
+ uint32_t address = bank->base + offset; /* address of first byte to be programmed */
+ uint32_t write_p, copy_p;
+ int align; /* number of unaligned bytes */
+ int blk_count; /* number of bus_width bytes for block copy */
+ uint8_t current_word[CFI_MAX_BUS_WIDTH * 4]; /* word (bus_width size) currently being programmed */
+ int i;
+ int retval;
+
+ if (bank->target->state != TARGET_HALTED)
+ {
+ LOG_ERROR("Target not halted");
+ return ERROR_TARGET_NOT_HALTED;
+ }
+
+ if (offset + count > bank->size)
+ return ERROR_FLASH_DST_OUT_OF_BANK;
+
+ if (cfi_info->qry[0] != 'Q')
+ return ERROR_FLASH_BANK_NOT_PROBED;
+
+ /* start at the first byte of the first word (bus_width size) */
+ write_p = address & ~(bank->bus_width - 1);
+ if ((align = address - write_p) != 0)
+ {
+ LOG_INFO("Fixup %d unaligned head bytes", align);
+
+ for (i = 0; i < bank->bus_width; i++)
+ current_word[i] = 0;
+ copy_p = write_p;
+
+ /* copy bytes before the first write address */
+ for (i = 0; i < align; ++i, ++copy_p)
+ {
+ uint8_t byte;
+ if ((retval = target_read_memory(target, copy_p, 1, 1, &byte)) != ERROR_OK)
+ {
+ return retval;
+ }
+ cfi_add_byte(bank, current_word, byte);
+ }
+
+ /* add bytes from the buffer */
+ for (; (i < bank->bus_width) && (count > 0); i++)
+ {
+ cfi_add_byte(bank, current_word, *buffer++);
+ count--;
+ copy_p++;
+ }
+
+ /* if the buffer is already finished, copy bytes after the last write address */
+ for (; (count == 0) && (i < bank->bus_width); ++i, ++copy_p)
+ {
+ uint8_t byte;
+ if ((retval = target_read_memory(target, copy_p, 1, 1, &byte)) != ERROR_OK)
+ {
+ return retval;
+ }
+ cfi_add_byte(bank, current_word, byte);
+ }
+
+ retval = cfi_write_word(bank, current_word, write_p);
+ if (retval != ERROR_OK)
+ return retval;
+ write_p = copy_p;
+ }
+
+ /* handle blocks of bus_size aligned bytes */
+ blk_count = count & ~(bank->bus_width - 1); /* round down, leave tail bytes */
+ switch (cfi_info->pri_id)
+ {
+ /* try block writes (fails without working area) */
+ case 1:
+ case 3:
+ retval = cfi_intel_write_block(bank, buffer, write_p, blk_count);
+ break;
+ case 2:
+ retval = cfi_spansion_write_block(bank, buffer, write_p, blk_count);
+ break;
+ default:
+ LOG_ERROR("cfi primary command set %i unsupported", cfi_info->pri_id);
+ retval = ERROR_FLASH_OPERATION_FAILED;
+ break;
+ }
+ if (retval == ERROR_OK)
+ {
+ /* Increment pointers and decrease count on succesful block write */
+ buffer += blk_count;
+ write_p += blk_count;
+ count -= blk_count;
+ }
+ else
+ {
+ if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE)
+ {
+ //adjust buffersize for chip width
+ uint32_t buffersize = (1UL << cfi_info->max_buf_write_size) * (bank->bus_width / bank->chip_width);
+ uint32_t buffermask = buffersize-1;
+ uint32_t bufferwsize;
+
+ switch (bank->chip_width)
+ {
+ case 4 : bufferwsize = buffersize / 4; break;
+ case 2 : bufferwsize = buffersize / 2; break;
+ case 1 : bufferwsize = buffersize; break;
+ default:
+ LOG_ERROR("Unsupported chip width %d", bank->chip_width);
+ return ERROR_FLASH_OPERATION_FAILED;
+ }
+
+ bufferwsize/=(bank->bus_width / bank->chip_width);
+
+ /* fall back to memory writes */
+ while (count >= (uint32_t)bank->bus_width)
+ {
+ int fallback;
+ if ((write_p & 0xff) == 0)
+ {
+ LOG_INFO("Programming at %08" PRIx32 ", count %08" PRIx32 " bytes remaining", write_p, count);
+ }
+ fallback = 1;
+ if ((bufferwsize > 0) && (count >= buffersize) && !(write_p & buffermask))
+ {
+ retval = cfi_write_words(bank, buffer, bufferwsize, write_p);
+ if (retval == ERROR_OK)
+ {
+ buffer += buffersize;
+ write_p += buffersize;
+ count -= buffersize;
+ fallback = 0;
+ }
+ }
+ /* try the slow way? */
+ if (fallback)
+ {
+ for (i = 0; i < bank->bus_width; i++)
+ current_word[i] = 0;
+
+ for (i = 0; i < bank->bus_width; i++)
+ {
+ cfi_add_byte(bank, current_word, *buffer++);
+ }
+
+ retval = cfi_write_word(bank, current_word, write_p);
+ if (retval != ERROR_OK)
+ return retval;
+
+ write_p += bank->bus_width;
+ count -= bank->bus_width;
+ }
+ }
+ }
+ else
+ return retval;
+ }
+
+ /* return to read array mode, so we can read from flash again for padding */
+ cfi_command(bank, 0xf0, current_word);
+ if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, current_word)) != ERROR_OK)
+ {
+ return retval;
+ }
+ cfi_command(bank, 0xff, current_word);
+ if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, current_word)) != ERROR_OK)
+ {
+ return retval;
+ }
+
+ /* handle unaligned tail bytes */
+ if (count > 0)
+ {
+ LOG_INFO("Fixup %" PRId32 " unaligned tail bytes", count);
+
+ copy_p = write_p;
+ for (i = 0; i < bank->bus_width; i++)
+ current_word[i] = 0;
+
+ for (i = 0; (i < bank->bus_width) && (count > 0); ++i, ++copy_p)