diff options
Diffstat (limited to 'common/spi_flash.c')
-rw-r--r-- | common/spi_flash.c | 707 |
1 files changed, 0 insertions, 707 deletions
diff --git a/common/spi_flash.c b/common/spi_flash.c deleted file mode 100644 index e202e1e17d..0000000000 --- a/common/spi_flash.c +++ /dev/null @@ -1,707 +0,0 @@ -/* - * Copyright 2014 The Chromium OS Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - * - * SPI flash driver for Chrome EC. - */ - -#include "common.h" -#include "console.h" -#include "host_command.h" -#include "shared_mem.h" -#include "spi.h" -#include "spi_flash.h" -#include "spi_flash_reg.h" -#include "timer.h" -#include "util.h" -#include "watchdog.h" -#include "ec_commands.h" -#include "flash.h" - -/* - * Time to sleep when chip is busy - */ -#define SPI_FLASH_SLEEP_USEC 100 - -/* - * This is the max time for 32kb flash erase - */ -#define SPI_FLASH_TIMEOUT_USEC (800*MSEC) - -/* Internal buffer used by SPI flash driver */ -static uint8_t buf[SPI_FLASH_MAX_MESSAGE_SIZE]; - -/** - * Waits for chip to finish current operation. Must be called after - * erase/write operations to ensure successive commands are executed. - * - * @return EC_SUCCESS or error on timeout - */ -int spi_flash_wait(void) -{ - timestamp_t timeout; - - timeout.val = get_time().val + SPI_FLASH_TIMEOUT_USEC; - /* Wait until chip is not busy */ - while (spi_flash_get_status1() & SPI_FLASH_SR1_BUSY) { - usleep(SPI_FLASH_SLEEP_USEC); - - if (get_time().val > timeout.val) - return EC_ERROR_TIMEOUT; - } - - return EC_SUCCESS; -} - -/** - * Set the write enable latch - */ -static int spi_flash_write_enable(void) -{ - uint8_t cmd = SPI_FLASH_WRITE_ENABLE; - return spi_transaction(SPI_FLASH_DEVICE, &cmd, 1, NULL, 0); -} - -/** - * Returns the contents of SPI flash status register 1 - * @return register contents or 0xff on error - */ -uint8_t spi_flash_get_status1(void) -{ - uint8_t cmd = SPI_FLASH_READ_SR1; - uint8_t resp; - - if (spi_transaction(SPI_FLASH_DEVICE, &cmd, 1, &resp, 1) != EC_SUCCESS) - return 0xff; - - return resp; -} - -/** - * Returns the contents of SPI flash status register 2 - * @return register contents or 0xff on error - */ -uint8_t spi_flash_get_status2(void) -{ - uint8_t cmd = SPI_FLASH_READ_SR2; - uint8_t resp; - - /* Second status register not present */ -#ifndef CONFIG_SPI_FLASH_HAS_SR2 - return 0; -#endif - - if (spi_transaction(SPI_FLASH_DEVICE, &cmd, 1, &resp, 1) != EC_SUCCESS) - return 0xff; - - return resp; -} - -/** - * Sets the SPI flash status registers (non-volatile bits only) - * Pass reg2 == -1 to only set reg1. - * - * @param reg1 Status register 1 - * @param reg2 Status register 2 (optional) - * - * @return EC_SUCCESS, or non-zero if any error. - */ -int spi_flash_set_status(int reg1, int reg2) -{ - uint8_t cmd[3] = {SPI_FLASH_WRITE_SR, reg1, reg2}; - int rv = EC_SUCCESS; - - /* fail if both HW pin is asserted and SRP(s) is 1 */ - if (spi_flash_check_wp() != SPI_WP_NONE && - (crec_flash_get_protect() & - EC_FLASH_PROTECT_GPIO_ASSERTED) != 0) - return EC_ERROR_ACCESS_DENIED; - - /* Enable writing to SPI flash */ - rv = spi_flash_write_enable(); - if (rv) - return rv; - - /* Second status register not present */ -#ifndef CONFIG_SPI_FLASH_HAS_SR2 - reg2 = -1; -#endif - - if (reg2 == -1) - rv = spi_transaction(SPI_FLASH_DEVICE, cmd, 2, NULL, 0); - else - rv = spi_transaction(SPI_FLASH_DEVICE, cmd, 3, NULL, 0); - if (rv) - return rv; - - /* SRP update takes up to 10 ms, so wait for transaction to finish */ - spi_flash_wait(); - - return rv; -} - -/** - * Returns the content of SPI flash - * - * @param buf_usr Buffer to write flash contents - * @param offset Flash offset to start reading from - * @param bytes Number of bytes to read. - * - * @return EC_SUCCESS, or non-zero if any error. - */ -int spi_flash_read(uint8_t *buf_usr, unsigned int offset, unsigned int bytes) -{ - int i, read_size, ret, spi_addr; - uint8_t cmd[4]; - if (offset + bytes > CONFIG_FLASH_SIZE_BYTES) - return EC_ERROR_INVAL; - cmd[0] = SPI_FLASH_READ; - for (i = 0; i < bytes; i += read_size) { - spi_addr = offset + i; - cmd[1] = (spi_addr >> 16) & 0xFF; - cmd[2] = (spi_addr >> 8) & 0xFF; - cmd[3] = spi_addr & 0xFF; - read_size = MIN((bytes - i), SPI_FLASH_MAX_READ_SIZE); - ret = spi_transaction(SPI_FLASH_DEVICE, - cmd, - 4, - buf_usr + i, - read_size); - if (ret != EC_SUCCESS) - break; - msleep(CONFIG_SPI_FLASH_READ_WAIT_MS); - } - return ret; -} - -/** - * Erase a block of SPI flash. - * - * @param offset Flash offset to start erasing - * @param block Block size in kb (4 or 32) - * - * @return EC_SUCCESS, or non-zero if any error. - */ -static int spi_flash_erase_block(unsigned int offset, unsigned int block) -{ - uint8_t cmd[4]; - int rv = EC_SUCCESS; - - /* Invalid block size */ - if (block != 4 && block != 32) - return EC_ERROR_INVAL; - - /* Not block aligned */ - if ((offset % (block * 1024)) != 0) - return EC_ERROR_INVAL; - - /* Enable writing to SPI flash */ - rv = spi_flash_write_enable(); - if (rv) - return rv; - - /* Compose instruction */ - cmd[0] = (block == 4) ? SPI_FLASH_ERASE_4KB : SPI_FLASH_ERASE_32KB; - cmd[1] = (offset >> 16) & 0xFF; - cmd[2] = (offset >> 8) & 0xFF; - cmd[3] = offset & 0xFF; - - rv = spi_transaction(SPI_FLASH_DEVICE, cmd, 4, NULL, 0); - if (rv) - return rv; - - /* Wait for previous operation to complete */ - return spi_flash_wait(); -} - -/** - * Erase SPI flash. - * - * @param offset Flash offset to start erasing - * @param bytes Number of bytes to erase - * - * @return EC_SUCCESS, or non-zero if any error. - */ -int spi_flash_erase(unsigned int offset, unsigned int bytes) -{ - int rv = EC_SUCCESS; - - /* Invalid input */ - if (offset + bytes > CONFIG_FLASH_SIZE_BYTES) - return EC_ERROR_INVAL; - - /* Not aligned to sector (4kb) */ - if (offset % 4096 || bytes % 4096) - return EC_ERROR_INVAL; - - /* Largest unit is block (32kb) */ - if (offset % (32 * 1024) == 0) { - while (bytes != (bytes % (32 * 1024))) { - rv = spi_flash_erase_block(offset, 32); - if (rv) - return rv; - - bytes -= 32 * 1024; - offset += 32 * 1024; - /* - * Refresh watchdog since we may be erasing a large - * number of blocks. - */ - watchdog_reload(); - } - } - - /* Largest unit is sector (4kb) */ - while (bytes != (bytes % (4 * 1024))) { - rv = spi_flash_erase_block(offset, 4); - if (rv) - return rv; - - bytes -= 4 * 1024; - offset += 4 * 1024; - } - - return rv; -} - -/** - * Write to SPI flash. Assumes already erased. - * Limited to SPI_FLASH_MAX_WRITE_SIZE by chip. - * - * @param offset Flash offset to write - * @param bytes Number of bytes to write - * @param data Data to write to flash - * - * @return EC_SUCCESS, or non-zero if any error. - */ -int spi_flash_write(unsigned int offset, unsigned int bytes, - const uint8_t *data) -{ - int rv, write_size; - - /* Invalid input */ - if (!data || offset + bytes > CONFIG_FLASH_SIZE_BYTES || - bytes > SPI_FLASH_MAX_WRITE_SIZE) - return EC_ERROR_INVAL; - - while (bytes > 0) { - watchdog_reload(); - /* Write length can not go beyond the end of the flash page */ - write_size = MIN(bytes, SPI_FLASH_MAX_WRITE_SIZE - - (offset & (SPI_FLASH_MAX_WRITE_SIZE - 1))); - - /* Wait for previous operation to complete */ - rv = spi_flash_wait(); - if (rv) - return rv; - - /* Enable writing to SPI flash */ - rv = spi_flash_write_enable(); - if (rv) - return rv; - - /* Copy data to send buffer; buffers may overlap */ - memmove(buf + 4, data, write_size); - - /* Compose instruction */ - buf[0] = SPI_FLASH_PAGE_PRGRM; - buf[1] = (offset) >> 16; - buf[2] = (offset) >> 8; - buf[3] = offset; - - rv = spi_transaction(SPI_FLASH_DEVICE, - buf, 4 + write_size, NULL, 0); - if (rv) - return rv; - - data += write_size; - offset += write_size; - bytes -= write_size; - } - - /* Wait for previous operation to complete */ - return spi_flash_wait(); -} - -/** - * Gets the SPI flash JEDEC ID (manufacturer ID, memory type, and capacity) - * - * @param dest Destination buffer; must be 3 bytes long - * @return EC_SUCCESS or non-zero on error - */ -int spi_flash_get_jedec_id(uint8_t *dest) -{ - uint8_t cmd = SPI_FLASH_JEDEC_ID; - - return spi_transaction(SPI_FLASH_DEVICE, &cmd, 1, dest, 3); -} - -/** - * Gets the SPI flash manufacturer and device ID - * - * @param dest Destination buffer; must be 2 bytes long - * @return EC_SUCCESS or non-zero on error - */ -int spi_flash_get_mfr_dev_id(uint8_t *dest) -{ - uint8_t cmd[4] = {SPI_FLASH_MFR_DEV_ID, 0, 0, 0}; - - return spi_transaction(SPI_FLASH_DEVICE, cmd, sizeof(cmd), dest, 2); -} - -/** - * Gets the SPI flash unique ID (serial) - * - * @param dest Destination buffer; must be 8 bytes long - * @return EC_SUCCESS or non-zero on error - */ -int spi_flash_get_unique_id(uint8_t *dest) -{ - uint8_t cmd[5] = {SPI_FLASH_UNIQUE_ID, 0, 0, 0, 0}; - - return spi_transaction(SPI_FLASH_DEVICE, cmd, sizeof(cmd), dest, 8); -} - -/** - * Check for SPI flash status register write protection - * Cannot sample WP pin, so caller should sample it if necessary, if - * SPI_WP_HARDWARE is returned. - * - * @return enum spi_flash_wp status based on protection - */ -enum spi_flash_wp spi_flash_check_wp(void) -{ - int sr1_prot = spi_flash_get_status1() & SPI_FLASH_SR1_SRP0; - int sr2_prot = spi_flash_get_status2() & SPI_FLASH_SR2_SRP1; - - if (sr2_prot) - return sr1_prot ? SPI_WP_PERMANENT : SPI_WP_POWER_CYCLE; - else if (sr1_prot) - return SPI_WP_HARDWARE; - - return SPI_WP_NONE; -} - -/** - * Set SPI flash status register write protection - * - * @param wp Status register write protection mode - * - * @return EC_SUCCESS for no protection, or non-zero if error. - */ -int spi_flash_set_wp(enum spi_flash_wp w) -{ - int sr1 = spi_flash_get_status1(); - int sr2 = spi_flash_get_status2(); - - switch (w) { - case SPI_WP_NONE: - sr1 &= ~SPI_FLASH_SR1_SRP0; - sr2 &= ~SPI_FLASH_SR2_SRP1; - break; - case SPI_WP_HARDWARE: - sr1 |= SPI_FLASH_SR1_SRP0; - sr2 &= ~SPI_FLASH_SR2_SRP1; - break; - case SPI_WP_POWER_CYCLE: - sr1 &= ~SPI_FLASH_SR1_SRP0; - sr2 |= SPI_FLASH_SR2_SRP1; - break; - case SPI_WP_PERMANENT: - sr1 |= SPI_FLASH_SR1_SRP0; - sr2 |= SPI_FLASH_SR2_SRP1; - break; - default: - return EC_ERROR_INVAL; - } - - return spi_flash_set_status(sr1, sr2); -} - -/** - * Check for SPI flash block write protection - * - * @param offset Flash block offset to check - * @param bytes Flash block length to check - * - * @return EC_SUCCESS for no protection, or non-zero if error. - */ -int spi_flash_check_protect(unsigned int offset, unsigned int bytes) -{ - uint8_t sr1 = spi_flash_get_status1(); - uint8_t sr2 = spi_flash_get_status2(); - unsigned int start; - unsigned int len; - int rv = EC_SUCCESS; - - /* Invalid value */ - if (sr1 == 0xff || sr2 == 0xff || - offset + bytes > CONFIG_FLASH_SIZE_BYTES) - return EC_ERROR_INVAL; - - /* Compute current protect range */ - rv = spi_flash_reg_to_protect(sr1, sr2, &start, &len); - if (rv) - return rv; - - /* Check if ranges overlap */ - if (MAX(start, offset) < MIN(start + len, offset + bytes)) - return EC_ERROR_ACCESS_DENIED; - - return EC_SUCCESS; -} - -/** - * Set SPI flash block write protection - * If offset == bytes == 0, remove protection. - * - * @param offset Flash block offset to protect - * @param bytes Flash block length to protect - * - * @return EC_SUCCESS, or non-zero if error. - */ -int spi_flash_set_protect(unsigned int offset, unsigned int bytes) -{ - int rv; - uint8_t sr1 = spi_flash_get_status1(); - uint8_t sr2 = spi_flash_get_status2(); - - /* Invalid values */ - if (sr1 == 0xff || sr2 == 0xff || - offset + bytes > CONFIG_FLASH_SIZE_BYTES) - return EC_ERROR_INVAL; - - /* Compute desired protect range */ - rv = spi_flash_protect_to_reg(offset, bytes, &sr1, &sr2); - if (rv) - return rv; - - return spi_flash_set_status(sr1, sr2); -} - -static int command_spi_flashinfo(int argc, char **argv) -{ - uint8_t jedec[3]; - uint8_t unique[8]; - int rv; - - /* TODO(tomhughes): use board function to get devices. */ - spi_enable(SPI_FLASH_DEVICE, 1); - - /* Wait for previous operation to complete */ - rv = spi_flash_wait(); - if (rv) - return rv; - - spi_flash_get_jedec_id(jedec); - spi_flash_get_unique_id(unique); - - ccprintf("Manufacturer ID: %02x\nDevice ID: %02x %02x\n", - jedec[0], jedec[1], jedec[2]); - ccprintf("Unique ID: %02x %02x %02x %02x %02x %02x %02x %02x\n", - unique[0], unique[1], unique[2], unique[3], - unique[4], unique[5], unique[6], unique[7]); - ccprintf("Capacity: %4d kB\n", SPI_FLASH_SIZE(jedec[2]) / 1024); - - return rv; -} -DECLARE_CONSOLE_COMMAND(spi_flashinfo, command_spi_flashinfo, - NULL, - "Print SPI flash info"); - -#ifdef CONFIG_HOSTCMD_FLASH_SPI_INFO -static enum ec_status flash_command_spi_info(struct host_cmd_handler_args *args) -{ - struct ec_response_flash_spi_info *r = args->response; - - spi_flash_get_jedec_id(r->jedec); - r->reserved0 = 0; - spi_flash_get_mfr_dev_id(r->mfr_dev_id); - r->sr1 = spi_flash_get_status1(); - r->sr2 = spi_flash_get_status2(); - - args->response_size = sizeof(*r); - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_FLASH_SPI_INFO, - flash_command_spi_info, - EC_VER_MASK(0)); -#endif /* CONFIG_HOSTCMD_FLASH_SPI_INFO */ - -#ifdef CONFIG_CMD_SPI_FLASH -static int command_spi_flasherase(int argc, char **argv) -{ - int offset = -1; - int bytes = 4096; - int rv = parse_offset_size(argc, argv, 1, &offset, &bytes); - - if (rv) - return rv; - - spi_enable(SPI_FLASH_DEVICE, 1); - - /* Chip has protection */ - if (spi_flash_check_protect(offset, bytes)) - return EC_ERROR_ACCESS_DENIED; - - ccprintf("Erasing %d bytes at 0x%x...\n", bytes, offset); - return spi_flash_erase(offset, bytes); -} -DECLARE_CONSOLE_COMMAND(spi_flasherase, command_spi_flasherase, - "offset [bytes]", - "Erase flash"); - -static int command_spi_flashwrite(int argc, char **argv) -{ - int offset = -1; - int bytes = SPI_FLASH_MAX_WRITE_SIZE; - int write_len; - int rv = EC_SUCCESS; - int i; - - rv = parse_offset_size(argc, argv, 1, &offset, &bytes); - if (rv) - return rv; - - spi_enable(SPI_FLASH_DEVICE, 1); - - /* Chip has protection */ - if (spi_flash_check_protect(offset, bytes)) - return EC_ERROR_ACCESS_DENIED; - - /* Fill the data buffer with a pattern */ - for (i = 0; i < SPI_FLASH_MAX_WRITE_SIZE; i++) - buf[i] = i; - - ccprintf("Writing %d bytes to 0x%x...\n", bytes, offset); - while (bytes > 0) { - /* First write multiples of 256, then (bytes % 256) last */ - write_len = ((bytes % SPI_FLASH_MAX_WRITE_SIZE) == bytes) ? - bytes : SPI_FLASH_MAX_WRITE_SIZE; - - /* Perform write */ - rv = spi_flash_write(offset, write_len, buf); - if (rv) - return rv; - - offset += write_len; - bytes -= write_len; - } - - ASSERT(bytes == 0); - - return rv; -} -DECLARE_CONSOLE_COMMAND(spi_flashwrite, command_spi_flashwrite, - "offset [bytes]", - "Write pattern to flash"); - -static int command_spi_flashread(int argc, char **argv) -{ - int i; - int offset = -1; - int bytes = -1; - int read_len; - int rv; - - rv = parse_offset_size(argc, argv, 1, &offset, &bytes); - if (rv) - return rv; - - spi_enable(SPI_FLASH_DEVICE, 1); - - /* Can't read past size of memory */ - if (offset + bytes > CONFIG_FLASH_SIZE_BYTES) - return EC_ERROR_INVAL; - - /* Wait for previous operation to complete */ - rv = spi_flash_wait(); - if (rv) - return rv; - - ccprintf("Reading %d bytes from 0x%x...\n", bytes, offset); - /* Read <= 256 bytes to avoid allocating another buffer */ - while (bytes > 0) { - watchdog_reload(); - - /* First read (bytes % 256), then in multiples of 256 */ - read_len = (bytes % SPI_FLASH_MAX_READ_SIZE) ? - (bytes % SPI_FLASH_MAX_READ_SIZE) : - SPI_FLASH_MAX_READ_SIZE; - - rv = spi_flash_read(buf, offset, read_len); - if (rv) - return rv; - - for (i = 0; i < read_len; i++) { - if (i % 16 == 0) - ccprintf("%02x:", offset + i); - - ccprintf(" %02x", buf[i]); - - if (i % 16 == 15 || i == read_len - 1) - ccputs("\n"); - } - - offset += read_len; - bytes -= read_len; - } - - ASSERT(bytes == 0); - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(spi_flashread, command_spi_flashread, - "offset bytes", - "Read flash"); - -static int command_spi_flashread_sr(int argc, char **argv) -{ - spi_enable(SPI_FLASH_DEVICE, 1); - - ccprintf("Status Register 1: 0x%02x\n", spi_flash_get_status1()); - ccprintf("Status Register 2: 0x%02x\n", spi_flash_get_status2()); - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(spi_flash_rsr, command_spi_flashread_sr, - NULL, - "Read status registers"); - -static int command_spi_flashwrite_sr(int argc, char **argv) -{ - int val1 = 0; - int val2 = 0; - int rv = parse_offset_size(argc, argv, 1, &val1, &val2); - - if (rv) - return rv; - - spi_enable(SPI_FLASH_DEVICE, 1); - - ccprintf("Writing 0x%02x to status register 1, ", val1); - ccprintf("0x%02x to status register 2...\n", val2); - return spi_flash_set_status(val1, val2); -} -DECLARE_CONSOLE_COMMAND(spi_flash_wsr, command_spi_flashwrite_sr, - "value1 value2", - "Write to status registers"); - -static int command_spi_flashprotect(int argc, char **argv) -{ - int val1 = 0; - int val2 = 0; - int rv = parse_offset_size(argc, argv, 1, &val1, &val2); - - if (rv) - return rv; - - spi_enable(SPI_FLASH_DEVICE, 1); - - ccprintf("Setting protection for 0x%06x to 0x%06x\n", val1, val1+val2); - return spi_flash_set_protect(val1, val2); -} -DECLARE_CONSOLE_COMMAND(spi_flash_prot, command_spi_flashprotect, - "offset len", - "Set block protection"); -#endif |