Files
OpenCellular/common/spi_flash.c
Vic Yang 9ef82030e6 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>
2014-07-15 09:07:40 +00:00

842 lines
19 KiB
C

/*
* 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