int spi_flash_clear_block_protect()

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, &reg[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;
}