in core/flash/spi_flash.c [1341:1483]
int spi_flash_clear_block_protect (const struct spi_flash *flash)
{
struct flash_xfer xfer;
uint8_t reg[2] = {0};
uint8_t cmd_len = 1;
uint8_t mask = 0x83;
uint8_t cmpl_bp = 0;
uint8_t cmpl_mask = 0xff;
uint8_t vendor;
uint16_t device_id;
int status;
if (flash == NULL) {
return SPI_FLASH_INVALID_ARGUMENT;
}
status = spi_flash_get_device_id (flash, &vendor, &device_id);
if (status != 0) {
return status;
}
platform_mutex_lock (&flash->state->lock);
if ((vendor != FLASH_ID_MICROCHIP) && (vendor != FLASH_ID_INFINEON) &&
(!((vendor == FLASH_ID_MACRONIX) &&
(FLASH_ID_DEVICE_SERIES (device_id) == FLASH_ID_MX66UW)))) {
/* Depending on the quad enable bit, the block clear needs to be handled differently:
* - If the quad bit is in SR1, then we need to be sure not to clear it.
* - On some devices, writing only 1 byte to SR1 will automatically clear SR2. On these
* devices we need to write both SR1 and SR2 to ensure the quad bit doesn't get
* cleared. */
switch (flash->state->quad_enable) {
case SPI_FLASH_SFDP_QUAD_QE_BIT6_SR1:
mask = 0xc3;
break;
case SPI_FLASH_SFDP_QUAD_QE_BIT1_SR2:
cmd_len = 2;
break;
case SPI_FLASH_SFDP_QUAD_QE_BIT1_SR2_35:
FLASH_XFER_INIT_READ_REG (xfer, FLASH_CMD_RDSR2, ®[1], 1, 0);
status = flash->spi->xfer (flash->spi, &xfer);
if (status != 0) {
goto exit;
}
break;
default:
break;
}
FLASH_XFER_INIT_READ_REG (xfer, FLASH_CMD_RDSR, reg, cmd_len, 0);
status = flash->spi->xfer (flash->spi, &xfer);
if (status != 0) {
goto exit;
}
/* On Winbond devices with QE bit in SR2, ensure bit 6 in SR2
* (complement block protect in SR1) is cleared to ensure block protection is cleared. */
if (vendor == FLASH_ID_WINBOND) {
cmpl_bp = 0x40;
cmpl_mask = 0x03;
}
if ((reg[0] & ~mask) || (reg[1] & cmpl_bp)) {
if (flash->state->quad_enable == SPI_FLASH_SFDP_QUAD_QE_BIT1_SR2_35) {
cmd_len = 2;
}
reg[0] &= mask;
reg[1] &= cmpl_mask;
status = spi_flash_write_register (flash, FLASH_CMD_WRSR, reg, cmd_len,
flash->state->sr1_volatile);
}
}
else if (vendor == FLASH_ID_INFINEON) {
/* Infineon devices have a different block protect mechanism. The block protect bits are
* in the status register 1, but the content of STR1 is different from legacy devices.
* The block protect bits are cleared by writing 0 to the block protect bits. Along with that,
* single byte write has to be enabled by writing 0 to volatile configuration register 4. */
FLASH_XFER_INIT_READ_REG (xfer, FLASH_CMD_RDSR, reg, 1, 0);
status = flash->spi->xfer (flash->spi, &xfer);
if (status != 0) {
goto exit;
}
if (reg[0] & 0x1c) {
reg[0] &= ~0x1c;
status = spi_flash_write_register_with_4byte_address (flash, FLASH_CMD_INFINEON_WR_ARG,
INFINEON_STR1V, reg, 1);
if (status != 0) {
goto exit;
}
}
/* Enable single byte write */
reg[0] = 0xC0;
status = spi_flash_write_register_with_4byte_address (flash, FLASH_CMD_INFINEON_WR_ARG,
INFINEON_CFR4V, reg, 1);
}
else if ((vendor == FLASH_ID_MACRONIX) &&
(FLASH_ID_DEVICE_SERIES (device_id) == FLASH_ID_MX66UW)) {
/* Macronix MX66U OSPI flash device has different block protect mechanism from Macronix
* SPI flash devices. The block protect bits in status register are different. Double page
* programming has to be enabled by writing to volatile configuration register 2. */
FLASH_XFER_INIT_READ_REG (xfer, FLASH_CMD_RDSR, reg, 1, 0);
status = flash->spi->xfer (flash->spi, &xfer);
if (status != 0) {
goto exit;
}
if (reg[0] & 0x3c) {
reg[0] &= ~0x3c;
status = spi_flash_write_register (flash, FLASH_CMD_WRSR, reg, cmd_len,
flash->state->sr1_volatile);
if (status != 0) {
goto exit;
}
}
/* Modify ECC configuration to enable double page programming. */
reg[0] = 0x02;
status = spi_flash_write_register_with_4byte_address (flash, FLASH_CMD_MACRONIX_WRCR2,
MX66UW_CFR2V_ECS, reg, 1);
if (status != 0) {
goto exit;
}
}
else {
/* Microchip flash does not have block protect bits in the status register. Instead, there
* is a single command that unlocks all protection. This needs to be sent after every power
* cycle before any erase or program operations can be performed. */
FLASH_XFER_INIT_CMD_ONLY (xfer, FLASH_CMD_GBULK, 0);
status = flash->spi->xfer (flash->spi, &xfer);
}
exit:
platform_mutex_unlock (&flash->state->lock);
return status;
}