+ LOG_DEBUG("Using target buffer at 0x%08" PRIx32 " and of size 0x%04" PRIx32,
+ source->address, buffer_size);
+
+ /* Programming main loop */
+ while (count > 0) {
+ uint32_t thisrun_count = (count > buffer_size) ? buffer_size : count;
+ uint32_t wsm_error;
+
+ retval = target_write_buffer(target, source->address, thisrun_count, buffer);
+ if (retval != ERROR_OK)
+ goto cleanup;
+
+ buf_set_u32(reg_params[0].value, 0, 32, source->address);
+ buf_set_u32(reg_params[1].value, 0, 32, address);
+ buf_set_u32(reg_params[2].value, 0, 32, thisrun_count / bank->bus_width);
+
+ buf_set_u32(reg_params[3].value, 0, 32, write_command_val);
+ buf_set_u32(reg_params[5].value, 0, 32, busy_pattern_val);
+ buf_set_u32(reg_params[6].value, 0, 32, error_pattern_val);
+
+ LOG_DEBUG("Write 0x%04" PRIx32 " bytes to flash at 0x%08" PRIx32,
+ thisrun_count, address);
+
+ /* Execute algorithm, assume breakpoint for last instruction */
+ retval = target_run_algorithm(target, 0, NULL, 7, reg_params,
+ write_algorithm->address,
+ write_algorithm->address + target_code_size -
+ sizeof(uint32_t),
+ 10000, /* 10s should be enough for max. 32k of data */
+ &arm_algo);
+
+ /* On failure try a fall back to direct word writes */
+ if (retval != ERROR_OK) {
+ cfi_intel_clear_status_register(bank);
+ LOG_ERROR(
+ "Execution of flash algorythm failed. Can't fall back. Please report.");
+ retval = ERROR_FLASH_OPERATION_FAILED;
+ /* retval = ERROR_TARGET_RESOURCE_NOT_AVAILABLE; */
+ /* FIXME To allow fall back or recovery, we must save the actual status
+ * somewhere, so that a higher level code can start recovery. */
+ goto cleanup;
+ }
+
+ /* Check return value from algo code */
+ wsm_error = buf_get_u32(reg_params[4].value, 0, 32) & error_pattern_val;
+ if (wsm_error) {
+ /* read status register (outputs debug information) */
+ uint8_t status;
+ cfi_intel_wait_status_busy(bank, 100, &status);
+ cfi_intel_clear_status_register(bank);
+ retval = ERROR_FLASH_OPERATION_FAILED;
+ goto cleanup;
+ }
+
+ buffer += thisrun_count;
+ address += thisrun_count;
+ count -= thisrun_count;
+
+ keep_alive();
+ }
+
+ /* free up resources */
+cleanup:
+ if (source)
+ target_free_working_area(target, source);
+
+ target_free_working_area(target, write_algorithm);
+
+ destroy_reg_param(®_params[0]);
+ destroy_reg_param(®_params[1]);
+ destroy_reg_param(®_params[2]);
+ destroy_reg_param(®_params[3]);
+ destroy_reg_param(®_params[4]);
+ destroy_reg_param(®_params[5]);
+ destroy_reg_param(®_params[6]);
+
+ return retval;
+}
+
+static int cfi_spansion_write_block_mips(struct flash_bank *bank, const uint8_t *buffer,
+ uint32_t address, uint32_t count)
+{
+ 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;
+ struct reg_param reg_params[10];
+ struct mips32_algorithm mips32_info;
+ struct working_area *write_algorithm;
+ struct working_area *source;
+ uint32_t buffer_size = 32768;
+ uint32_t status;
+ int retval = ERROR_OK;
+
+ /* input parameters -
+ * 4 A0 = source address
+ * 5 A1 = destination address
+ * 6 A2 = number of writes
+ * 7 A3 = flash write command
+ * 8 T0 = constant to mask DQ7 bits (also used for Dq5 with shift)
+ * output parameters -
+ * 9 T1 = 0x80 ok 0x00 bad
+ * temp registers -
+ * 10 T2 = value read from flash to test status
+ * 11 T3 = holding register
+ * unlock registers -
+ * 12 T4 = unlock1_addr
+ * 13 T5 = unlock1_cmd
+ * 14 T6 = unlock2_addr
+ * 15 T7 = unlock2_cmd */
+
+ static const uint32_t mips_word_16_code[] = {
+ /* start: */
+ MIPS32_LHU(9, 0, 4), /* lhu $t1, ($a0) ; out = &saddr */
+ MIPS32_ADDI(4, 4, 2), /* addi $a0, $a0, 2 ; saddr += 2 */
+ MIPS32_SH(13, 0, 12), /* sh $t5, ($t4) ; *fl_unl_addr1 = fl_unl_cmd1 */
+ MIPS32_SH(15, 0, 14), /* sh $t7, ($t6) ; *fl_unl_addr2 = fl_unl_cmd2 */
+ MIPS32_SH(7, 0, 12), /* sh $a3, ($t4) ; *fl_unl_addr1 = fl_write_cmd */
+ MIPS32_SH(9, 0, 5), /* sh $t1, ($a1) ; *daddr = out */
+ MIPS32_NOP, /* nop */
+ /* busy: */
+ MIPS32_LHU(10, 0, 5), /* lhu $t2, ($a1) ; temp1 = *daddr */
+ MIPS32_XOR(11, 9, 10), /* xor $t3, $a0, $t2 ; temp2 = out ^ temp1; */
+ MIPS32_AND(11, 8, 11), /* and $t3, $t0, $t3 ; temp2 = temp2 & DQ7mask */
+ MIPS32_BNE(11, 8, 13), /* bne $t3, $t0, cont ; if (temp2 != DQ7mask) goto cont */
+ MIPS32_NOP, /* nop */
+
+ MIPS32_SRL(10, 8, 2), /* srl $t2,$t0,2 ; temp1 = DQ7mask >> 2 */
+ MIPS32_AND(11, 10, 11), /* and $t3, $t2, $t3 ; temp2 = temp2 & temp1 */
+ MIPS32_BNE(11, 10, NEG16(8)), /* bne $t3, $t2, busy ; if (temp2 != temp1) goto busy */
+ MIPS32_NOP, /* nop */
+
+ MIPS32_LHU(10, 0, 5), /* lhu $t2, ($a1) ; temp1 = *daddr */
+ MIPS32_XOR(11, 9, 10), /* xor $t3, $a0, $t2 ; temp2 = out ^ temp1; */
+ MIPS32_AND(11, 8, 11), /* and $t3, $t0, $t3 ; temp2 = temp2 & DQ7mask */
+ MIPS32_BNE(11, 8, 4), /* bne $t3, $t0, cont ; if (temp2 != DQ7mask) goto cont */
+ MIPS32_NOP, /* nop */
+
+ MIPS32_XOR(9, 9, 9), /* xor $t1, $t1, $t1 ; out = 0 */
+ MIPS32_BEQ(9, 0, 11), /* beq $t1, $zero, done ; if (out == 0) goto done */
+ MIPS32_NOP, /* nop */
+ /* cont: */
+ MIPS32_ADDI(6, 6, NEG16(1)), /* addi, $a2, $a2, -1 ; numwrites-- */
+ MIPS32_BNE(6, 0, 5), /* bne $a2, $zero, cont2 ; if (numwrite != 0) goto cont2 */
+ MIPS32_NOP, /* nop */
+
+ MIPS32_LUI(9, 0), /* lui $t1, 0 */
+ MIPS32_ORI(9, 9, 0x80), /* ori $t1, $t1, 0x80 ; out = 0x80 */
+
+ MIPS32_B(4), /* b done ; goto done */
+ MIPS32_NOP, /* nop */
+ /* cont2: */
+ MIPS32_ADDI(5, 5, 2), /* addi $a0, $a0, 2 ; daddr += 2 */
+ MIPS32_B(NEG16(33)), /* b start ; goto start */
+ MIPS32_NOP, /* nop */
+ /* done: */
+ MIPS32_SDBBP, /* sdbbp ; break(); */
+ };
+
+ mips32_info.common_magic = MIPS32_COMMON_MAGIC;
+ mips32_info.isa_mode = MIPS32_ISA_MIPS32;
+
+ int target_code_size = 0;
+ const uint32_t *target_code_src = NULL;
+
+ switch (bank->bus_width) {
+ case 2:
+ /* Check for DQ5 support */
+ if (cfi_info->status_poll_mask & (1 << 5)) {
+ target_code_src = mips_word_16_code;
+ target_code_size = sizeof(mips_word_16_code);
+ } else {
+ LOG_ERROR("Need DQ5 support");
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ /* target_code_src = mips_word_16_code_dq7only; */
+ /* target_code_size = sizeof(mips_word_16_code_dq7only); */
+ }
+ break;
+ default:
+ LOG_ERROR("Unsupported bank buswidth %d, can't do block memory writes",
+ bank->bus_width);
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ }
+
+ /* flash write code */
+ uint8_t *target_code;
+
+ /* convert bus-width dependent algorithm code to correct endianness */
+ target_code = malloc(target_code_size);
+ if (target_code == NULL) {
+ LOG_ERROR("Out of memory");
+ return ERROR_FAIL;
+ }
+
+ target_buffer_set_u32_array(target, target_code, target_code_size / 4, target_code_src);
+
+ /* allocate working area */
+ retval = target_alloc_working_area(target, target_code_size,
+ &write_algorithm);
+ if (retval != ERROR_OK) {
+ free(target_code);
+ return retval;
+ }
+
+ /* write algorithm code to working area */
+ retval = target_write_buffer(target, write_algorithm->address,
+ target_code_size, target_code);
+ if (retval != ERROR_OK) {
+ free(target_code);
+ return retval;
+ }
+
+ free(target_code);
+
+ /* the following code still assumes target code is fixed 24*4 bytes */
+
+ while (target_alloc_working_area_try(target, buffer_size, &source) != ERROR_OK) {
+ buffer_size /= 2;
+ if (buffer_size <= 256) {
+ /* we already allocated the writing code, but failed to get a
+ * buffer, free the algorithm */
+ target_free_working_area(target, write_algorithm);
+
+ LOG_WARNING(
+ "not enough working area available, can't do block memory writes");
+ return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+ }
+ }
+ ;
+
+ init_reg_param(®_params[0], "a0", 32, PARAM_OUT);
+ init_reg_param(®_params[1], "a1", 32, PARAM_OUT);
+ init_reg_param(®_params[2], "a2", 32, PARAM_OUT);
+ init_reg_param(®_params[3], "a3", 32, PARAM_OUT);
+ init_reg_param(®_params[4], "t0", 32, PARAM_OUT);
+ init_reg_param(®_params[5], "t1", 32, PARAM_IN);
+ init_reg_param(®_params[6], "t4", 32, PARAM_OUT);
+ init_reg_param(®_params[7], "t5", 32, PARAM_OUT);
+ init_reg_param(®_params[8], "t6", 32, PARAM_OUT);
+ init_reg_param(®_params[9], "t7", 32, PARAM_OUT);