/* Write up to CONFIG_FLASH_WRITE_IDEAL_SIZE bytes at once */ static int write_batch(int byte_offset, int is_info_bank, int words, const uint8_t *data) { volatile uint32_t *fsh_wr_data = GREG32_ADDR(FLASH, FSH_WR_DATA0); uint32_t val; int i; /* Load the write buffer. */ for (i = 0; i < words; i++) { /* * We have to write 32-bit values, but we can't guarantee * alignment for the data. We'll just assemble the word * manually to avoid alignment faults. Note that we're assuming * little-endian order here. */ val = ((data[3] << 24) | (data[2] << 16) | (data[1] << 8) | data[0]); *fsh_wr_data = val; data += 4; fsh_wr_data++; } return do_flash_op(OP_WRITE_BLOCK, is_info_bank, byte_offset, words); }
/* Send cmd to flash controller. */ static int _flash_cmd(uint32_t fidx, uint32_t cmd) { int cnt, retval; /* Activate controller. */ GREG32(FLASH, FSH_PE_EN) = FSH_OP_ENABLE; GREG32_ADDR(FLASH, FSH_PE_CONTROL0)[fidx] = cmd; /* wait on FSH_PE_EN (means the operation started) */ cnt = 500; /* TODO(mschilder): pick sane value. */ do { retval = GREG32(FLASH, FSH_PE_EN); } while (retval && cnt--); if (retval) { debug_printf("ERROR: FLASH_FSH_PE_EN never went to 0, is "); debug_printf("0x%x after timeout\n", retval); return E_FL_TIMEOUT; } /* * wait 100us before checking FSH_PE_CONTROL (means the operation * ended) */ cnt = 1000000; do { retval = GREG32_ADDR(FLASH, FSH_PE_CONTROL0)[fidx]; } while (retval && --cnt); if (retval) { debug_printf ("ERROR: FLASH_FSH_PE_CONTROL%d is 0x%x after timeout\n", fidx, retval); GREG32_ADDR(FLASH, FSH_PE_CONTROL0)[fidx] = 0; return E_FL_TIMEOUT; } return 0; }
/* Drop run level to at least medium. */ static void init_runlevel(const enum permission_level desired_level) { volatile uint32_t *const reg_addrs[] = { GREG32_ADDR(GLOBALSEC, CPU0_S_PERMISSION), GREG32_ADDR(GLOBALSEC, DDMA0_PERMISSION), }; int i; /* Permission registers drop by 1 level (e.g. HIGHEST -> HIGH) * each time a write is encountered (the value written does * not matter). So we repeat writes and reads, until the * desired level is reached. */ for (i = 0; i < ARRAY_SIZE(reg_addrs); i++) { uint32_t current_level; while (1) { current_level = *reg_addrs[i]; if (current_level <= desired_level) break; *reg_addrs[i] = desired_level; } } }
static int do_flash_op(enum flash_op op, int is_info_bank, int byte_offset, int words) { volatile uint32_t *fsh_pe_control; uint32_t opcode, tmp, errors; int retry_count, max_attempts, extra_prog_pulse, i; int timedelay_us = 100; uint32_t prev_error = 0; /* Make sure the smart program/erase algorithms are enabled. */ if (!GREAD(FLASH, FSH_TIMING_PROG_SMART_ALGO_ON) || !GREAD(FLASH, FSH_TIMING_ERASE_SMART_ALGO_ON)) { CPRINTF("%s:%d\n", __func__, __LINE__); return EC_ERROR_UNIMPLEMENTED; } /* Error status is self-clearing. Read it until it does (we hope). */ for (i = 0; i < 50; i++) { tmp = GREAD(FLASH, FSH_ERROR); if (!tmp) break; usleep(timedelay_us); } /* If we can't clear the error status register then something is wrong. */ if (tmp) { CPRINTF("%s:%d\n", __func__, __LINE__); return EC_ERROR_UNKNOWN; } /* We have two flash banks. Adjust offset and registers accordingly. */ if (is_info_bank) { /* Only INFO bank operations are supported. */ fsh_pe_control = GREG32_ADDR(FLASH, FSH_PE_CONTROL1); } else if (byte_offset >= CFG_FLASH_HALF) { byte_offset -= CFG_FLASH_HALF; fsh_pe_control = GREG32_ADDR(FLASH, FSH_PE_CONTROL1); } else { fsh_pe_control = GREG32_ADDR(FLASH, FSH_PE_CONTROL0); } /* What are we doing? */ switch (op) { case OP_ERASE_BLOCK: if (is_info_bank) /* Erasing the INFO bank from the RW section is * unsupported. */ return EC_ERROR_INVAL; opcode = 0x31415927; words = 0; /* don't care, really */ /* This number is based on the TSMC spec Nme=Terase/Tsme */ max_attempts = 45; break; case OP_WRITE_BLOCK: opcode = 0x27182818; words--; /* count register is zero-based */ /* This number is based on the TSMC spec Nmp=Tprog/Tsmp */ max_attempts = 9; break; case OP_READ_BLOCK: if (!is_info_bank) /* This code path only supports reading from * the INFO bank. */ return EC_ERROR_INVAL; opcode = 0x16021765; words = 1; max_attempts = 9; break; default: return EC_ERROR_INVAL; } /* * Set the parameters. For writes, we assume the write buffer is * already filled before we call this function. */ GWRITE_FIELD(FLASH, FSH_TRANS, OFFSET, byte_offset / 4); /* word offset */ GWRITE_FIELD(FLASH, FSH_TRANS, MAINB, is_info_bank ? 1 : 0); GWRITE_FIELD(FLASH, FSH_TRANS, SIZE, words); /* TODO: Make sure this function isn't getting called "too often" in * between erases. */ extra_prog_pulse = 0; for (retry_count = 0; retry_count < max_attempts; retry_count++) { /* Kick it off */ GWRITE(FLASH, FSH_PE_EN, 0xb11924e1); *fsh_pe_control = opcode; /* Wait for completion. 150ms should be enough * (crosbug.com/p/45366). */ for (i = 0; i < 1500; i++) { tmp = *fsh_pe_control; if (!tmp) break; usleep(timedelay_us); } /* Timed out waiting for control register to clear */ if (tmp) { CPRINTF("%s:%d\n", __func__, __LINE__); return EC_ERROR_UNKNOWN; } /* Check error status */ errors = GREAD(FLASH, FSH_ERROR); if (errors && (errors != prev_error)) { prev_error = errors; CPRINTF("%s:%d errors %x fsh_pe_control %p\n", __func__, __LINE__, errors, fsh_pe_control); } /* Error status is self-clearing. Read it until it does * (we hope). */ for (i = 0; i < 50; i++) { tmp = GREAD(FLASH, FSH_ERROR); if (!tmp) break; usleep(timedelay_us); } /* If we can't clear the error status register then something * is wrong. */ if (tmp) { CPRINTF("%s:%d\n", __func__, __LINE__); return EC_ERROR_UNKNOWN; } /* The operation was successful. */ if (!errors) { /* From the spec: * "In addition, one more program pulse is needed after * program verification is passed." */ if (op == OP_WRITE_BLOCK && !extra_prog_pulse) { extra_prog_pulse = 1; max_attempts++; continue; } return EC_SUCCESS; } /* If there were errors after completion retry. */ watchdog_reload(); } CPRINTF("%s:%d, retry count %d\n", __func__, __LINE__, retry_count); return EC_ERROR_UNKNOWN; }