diff options
author | Vic Yang <victoryang@chromium.org> | 2014-07-02 14:55:07 -0700 |
---|---|---|
committer | chrome-internal-fetch <chrome-internal-fetch@google.com> | 2014-07-15 09:07:40 +0000 |
commit | 9ef82030e6a005df990f8f2924cf54076cd2e8da (patch) | |
tree | 95706def6813f4891bb27981cb1698c818f80b30 /common | |
parent | 94126fbfa34030e3469d2d7309d29e199a0daa24 (diff) | |
download | chrome-ec-9ef82030e6a005df990f8f2924cf54076cd2e8da.tar.gz |
Refactor STM32 SPI flash driver
This CL factors out the SPI flash driver to be a STM32-specific SPI
master driver and a common SPI flash driver.
BUG=None
TEST=Verify on Fruitpie
BRANCH=None
Change-Id: I9cca918299bc57a6532c85c4452e73f04550a424
Signed-off-by: Vic Yang <victoryang@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/206582
Reviewed-by: Dmitry Torokhov <dtor@chromium.org>
Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
Reviewed-by: Daming Chen <ddchen@chromium.org>
Tested-by: Daming Chen <ddchen@chromium.org>
Diffstat (limited to 'common')
-rw-r--r-- | common/build.mk | 1 | ||||
-rw-r--r-- | common/spi_flash.c | 841 |
2 files changed, 842 insertions, 0 deletions
diff --git a/common/build.mk b/common/build.mk index 09bbfe2564..6b2dd5dbfb 100644 --- a/common/build.mk +++ b/common/build.mk @@ -60,6 +60,7 @@ common-$(CONFIG_PWM)+=pwm.o common-$(CONFIG_PWM_KBLIGHT)+=pwm_kblight.o common-$(CONFIG_SHA1)+=sha1.o common-$(CONFIG_SOFTWARE_CLZ)+=clz.o +common-$(CONFIG_SPI_FLASH)+=spi_flash.o common-$(CONFIG_SWITCH)+=switch.o common-$(CONFIG_TEMP_SENSOR)+=temp_sensor.o thermal.o common-$(CONFIG_USB_PORT_POWER_DUMB)+=usb_port_power_dumb.o diff --git a/common/spi_flash.c b/common/spi_flash.c new file mode 100644 index 0000000000..20e4ce37cb --- /dev/null +++ b/common/spi_flash.c @@ -0,0 +1,841 @@ +/* + * Copyright (c) 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, particularly Winbond W25Q64FV. + */ + +#include "common.h" +#include "console.h" +#include "shared_mem.h" +#include "spi.h" +#include "spi_flash.h" +#include "timer.h" +#include "util.h" +#include "watchdog.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) + +/* + * Registers for the W25Q64FV SPI flash + */ +#define SPI_FLASH_SR2_SUS (1 << 7) +#define SPI_FLASH_SR2_CMP (1 << 6) +#define SPI_FLASH_SR2_LB3 (1 << 5) +#define SPI_FLASH_SR2_LB2 (1 << 4) +#define SPI_FLASH_SR2_LB1 (1 << 3) +#define SPI_FLASH_SR2_QE (1 << 1) +#define SPI_FLASH_SR2_SRP1 (1 << 0) +#define SPI_FLASH_SR1_SRP0 (1 << 7) +#define SPI_FLASH_SR1_SEC (1 << 6) +#define SPI_FLASH_SR1_TB (1 << 5) +#define SPI_FLASH_SR1_BP2 (1 << 4) +#define SPI_FLASH_SR1_BP1 (1 << 3) +#define SPI_FLASH_SR1_BP0 (1 << 2) +#define SPI_FLASH_SR1_WEL (1 << 1) +#define SPI_FLASH_SR1_BUSY (1 << 0) + +/* Internal buffer used by SPI flash driver */ +static uint8_t buf[SPI_FLASH_MAX_MESSAGE_SIZE]; + +/** + * Computes block write protection range from registers + * Returns start == len == 0 for no protection + * + * @param sr1 Status register 1 + * @param sr2 Status register 2 + * @param start Output pointer for protection start offset + * @param len Output pointer for protection length + * + * @return EC_SUCCESS, or non-zero if any error. + */ +static int reg_to_protect(uint8_t sr1, uint8_t sr2, unsigned int *start, + unsigned int *len) +{ + int blocks; + int size; + uint8_t cmp; + uint8_t sec; + uint8_t tb; + uint8_t bp; + + /* Determine flags */ + cmp = (sr2 & SPI_FLASH_SR2_CMP) ? 1 : 0; + sec = (sr1 & SPI_FLASH_SR1_SEC) ? 1 : 0; + tb = (sr1 & SPI_FLASH_SR1_TB) ? 1 : 0; + bp = (sr1 & (SPI_FLASH_SR1_BP2 | SPI_FLASH_SR1_BP1 | SPI_FLASH_SR1_BP0)) + >> 2; + + /* Bad pointers or invalid data */ + if (!start || !len || sr1 == -1 || sr2 == -1) + return EC_ERROR_INVAL; + + /* Not defined by datasheet */ + if (sec && bp == 6) + return EC_ERROR_INVAL; + + /* Determine granularity (4kb sector or 64kb block) */ + /* Computation using 2 * 1024 is correct */ + size = sec ? (2 * 1024) : (64 * 1024); + + /* Determine number of blocks */ + /* Equivalent to pow(2, bp) with pow(2, 0) = 0 */ + blocks = bp ? (1 << bp) : 0; + /* Datasheet specifies don't care for BP == 4, BP == 5 */ + if (sec && bp == 5) + blocks = (1 << 4); + + /* Determine number of bytes */ + *len = size * blocks; + + /* Determine bottom/top of memory to protect */ + *start = tb ? 0 : + (CONFIG_SPI_FLASH_SIZE - *len) % CONFIG_SPI_FLASH_SIZE; + + /* Reverse computations if complement set */ + if (cmp) { + *start = (*start + *len) % CONFIG_SPI_FLASH_SIZE; + *len = CONFIG_SPI_FLASH_SIZE - *len; + } + + return EC_SUCCESS; +} + +/** + * Computes block write protection registers from range + * + * @param start Desired protection start offset + * @param len Desired protection length + * @param sr1 Output pointer for status register 1 + * @param sr2 Output pointer for status register 2 + * + * @return EC_SUCCESS, or non-zero if any error. + */ +static int protect_to_reg(unsigned int start, unsigned int len, + uint8_t *sr1, uint8_t *sr2) +{ + char cmp = 0; + char sec = 0; + char tb = 0; + char bp = 0; + int blocks; + int size; + + /* Bad pointers */ + if (!sr1 || !sr2 || *sr1 == -1 || *sr2 == -1) + return EC_ERROR_INVAL; + + /* Invalid data */ + if ((start && !len) || start + len > CONFIG_SPI_FLASH_SIZE) + return EC_ERROR_INVAL; + + /* Set complement bit based on whether length is power of 2 */ + if ((len & (len - 1)) != 0) { + cmp = 1; + start = (start + len) % CONFIG_SPI_FLASH_SIZE; + len = CONFIG_SPI_FLASH_SIZE - len; + } + + /* Set bottom/top bit based on start address */ + /* Do not set if len == 0 or len == CONFIG_SPI_FLASH_SIZE */ + if (!start && (len % CONFIG_SPI_FLASH_SIZE)) + tb = 1; + + /* Set sector bit and determine block length based on protect length */ + if (len == 0 || len >= 128 * 1024) { + sec = 0; + size = 64 * 1024; + } else if (len >= 4 * 1024 && len <= 32 * 1024) { + sec = 1; + size = 2 * 1024; + } else + return EC_ERROR_INVAL; + + /* Determine number of blocks */ + if (len % size != 0) + return EC_ERROR_INVAL; + blocks = len / size; + + /* Determine bp = log2(blocks) with log2(0) = 0 */ + bp = blocks ? (31 - __builtin_clz(blocks)) : 0; + + /* Clear bits */ + *sr1 &= ~(SPI_FLASH_SR1_SEC | SPI_FLASH_SR1_TB | + SPI_FLASH_SR1_BP2 | SPI_FLASH_SR1_BP1 | SPI_FLASH_SR1_BP0); + *sr2 &= ~SPI_FLASH_SR2_CMP; + + /* Set bits */ + *sr1 |= (sec ? SPI_FLASH_SR1_SEC : 0) | (tb ? SPI_FLASH_SR1_TB : 0) + | (bp << 2); + *sr2 |= (cmp ? SPI_FLASH_SR2_CMP : 0); + + return EC_SUCCESS; +} + +/** + * 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(&cmd, 1, NULL, 0); +} + +/** + * Returns the contents of SPI flash status register 1 + * @return register contents or -1 on error + */ +uint8_t spi_flash_get_status1(void) +{ + uint8_t cmd = SPI_FLASH_READ_SR1; + uint8_t resp; + + if (spi_transaction(&cmd, 1, &resp, 1) != EC_SUCCESS) + return -1; + + return resp; +} + +/** + * Returns the contents of SPI flash status register 2 + * @return register contents or -1 on error + */ +uint8_t spi_flash_get_status2(void) +{ + uint8_t cmd = SPI_FLASH_READ_SR2; + uint8_t resp; + + if (spi_transaction(&cmd, 1, &resp, 1) != EC_SUCCESS) + return -1; + + 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; + + /* Register has protection */ + rv = spi_flash_check_wp(); + if (rv) + return rv; + + /* Enable writing to SPI flash */ + rv = spi_flash_write_enable(); + if (rv) + return rv; + + if (reg2 == -1) + rv = spi_transaction(cmd, 2, NULL, 0); + else + rv = spi_transaction(cmd, 3, NULL, 0); + if (rv) + return rv; + + return rv; +} + +/** + * Returns the content of SPI flash + * + * @param buf Buffer to write flash contents + * @param offset Flash offset to start reading from + * @param bytes Number of bytes to read. Limited by receive buffer to 256. + * + * @return EC_SUCCESS, or non-zero if any error. + */ +int spi_flash_read(uint8_t *buf_usr, unsigned int offset, unsigned int bytes) +{ + uint8_t cmd[4] = {SPI_FLASH_READ, + (offset >> 16) & 0xFF, + (offset >> 8) & 0xFF, + offset & 0xFF}; + + if (offset + bytes > CONFIG_SPI_FLASH_SIZE) + return EC_ERROR_INVAL; + + return spi_transaction(cmd, 4, buf_usr, bytes); +} + +/** + * 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; + + /* 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; + + /* 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(cmd, 4, NULL, 0); + if (rv) + return rv; + + return rv; +} + +/** + * 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_SPI_FLASH_SIZE) + 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; + } + } + + /* 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 const *data) +{ + int rv; + + /* Invalid input */ + if (!data || offset + bytes > CONFIG_SPI_FLASH_SIZE || + bytes > SPI_FLASH_MAX_WRITE_SIZE) + return EC_ERROR_INVAL; + + /* 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, bytes); + + /* Compose instruction */ + buf[0] = SPI_FLASH_PAGE_PRGRM; + buf[1] = (offset >> 16) & 0xFF; + buf[2] = (offset >> 8) & 0xFF; + buf[3] = offset & 0xFF; + + return spi_transaction(buf, 4 + bytes, NULL, 0); +} + +/** + * Returns the SPI flash manufacturer ID and device ID [8:0] + * + * @return flash manufacturer + device ID or -1 on error + */ +uint16_t spi_flash_get_id(void) +{ + uint8_t cmd[4] = {SPI_FLASH_MFR_DEV_ID, 0, 0, 0}; + uint8_t resp[2]; + + if (spi_transaction(cmd, 4, resp, 2) != EC_SUCCESS) + return -1; + + return (resp[4] << 8) | resp[5]; +} + +/** + * Returns the SPI flash JEDEC ID (manufacturer ID, memory type, and capacity) + * + * @return flash JEDEC ID or -1 on error + */ +uint32_t spi_flash_get_jedec_id(void) +{ + uint8_t cmd = SPI_FLASH_JEDEC_ID; + uint32_t resp; + + if (spi_transaction(&cmd, 1, (uint8_t *)&resp, 4) != EC_SUCCESS) + return -1; + + return resp; +} + +/** + * Returns the SPI flash unique ID (serial) + * + * @return flash unique ID or -1 on error + */ +uint64_t spi_flash_get_unique_id(void) +{ + uint8_t cmd[5] = {SPI_FLASH_UNIQUE_ID, 0, 0, 0, 0}; + uint64_t resp; + + if (spi_transaction(cmd, 5, (uint8_t *)&resp, 8) != EC_SUCCESS) + return -1; + + return resp; +} + +/** + * Check for SPI flash status register write protection + * Cannot sample WP pin, will consider hardware WP to be no protection + * + * @param wp Status register write protection mode + * + * @return EC_SUCCESS for no protection, or non-zero if error. + */ +int spi_flash_check_wp(void) +{ + int sr2 = spi_flash_get_status2(); + + /* Power cycle or OTP protection */ + if (sr2 & SPI_FLASH_SR2_SRP1) + return EC_ERROR_ACCESS_DENIED; + + return EC_SUCCESS; +} + +/** + * 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 == -1 || sr2 == -1 || offset + bytes > CONFIG_SPI_FLASH_SIZE) + return EC_ERROR_INVAL; + + /* Compute current protect range */ + rv = 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 == -1 || sr2 == -1 || offset + bytes > CONFIG_SPI_FLASH_SIZE) + return EC_ERROR_INVAL; + + /* Compute desired protect range */ + rv = 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) +{ + uint32_t jedec; + uint64_t unique; + int rv; + + spi_enable(1); + + /* Wait for previous operation to complete */ + rv = spi_flash_wait(); + if (rv) + return rv; + + jedec = spi_flash_get_jedec_id(); + unique = spi_flash_get_unique_id(); + + ccprintf("Manufacturer ID: %02x\nDevice ID: %02x %02x\n", + ((uint8_t *)&jedec)[0], ((uint8_t *)&jedec)[1], + ((uint8_t *)&jedec)[2]); + ccprintf("Unique ID: %02x %02x %02x %02x %02x %02x %02x %02x\n", + ((uint8_t *)&unique)[0], ((uint8_t *)&unique)[1], + ((uint8_t *)&unique)[2], ((uint8_t *)&unique)[3], + ((uint8_t *)&unique)[4], ((uint8_t *)&unique)[5], + ((uint8_t *)&unique)[6], ((uint8_t *)&unique)[7]); + ccprintf("Capacity: %4d MB\n", + SPI_FLASH_SIZE(((uint8_t *)&jedec)[2]) / 1024); + + return rv; +} +DECLARE_CONSOLE_COMMAND(spi_flashinfo, command_spi_flashinfo, + NULL, + "Print SPI flash info", + NULL); + +#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(1); + + /* Chip has protection */ + if (spi_flash_check_protect(offset, bytes)) + return EC_ERROR_ACCESS_DENIED; + + /* Wait for previous operation to complete */ + rv = spi_flash_wait(); + if (rv) + return rv; + + ccprintf("Erasing %d bytes at 0x%x...\n", bytes, offset); + rv = spi_flash_erase(offset, bytes); + if (rv) + return rv; + + /* Wait for the operation to complete */ + return spi_flash_wait(); +} +DECLARE_CONSOLE_COMMAND(spi_flasherase, command_spi_flasherase, + "offset [bytes]", + "Erase flash", + NULL); + +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(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) { + watchdog_reload(); + + /* First write multiples of 256, then (bytes % 256) last */ + write_len = ((bytes % SPI_FLASH_MAX_WRITE_SIZE) == bytes) ? + bytes : SPI_FLASH_MAX_WRITE_SIZE; + + /* Wait for previous operation to complete */ + rv = spi_flash_wait(); + if (rv) + return rv; + + /* Perform write */ + rv = spi_flash_write(offset, write_len, buf); + if (rv) + return rv; + + offset += write_len; + bytes -= write_len; + } + + ASSERT(bytes == 0); + + return spi_flash_wait(); +} +DECLARE_CONSOLE_COMMAND(spi_flashwrite, command_spi_flashwrite, + "offset [bytes]", + "Write pattern to flash", + NULL); + +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(1); + + /* Can't read past size of memory */ + if (offset + bytes > CONFIG_SPI_FLASH_SIZE) + 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", + NULL); + +static int command_spi_flashread_sr(int argc, char **argv) +{ + spi_enable(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", + NULL); + +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(1); + + /* Wait for previous operation to complete */ + rv = spi_flash_wait(); + if (rv) + return rv; + + ccprintf("Writing 0x%02x to status register 1, ", val1); + ccprintf("0x%02x to status register 2...\n", val2); + rv = spi_flash_set_status(val1, val2); + if (rv) + return rv; + + /* Wait for the operation to complete */ + return spi_flash_wait(); +} +DECLARE_CONSOLE_COMMAND(spi_flash_wsr, command_spi_flashwrite_sr, + "value1 value2", + "Write to status registers", + NULL); + +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(1); + + /* Wait for previous operation to complete */ + rv = spi_flash_wait(); + if (rv) + return rv; + + ccprintf("Setting protection for 0x%06x to 0x%06x\n", val1, val1+val2); + rv = spi_flash_set_protect(val1, val2); + if (rv) + return rv; + + /* Wait for the operation to complete */ + return spi_flash_wait(); +} +DECLARE_CONSOLE_COMMAND(spi_flash_prot, command_spi_flashprotect, + "offset len", + "Set block protection", + NULL); +#endif |