Patrick Rudolph has uploaded this change for review. ( https://review.coreboot.org/25105
Change subject: [WIP]drivers/spi: Winbond specific writeprotection enable ......................................................................
[WIP]drivers/spi: Winbond specific writeprotection enable
Extend the SPI interface to enable write protection.
Change-Id: Ie3765b013855538eca37bc7800d3f9d5d09b8402 Signed-off-by: Patrick Rudolph patrick.rudolph@9elements.com --- M src/drivers/spi/spi_flash.c M src/drivers/spi/winbond.c M src/include/spi_flash.h 3 files changed, 196 insertions(+), 3 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/05/25105/1
diff --git a/src/drivers/spi/spi_flash.c b/src/drivers/spi/spi_flash.c index 9bad9f3..769ef48 100644 --- a/src/drivers/spi/spi_flash.c +++ b/src/drivers/spi/spi_flash.c @@ -452,6 +452,28 @@ return flash->ops->get_write_protection(flash, region); }
+int spi_flash_set_write_protected(const struct spi_flash *flash, + const struct region *region) +{ + struct region flash_region = { 0 }; + + if (!flash) + return -1; + + flash_region.size = flash->size; + + if (!region_is_subregion(&flash_region, region)) + return -1; + + if (flash->ops->set_write_protection) { + printk(BIOS_WARNING, "SPI: Setting write-protection is not " + "implemented for this vendor.\n"); + return 0; + } + + return flash->ops->set_write_protection(flash, region); +} + static uint32_t volatile_group_count CAR_GLOBAL;
int spi_flash_volatile_group_begin(const struct spi_flash *flash) diff --git a/src/drivers/spi/winbond.c b/src/drivers/spi/winbond.c index 6c1ce0d..2992dc3 100644 --- a/src/drivers/spi/winbond.c +++ b/src/drivers/spi/winbond.c @@ -31,6 +31,8 @@ #define CMD_W25_DP 0xb9 /* Deep Power-down */ #define CMD_W25_RES 0xab /* Release from DP, and Read Signature */ #define CMD_W25_B_LOCK_STATUS 0x3d /* Read Block/Sector Lock bit */ +#define CMD_W25_B_LOCK_SET 0x36 /* Set Block/Sector Lock bit */ +#define CMD_W25_B_LOCK_CLEAR 0x39 /* Clear Block/Sector Lock bit */
struct winbond_spi_flash_params { uint16_t id; @@ -319,9 +321,157 @@ return ret; } if (reg & STS_W25_WPS) - return winbond_read_b_lock_protection(flash, region); + return winbond_get_b_lock_protection(flash, region); else - return winbond_read_b_protect_protection(flash, region); + return winbond_get_b_protect_protection(flash, region); +} + +/* + * Only available on some devices. + * Set Block Protection Bits of each block. + * + * Returns: + * -1 on error + * 0 on success + */ +static int winbond_set_b_lock_protection(const struct spi_flash *flash, + const struct region *region) +{ + int ret, i; + u8 cmd[5], reg8; + + cmd[4] = 0x00; + + /* Writeable */ + cmd[0] = CMD_W25_B_LOCK_CLEAR; + for (i = 0; i < region->offset; i += flash->sector_size) { + cmd[1] = (i >> 16) & 0xff; + cmd[2] = (i >> 8) & 0xff; + cmd[3] = i & 0xff; + + ret = spi_flash_cmd_ext(&flash->spi, cmd, sizeof(cmd), + ®8, sizeof(reg8)); + if (ret) + return ret; + } + + /* Readonly */ + cmd[0] = CMD_W25_B_LOCK_SET; + for (i = 0; i < region->size; i += flash->sector_size) { + cmd[1] = ((region->offset + i) >> 16) & 0xff; + cmd[2] = ((region->offset + i) >> 8) & 0xff; + cmd[3] = (region->offset + i) & 0xff; + + ret = spi_flash_cmd_ext(&flash->spi, cmd, sizeof(cmd), + ®8, sizeof(reg8)); + if (ret) + return ret; + } + + /* Writeable */ + cmd[0] = CMD_W25_B_LOCK_CLEAR; + for (i = region->offset + region->size; i < flash->size; + i += flash->sector_size) { + cmd[1] = (i >> 16) & 0xff; + cmd[2] = (i >> 8) & 0xff; + cmd[3] = i & 0xff; + + ret = spi_flash_cmd_ext(&flash->spi, cmd, sizeof(cmd), + ®8, sizeof(reg8)); + if (ret) + return ret; + } + return 1; +} + +/* + * Available on all devices. + * Write block protect bits to Status/Status2 Reg. + * + * Returns: + * -1 on error + * 0 on success + */ +static int winbond_set_b_protect_protection(const struct spi_flash *flash, + const struct region *region) +{ + int ret, shift, block; + union { + u8 u; + struct { + u8 busy : 1; + u8 wel : 1; + u8 bp : 3; + u8 tb : 1; + u8 sec : 1; + u8 srp0 : 1; + } s; + } reg1 = {.u = 0}; + union { + u8 u; + struct { + u8 : 6; + u8 cmp : 1; + u8 : 1; + } s; + } reg2 = {.u = 0}; + + /* Need to touch TOP or BOTTOM */ + if (region->offset == 0 || + region->offset + region->size == flash->size) + return -1; + + reg1.s.tb = region->offset == 0; + + reg1.s.sec = region->size < flash->block_size; + + if (reg1.s.sec && (region->size > flash->block_size / 2)) + return -1; + + if (reg1.s.sec) + block = flash->sector_size; + else + block = max(flash->block_size, flash->size / 64); + + if (region->size > 0) { + for (shift = 0; shift <= 0x7; shift++) { + if (region->size == block << shift) + break; + } + if (shift > 0x7) + return -1; + reg1.s.bp = shift; + } else + reg1.s.bp = 0; + + ret = spi_flash_cmd(&flash->spi, CMD_W25_WRSR, ®1.u, sizeof(reg1.u)); + if (ret) + return ret; + + return spi_flash_cmd(&flash->spi, CMD_W25_WRSR2, ®2.u, + sizeof(reg2.u)); +} + +static int winbond_set_write_protection(const struct spi_flash *flash, + const struct region *region) +{ + u8 reg = STS_W25_WPS; + + /* Check alignment */ + if (region->offset & (flash->sector_size - 1)) + return -1; + if (region->size & (flash->sector_size - 1)) + return -1; + + if (IS_ENABLED(CONFIG_SPI_FLASH_HAS_BLOCK_LOCK_BITS)) { + int ret = spi_flash_cmd(&flash->spi, CMD_W25_WRSR3, ®, + sizeof(reg)); + if (ret) + return ret; + return winbond_set_b_lock_protection(flash, region); + } + + return winbond_set_b_protect_protection(flash, region); }
static const struct spi_flash_ops spi_flash_ops = { @@ -334,6 +484,7 @@ .read = spi_flash_cmd_read_fast, #endif .get_write_protection = winbond_get_write_protection, + .set_write_protection = winbond_set_write_protection, };
int spi_flash_probe_winbond(const struct spi_slave *spi, u8 *idcode, diff --git a/src/include/spi_flash.h b/src/include/spi_flash.h index 82705d4..d1dfa92 100644 --- a/src/include/spi_flash.h +++ b/src/include/spi_flash.h @@ -42,7 +42,8 @@ int (*status)(const struct spi_flash *flash, u8 *reg); int (*get_write_protection)(const struct spi_flash *flash, const struct region *region); - + int (*set_write_protection)(const struct spi_flash *flash, + const struct region *region); };
struct spi_flash { @@ -113,6 +114,25 @@ int spi_flash_is_write_protected(const struct spi_flash *flash, const struct region *region); /* + * Enable the vendor dependent SPI flash write protection. The region not + * covered by write-protection will be set to write-able state. + * Only a single write-protected region is supported. + * Some flash ICs require the region to be aligned in the block size, sector + * size or page size. + * Some flash ICs require the region to start at TOP or BOTTOM. + * + * @param flash : A SPI flash device + * @param region: A subregion of the device's region + * + * Returns: + * -1 on error + * -1 if the region alignment is bad + * 0 on success + */ +int spi_flash_set_write_protected(const struct spi_flash *flash, + const struct region *region); + +/* * Some SPI controllers require exclusive access to SPI flash when volatile * operations like erase or write are being performed. In such cases, * volatile_group_begin will gain exclusive access to SPI flash if not already