/* SPDX-License-Identifier: BSD-3-Clause */ /* * Copyright (c) 2020-2022 Rockchip Electronics Co., Ltd. */ #include #include "Soc.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define HAL_SNOR_DEBUG #ifdef HAL_SNOR_DEBUG #define DEBUG_SNOR DEBUG_WARN #else #define DEBUG_SNOR DEBUG_INFO #endif struct FLASH_INFO { UINT32 id; UINT8 blockSize; UINT8 sectorSize; UINT8 readCmd; UINT8 progCmd; UINT8 readCmd_4; UINT8 progCmd_4; UINT8 sectorEraseCmd; UINT8 blockEraseCmd; UINT8 feature; UINT8 density; /* (1 << density) sectors */ UINT8 QEBits; UINT8 reserved2; }; /* FLASH_INFO feature */ #define FEA_STATUE_MASK (0x3 << 0) #define FEA_STATUE_MODE0 0 /* Readstatus0 05h-SR1, 35h-SR2, Writestatus0 01h-SR1, 31h-SR2 */ #define FEA_STATUE_MODE1 1 /* Readstatus0 05h-SR1, 35h-SR2, Writestatus1 01h-SR1-SR2 */ #define FEA_STATUE_MODE2 2 /* Readstatus1 05h-SR1, 15h-CR1, Writestatus1 01h-SR1-CR1 */ #define FEA_4BIT_READ BIT(2) #define FEA_4BIT_PROG BIT(3) #define FEA_4BYTE_ADDR BIT(4) #define FEA_4BYTE_ADDR_MODE BIT(5) /*Manufactory ID*/ #define MID_WINBOND 0xEF #define MID_GIGADEV 0xC8 #define MID_MICRON 0x2C #define MID_MACRONIX 0xC2 #define MID_SPANSION 0x01 #define MID_EON 0x1C #define MID_ST 0x20 #define MID_XTX 0x0B #define MID_PUYA 0x85 #define MID_XMC 0x20 /* Used for Macronix and Winbond flashes. */ #define SPINOR_OP_EN4B 0xb7 /* Enter 4-byte mode */ #define SPINOR_OP_EX4B 0xe9 /* Exit 4-byte mode */ /* Used for SST flashes only. */ #define SPINOR_OP_WRDI 0x04 /* Write disable */ #define SPINOR_OP_MAX_SIZE 0x40 #define UINT_MAX (~0U) #define READ_MAX_IOSIZE (1024 * 8) /* 8KB */ /********************* Private Structure Definition **************************/ /********************* Private Variable Definition ***************************/ static const struct FLASH_INFO s_spiFlashbl[] = { /* GD25Q32B */ { 0xc84016, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0D, 13, 9, 0 }, /* GD25Q64B */ { 0xc84017, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0D, 14, 9, 0 }, /* GD25Q127C and GD25Q128C*/ { 0xc84018, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0C, 15, 9, 0 }, /* GD25Q256B/C/D */ { 0xc84019, 128, 8, 0x13, 0x12, 0x6C, 0x3E, 0x21, 0xDC, 0x1C, 16, 6, 0 }, /* GD25LQ64C */ { 0xc86017, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0D, 14, 9, 0 }, /* GD25LQ32E */ { 0xc86016, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0D, 13, 9, 0 }, /* GD25Q256E */ { 0xc86019, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0D, 16, 9, 0 }, /* W25Q32JV */ { 0xef4016, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0C, 13, 9, 0 }, /* W25Q64JVSSIQ */ { 0xef4017, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0C, 14, 9, 0 }, /* W25Q128FV and W25Q128JV*/ { 0xef4018, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0C, 15, 9, 0 }, /* W25Q256F/J */ { 0xef4019, 128, 8, 0x13, 0x02, 0x6C, 0x32, 0x20, 0xD8, 0x3C, 16, 9, 0 }, /* 25Q32JW */ { 0xef6016, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0C, 13, 9, 0 }, /* W25Q256JWEQ*/ { 0xef6019, 128, 8, 0x13, 0x02, 0x6C, 0x32, 0x20, 0xD8, 0x3C, 16, 9, 0 }, /* W25Q64FWSSIG */ { 0xef6017, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0C, 14, 9, 0 }, /* W25Q64JVSIQ */ { 0xef7017, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0C, 14, 9, 0 }, /* W25Q128JVSIM */ { 0xef7018, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0C, 15, 9, 0 }, /* MX25L3233FM2I-08G */ { 0xc22016, 128, 8, 0x03, 0x02, 0x6B, 0x38, 0x20, 0xD8, 0x0E, 13, 6, 0 }, /* MX25L6433F */ { 0xc22017, 128, 8, 0x03, 0x02, 0x6B, 0x38, 0x20, 0xD8, 0x0E, 14, 6, 0 }, /* MX25L12835E/F MX25L12833FMI-10G */ { 0xc22018, 128, 8, 0x03, 0x02, 0x6B, 0x38, 0x20, 0xD8, 0x0E, 15, 6, 0 }, /* MX25U12832F */ { 0xc22538, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0E, 15, 6, 0 }, /* MX25L25635E/F MX25L25645G MX25L25645GMI-08G */ { 0xc22019, 128, 8, 0x13, 0x12, 0x6C, 0x3E, 0x21, 0xDC, 0x1E, 16, 6, 0 }, /* XM25QH32C */ { 0x204016, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0C, 13, 9, 0 }, /* XM25QH64B */ { 0x206017, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0D, 14, 6, 0 }, /* XM25QH128B */ { 0x206018, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0D, 15, 6, 0 }, /* XM25QH(QU)256B */ { 0x206019, 128, 8, 0x13, 0x12, 0x6C, 0x3E, 0x21, 0xDC, 0x1D, 16, 6, 0 }, /* XM25QH64A */ { 0x207017, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0C, 14, 0, 0 }, /* XM25QU128C */ { 0x204118, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0C, 15, 9, 0 }, /* XM25QU64C */ { 0x204117, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0C, 14, 9, 0 }, /* XT25F64BSSIGU-5 */ { 0x0b4017, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0D, 14, 9, 0 }, /* XT25F128BSSIGU */ { 0x0b4018, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0D, 15, 9, 0 }, /* XT25F32BS */ { 0x0b4016, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0D, 13, 9, 0 }, /* XT25F16BS */ { 0x0b4015, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0D, 12, 9, 0 }, /* EN25QH64A */ { 0x1c7017, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0C, 14, 0, 0 }, /* EN25QH128A */ { 0x1c7018, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0C, 15, 0, 0 }, /* EN25QH32B */ { 0x1c7016, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0C, 13, 0, 0 }, /* EN25S16A */ { 0x1c3815, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0C, 12, 0, 0 }, /* EN25S32A */ { 0x1c3816, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0C, 13, 0, 0 }, /* EN25S64A */ { 0x1c3817, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0C, 14, 0, 0 }, /* EN25QH256A */ { 0x1c7019, 128, 8, 0x13, 0x12, 0x6C, 0x34, 0x21, 0xDC, 0x3C, 16, 0, 0 }, /* P25Q64H */ { 0x856017, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0C, 14, 9, 0 }, /* P25Q128H */ { 0x856018, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0C, 15, 9, 0 }, /* P25Q16H-SUH-IT */ { 0x856015, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0D, 12, 9, 0 }, /* FM25Q64A */ { 0xf83217, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0D, 14, 9, 0 }, /* FM25M64C */ { 0xf84317, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0D, 14, 9, 0 }, /* P25Q32SL */ { 0x856016, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0C, 13, 9, 0 }, /* ZB25VQ64 */ { 0x5e4017, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0C, 14, 9, 0 }, /* ZB25VQ128 */ { 0x5e4018, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0C, 15, 9, 0 }, /* ZB25LQ128 */ { 0x5e5018, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0C, 15, 9, 0 }, /* BH25Q128AS */ { 0x684018, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0C, 15, 9, 0 }, /* BH25Q64BS */ { 0x684017, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0C, 14, 9, 0 }, /* P25Q64H */ { 0x856017, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0C, 14, 9, 0 }, /* FM25Q64-SOB-T-G */ { 0xA14017, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0C, 14, 9, 0 }, /* FM25Q64A */ { 0xf83217, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0D, 14, 9, 0 }, /* FM25M4AA */ { 0xf84218, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x0D, 15, 9, 0 }, }; STATIC struct HAL_FSPI_HOST *g_spi; STATIC struct SPI_NOR *g_nor; STATIC EFI_EVENT mNorVirtualAddrChangeEvent; /* Support single line case * - id: get from SPI Nor device information * - capacity: initial by SPI Nor id byte * - QE bits: no need * */ static struct FLASH_INFO s_commonSpiFlash = { 0, 128, 8, 0x03, 0x02, 0x6B, 0x32, 0x20, 0xD8, 0x00, 0, 0, 0 }; /********************* Private Function Definition ***************************/ static RETURN_STATUS SNOR_ReadWriteReg(struct SPI_NOR *nor, struct HAL_SPI_MEM_OP *op, void *buf) { if (op->data.dir == HAL_SPI_MEM_DATA_IN) { op->data.buf.in = buf; } else { op->data.buf.out = buf; } return HAL_FSPI_SpiXfer(nor->spi, op); } static RETURN_STATUS SNOR_ReadReg(struct SPI_NOR *nor, UINT8 code, UINT8 *val, UINT32 len) { struct HAL_SPI_MEM_OP op = HAL_SPI_MEM_OP_FORMAT(HAL_SPI_MEM_OP_CMD(code, 1), HAL_SPI_MEM_OP_NO_ADDR, HAL_SPI_MEM_OP_NO_DUMMY, HAL_SPI_MEM_OP_DATA_IN(len, NULL, 1)); RETURN_STATUS ret; ret = SNOR_ReadWriteReg(nor, &op, val); if (ret) { DEBUG ((DEBUG_SNOR, "error %d reading %x\n", ret, code)); } return ret; } static RETURN_STATUS SNOR_WriteReg(struct SPI_NOR *nor, UINT8 opcode, UINT8 *buf, UINT32 len) { struct HAL_SPI_MEM_OP op = HAL_SPI_MEM_OP_FORMAT(HAL_SPI_MEM_OP_CMD(opcode, 1), HAL_SPI_MEM_OP_NO_ADDR, HAL_SPI_MEM_OP_NO_DUMMY, HAL_SPI_MEM_OP_DATA_OUT(len, NULL, 1)); /* DEBUG ((DEBUG_SNOR, "%s %x %ld\n", __func__, opcode, len)); */ return SNOR_ReadWriteReg(nor, &op, buf); } static INT32 SNOR_ReadData(struct SPI_NOR *nor, UINT32 from, UINT32 len, UINT8 *buf) { struct HAL_SPI_MEM_OP op = HAL_SPI_MEM_OP_FORMAT(HAL_SPI_MEM_OP_CMD(nor->readOpcode, 1), HAL_SPI_MEM_OP_ADDR(nor->addrWidth, from, 1), HAL_SPI_MEM_OP_DUMMY(nor->readDummy, 1), HAL_SPI_MEM_OP_DATA_IN(len, buf, 1)); INT32 ret; /* get transfer protocols. */ op.cmd.buswidth = 1; op.addr.buswidth = SNOR_GET_PROTOCOL_ADDR_BITS(nor->readProto); op.dummy.buswidth = op.addr.buswidth; op.data.buswidth = SNOR_GET_PROTOCOL_DATA_BITS(nor->readProto); /* DEBUG ((DEBUG_SNOR, "%s %x %lx %lx %lx\n", __func__, nor->readDummy, op.data.nbytes, from, op.addr.val)); */ /* convert the dummy cycles to the number of bytes */ op.dummy.nbytes = (nor->readDummy * op.dummy.buswidth) >> 3; ret = HAL_FSPI_SpiXfer(nor->spi, &op); if (ret) { return 0; } return len; } static INT32 SNOR_WriteData(struct SPI_NOR *nor, UINT32 to, UINT32 len, const UINT8 *buf) { struct HAL_SPI_MEM_OP op = HAL_SPI_MEM_OP_FORMAT(HAL_SPI_MEM_OP_CMD(nor->programOpcode, 1), HAL_SPI_MEM_OP_ADDR(nor->addrWidth, to, 1), HAL_SPI_MEM_OP_NO_DUMMY, HAL_SPI_MEM_OP_DATA_OUT(len, buf, 1)); INT32 ret; /* get transfer protocols. */ op.cmd.buswidth = 1; op.addr.buswidth = SNOR_GET_PROTOCOL_ADDR_BITS(nor->writeProto); op.data.buswidth = SNOR_GET_PROTOCOL_DATA_BITS(nor->writeProto); op.data.nbytes = len < op.data.nbytes ? len : op.data.nbytes; ret = HAL_FSPI_SpiXfer(nor->spi, &op); if (ret) { return 0; } return op.data.nbytes; } /* * Initiate the erasure of a single sector */ static RETURN_STATUS SNOR_EraseSec(struct SPI_NOR *nor, UINT32 addr) { struct HAL_SPI_MEM_OP op = HAL_SPI_MEM_OP_FORMAT(HAL_SPI_MEM_OP_CMD(nor->eraseOpcodeSec, 1), HAL_SPI_MEM_OP_ADDR(nor->addrWidth, addr, 1), HAL_SPI_MEM_OP_NO_DUMMY, HAL_SPI_MEM_OP_NO_DATA); /* * Default implementation, if driver doesn't have a specialized HW * control */ return HAL_FSPI_SpiXfer(nor->spi, &op); } /* * Initiate the erasure of a single sector */ static RETURN_STATUS SNOR_EraseBlk(struct SPI_NOR *nor, UINT32 addr) { struct HAL_SPI_MEM_OP op = HAL_SPI_MEM_OP_FORMAT(HAL_SPI_MEM_OP_CMD(nor->eraseOpcodeBlk, 1), HAL_SPI_MEM_OP_ADDR(nor->addrWidth, addr, 1), HAL_SPI_MEM_OP_NO_DUMMY, HAL_SPI_MEM_OP_NO_DATA); /* * Default implementation, if driver doesn't have a specialized HW * control */ return HAL_FSPI_SpiXfer(nor->spi, &op); } static RETURN_STATUS SNOR_EraseChip(struct SPI_NOR *nor, UINT32 addr) { struct HAL_SPI_MEM_OP op = HAL_SPI_MEM_OP_FORMAT(HAL_SPI_MEM_OP_CMD(SPINOR_OP_CHIP_ERASE, 1), HAL_SPI_MEM_OP_NO_ADDR, HAL_SPI_MEM_OP_NO_DUMMY, HAL_SPI_MEM_OP_NO_DATA); /* * Default implementation, if driver doesn't have a specialized HW * control */ return HAL_FSPI_SpiXfer(nor->spi, &op); } static const struct FLASH_INFO *SNOR_GerFlashInfo(UINT8 *flashId) { UINT32 i; UINT32 id = (flashId[0] << 16) | (flashId[1] << 8) | (flashId[2] << 0); for (i = 0; i < ARRAY_SIZE(s_spiFlashbl); i++) { if (s_spiFlashbl[i].id == id) { return &s_spiFlashbl[i]; } } return NULL; } static RETURN_STATUS SNOR_WriteEnable(struct SPI_NOR *nor) { return SNOR_WriteReg(nor, SPINOR_OP_WREN, NULL, 0); } /* * Service routine to read status register until ready, or timeout occurs. * Returns non-zero if error. */ static RETURN_STATUS SNOR_WaitBusy(struct SPI_NOR *nor, unsigned long timeout) { RETURN_STATUS ret; UINT32 i; UINT8 status; /* DEBUG ((DEBUG_SNOR, "%s %lx\n", __func__, timeout)); */ for (i = 0; i < timeout; i++) { ret = SNOR_ReadReg(nor, SPINOR_OP_RDSR, &status, 1); if (ret != RETURN_SUCCESS) { return ret; } if ((status & 0x01) == 0) { return RETURN_SUCCESS; } NanoSecondDelay(500); } DEBUG ((DEBUG_SNOR, "%s error %ld\n", __func__, timeout)); return RETURN_NOT_READY; } static RETURN_STATUS SNOR_ReadStatus(struct SPI_NOR *nor, UINT32 regIndex, UINT8 *status) { UINT8 readStatCmd[] = { SPINOR_OP_RDSR, SPINOR_OP_RDSR1, SPINOR_OP_RDSR2 }; UINT8 i = nor->info->feature & FEA_STATUE_MASK; if (i == FEA_STATUE_MODE2) { /* Readstatus1 */ readStatCmd[1] = SPINOR_OP_RDCR; } return SNOR_ReadReg(nor, readStatCmd[regIndex], status, 1); } static RETURN_STATUS SNOR_WriteStatus(struct SPI_NOR *nor, UINT32 regIndex, UINT8 *status) { UINT8 WriteStatCmd[2] = { SPINOR_OP_WRSR, SPINOR_OP_WRSR1 }; UINT8 i = nor->info->feature & FEA_STATUE_MASK; RETURN_STATUS ret; if (i == FEA_STATUE_MODE0) { /* Writestatus0 */ SNOR_WriteEnable(nor); ret = SNOR_WriteReg(nor, WriteStatCmd[regIndex], status, 1); if (ret) { DEBUG ((DEBUG_SNOR, "error while writing configuration register\n")); return RETURN_DEVICE_ERROR; } ret = SNOR_WaitBusy(nor, 10000); if (ret) { DEBUG ((DEBUG_SNOR, "timeout while writing configuration register\n")); return ret; } } else { /* Writestatus1 */ UINT8 readIndex; UINT8 status2[2]; status2[regIndex] = *status; readIndex = (regIndex == 0) ? 1 : 0; ret = SNOR_ReadStatus(nor, readIndex, &status2[readIndex]); if (ret != RETURN_SUCCESS) { return ret; } SNOR_WriteEnable(nor); ret = SNOR_WriteReg(nor, SPINOR_OP_WRSR, &status2[0], 2); if (ret != RETURN_SUCCESS) { return ret; } ret = SNOR_WaitBusy(nor, 10000); } return RETURN_SUCCESS; } static RETURN_STATUS SNOR_EnableQE(struct SPI_NOR *nor) { RETURN_STATUS ret = RETURN_SUCCESS; int regIndex; int bitOffset; UINT8 status; regIndex = nor->info->QEBits >> 3; bitOffset = nor->info->QEBits & 0x7; ret = SNOR_ReadStatus(nor, regIndex, &status); if (ret != RETURN_SUCCESS) { return ret; } if (status & (1 << bitOffset)) { //is QE bit set return RETURN_SUCCESS; } status |= (1 << bitOffset); ret = SNOR_WriteStatus(nor, regIndex, &status); return ret; } /* Enable/disable 4-byte addressing mode. */ static RETURN_STATUS SNOR_Enter4byte(struct SPI_NOR *nor) { return SNOR_WriteReg(nor, SPINOR_OP_EN4B, NULL, 0); } RETURN_STATUS SNOR_ReadSFDP(struct SPI_NOR *nor, UINT32 addr, UINT8 *data) { struct HAL_SPI_MEM_OP op = HAL_SPI_MEM_OP_FORMAT(HAL_SPI_MEM_OP_CMD(SPINOR_OP_READ_SFDP, 1), HAL_SPI_MEM_OP_ADDR(3, addr, 1), HAL_SPI_MEM_OP_DUMMY(1, 1), HAL_SPI_MEM_OP_DATA_IN(1, data, 1)); return HAL_FSPI_SpiXfer(nor->spi, &op); } static void *SNOR_InfoAdjust(struct SPI_NOR *nor, struct FLASH_INFO *info) { UINT32 addr; UINT8 para; if (info->id == 0xc84019) { addr = 0x09; SNOR_ReadSFDP(nor, addr, ¶); if (para == 0x06) { info->QEBits = 9; info->progCmd_4 = 0x34; } } return 0; } /** * @brief Flash continuous writing. * @param nor: nor dev. * @param from: byte address. * @param buf: source address. * @param len: number of bytes. * @return If the transfer is successful, return the transfer length, or error code. */ RETURN_STATUS HAL_SNOR_ReadData(struct SPI_NOR *nor, UINT32 from, void *buf, UINT32 len) { RETURN_STATUS ret; UINT8 *pBuf = (UINT8 *)buf; UINT32 size, remain = len; /* DEBUG ((DEBUG_SNOR, "%s from 0x%08lx, len %lx\n", __func__, from, len)); */ if (from >= nor->size || len > nor->size || (from + len) > nor->size) { return RETURN_DEVICE_ERROR; } while (remain) { size = MIN(READ_MAX_IOSIZE, remain); ret = SNOR_ReadData(nor, from, size, pBuf); if (ret != (RETURN_STATUS)size) { DEBUG ((DEBUG_SNOR, "%s %lu ret= %ld\n", __func__, from >> 9, ret)); return ret; } remain -= size; from += size; pBuf += size; } return len; } /** * @brief Flash continuous reading. * @param nor: nor dev. * @param to: byte address. * @param buf: source address. * @param len: number of bytes. * @return If the transfer is successful, return the transfer length, or error code. */ RETURN_STATUS HAL_SNOR_ProgData(struct SPI_NOR *nor, UINT32 to, void *buf, UINT32 len) { RETURN_STATUS ret; UINT8 *pBuf = (UINT8 *)buf; UINT32 size, remain = len, pageOffset; /* DEBUG ((DEBUG_SNOR, "%s to 0x%08lx, len %lx\n", __func__, to, len)); */ if (to >= nor->size || len > nor->size || (to + len) > nor->size) { return RETURN_DEVICE_ERROR; } while (remain) { pageOffset = to & (nor->pageSize - 1); size = MIN(nor->pageSize - pageOffset, remain); SNOR_WriteEnable(nor); ret = SNOR_WriteData(nor, to, size, pBuf); if (ret != (RETURN_STATUS)size) { DEBUG ((DEBUG_SNOR, "%s %lu ret= %ld\n", __func__, to >> 9, ret)); return ret; } SNOR_WaitBusy(nor, 10000); remain -= size; to += size; pBuf += size; } return len; } /** * @brief Flash erase with erase type. * @param nor: nor dev. * @param addr: byte address. * @param eraseType: erase type. * @return RETURN_STATUS. */ RETURN_STATUS HAL_SNOR_Erase(struct SPI_NOR *nor, UINT32 addr, NOR_ERASE_TYPE eraseType) { RETURN_STATUS ret; INT32 timeout[] = { 400, 2000, 40000 }; /* DEBUG ((DEBUG_SNOR, "%s addr %lx\n", __func__, addr)); */ if (addr >= nor->size) { return RETURN_DEVICE_ERROR; } SNOR_WriteEnable(nor); if (eraseType == ERASE_SECTOR) { ret = SNOR_EraseSec(nor, addr); } else if (eraseType == ERASE_BLOCK64K) { ret = SNOR_EraseBlk(nor, addr); } else { ret = SNOR_EraseChip(nor, addr); } if (ret != RETURN_SUCCESS) { return ret; } return SNOR_WaitBusy(nor, timeout[eraseType] * 1000); } /** * @brief Flash continuous reading according to sectors. * @param nor: nor dev. * @param sec: sector address. * @param nSec: number of sectors. * @param pData: destination address. * @return If the transfer is successful, return the transfer length, or error code. */ RETURN_STATUS HAL_SNOR_Read(struct SPI_NOR *nor, UINT32 sec, UINT32 nSec, void *pData) { RETURN_STATUS ret = RETURN_SUCCESS; /* DEBUG ((DEBUG_SNOR, "%s sec 0x%08lx, nSec %lx\n", __func__, sec, nSec)); */ ret = HAL_SNOR_ReadData(nor, sec * nor->sectorSize, pData, nSec * nor->sectorSize); if (ret != (RETURN_STATUS)(nSec * nor->sectorSize)) { return ret; } return (RETURN_STATUS)nSec; } /** * @brief Flash continuous writing according to sectors. * @param nor: nor dev. * @param sec: sector address. * @param nSec: number of sectors. * @param pData: source address. * @return If the transfer is successful, return the transfer length, or error code. */ RETURN_STATUS HAL_SNOR_Write(struct SPI_NOR *nor, UINT32 sec, UINT32 nSec, void *pData) { RETURN_STATUS ret = RETURN_SUCCESS; /* DEBUG ((DEBUG_SNOR, "%s sec 0x%08lx, nSec %lx\n", __func__, sec, nSec)); */ ret = HAL_SNOR_ProgData(nor, sec * nor->sectorSize, pData, nSec * nor->sectorSize); if (ret != (RETURN_STATUS)(nSec * nor->sectorSize)) { return ret; } return (RETURN_STATUS)nSec; } /** * @brief Flash continuous writing according to sectors. * @param nor: nor dev. * @param sec: sector address. * @param nSec: number of sectors. * @param pData: source address. * @return If the transfer is successful, return the transfer length, or error code. */ RETURN_STATUS HAL_SNOR_OverWrite(struct SPI_NOR *nor, UINT32 sec, UINT32 nSec, void *pData) { RETURN_STATUS ret = RETURN_SUCCESS; UINT8 *pBuf = (UINT8 *)pData; UINT32 remaining = nSec; /* DEBUG ((DEBUG_SNOR, "%s sec 0x%08lx, nSec %lx\n", __func__, sec, nSec)); */ while (remaining) { ret = HAL_SNOR_Erase(nor, sec * nor->sectorSize, ERASE_SECTOR); if (ret != RETURN_SUCCESS) { return ret; } ret = HAL_SNOR_ProgData(nor, sec * nor->sectorSize, (void *)pBuf, nor->sectorSize); if (ret != (RETURN_STATUS)(nor->sectorSize)) { return ret; } pBuf += nor->sectorSize; remaining--; sec++; } return (RETURN_STATUS)nSec; } /** @} */ /** @defgroup SNOR_Exported_Functions_Group4 Init and DeInit Functions This section provides functions allowing to init and deinit the module: ...to do or delete this row * @{ */ /** * @brief SFC NOR flash module init. * @param nor: nor dev. * @return RETURN_STATUS. */ RETURN_STATUS HAL_SNOR_Init(struct SPI_NOR *nor) { UINT8 idByte[5]; const struct FLASH_INFO *info; INT32 ret = RETURN_SUCCESS; if (!nor->spi) { DEBUG ((DEBUG_SNOR, "%a no host\n", __func__)); return RETURN_DEVICE_ERROR; } //nor->read = SNOR_ReadData; //nor->write = SNOR_WriteData; //nor->readReg = SNOR_ReadReg; //nor->writeReg = SNOR_WriteReg; HAL_SNOR_ReadID(nor, idByte); DEBUG ((DEBUG_SNOR, "SPI Nor ID: %x %x %x\n", idByte[0], idByte[1], idByte[2])); if ((idByte[0] == 0xFF) || (idByte[0] == 0x00)) { return RETURN_DEVICE_ERROR; } info = SNOR_GerFlashInfo(idByte); if (!info) { if (nor->spi->mode & HAL_SPI_RX_QUAD || nor->spi->mode & HAL_SPI_TX_QUAD) { return RETURN_NO_MEDIA; } s_commonSpiFlash.id = (idByte[0] << 16) | (idByte[1] << 8) | idByte[2]; s_commonSpiFlash.density = idByte[2] - 9; info = &s_commonSpiFlash; } else { SNOR_InfoAdjust(nor, (struct FLASH_INFO *)info); } nor->info = info; nor->pageSize = 256; nor->addrWidth = 3; nor->eraseOpcodeSec = info->sectorEraseCmd; nor->eraseOpcodeBlk = info->blockEraseCmd; nor->readOpcode = info->readCmd; nor->readProto = SNOR_PROTO_1_1_1; nor->readDummy = 0; nor->programOpcode = info->progCmd; nor->writeProto = SNOR_PROTO_1_1_1; //nor->name = "spi-nor"; nor->sectorSize = info->sectorSize * 512; nor->size = 1 << (info->density + 9); nor->eraseSize = nor->sectorSize; if (nor->spi->mode & HAL_SPI_RX_QUAD) { ret = RETURN_SUCCESS; if (info->QEBits) { ret = SNOR_EnableQE(nor); } if (ret == RETURN_SUCCESS) { nor->readOpcode = info->readCmd_4; switch (nor->readOpcode) { case SPINOR_OP_READ_1_4_4: nor->readDummy = 6; nor->readProto = SNOR_PROTO_1_4_4; break; default: nor->readDummy = 8; nor->readProto = SNOR_PROTO_1_1_4; break; } } } else if (nor->spi->mode & HAL_SPI_RX_DUAL) { nor->readOpcode = SPINOR_OP_READ_1_1_2; nor->readDummy = 8; nor->readProto = SNOR_PROTO_1_1_2; } if (nor->spi->mode & HAL_SPI_TX_QUAD && info->QEBits) { if (SNOR_EnableQE(nor) == RETURN_SUCCESS) { nor->programOpcode = info->progCmd_4; switch (nor->programOpcode) { case SPINOR_OP_PP_1_4_4: nor->writeProto = SNOR_PROTO_1_4_4; break; default: nor->writeProto = SNOR_PROTO_1_1_4; break; } } } if (info->feature & FEA_4BYTE_ADDR) { nor->addrWidth = 4; } if (info->feature & FEA_4BYTE_ADDR_MODE) { SNOR_Enter4byte(nor); } DEBUG ((DEBUG_SNOR, "nor->addrWidth: %x\n", nor->addrWidth)); DEBUG ((DEBUG_SNOR, "nor->readProto: %x\n", nor->readProto)); DEBUG ((DEBUG_SNOR, "nor->writeProto: %x\n", nor->writeProto)); DEBUG ((DEBUG_SNOR, "nor->readCmd: %x\n", nor->readOpcode)); DEBUG ((DEBUG_SNOR, "nor->programCmd: %x\n", nor->programOpcode)); DEBUG ((DEBUG_SNOR, "nor->eraseOpcodeBlk: %x\n", nor->eraseOpcodeBlk)); DEBUG ((DEBUG_SNOR, "nor->eraseOpcodeSec: %x\n", nor->eraseOpcodeSec)); DEBUG ((DEBUG_SNOR, "nor->size: %ldMB\n", nor->size >> 20)); DEBUG ((DEBUG_SNOR, "nor->size: %ldMB\n", nor->size >> 20)); return RETURN_SUCCESS; } /** * @brief SFC NOR flash module deinit. * @param nor: nor dev. * @return RETURN_STATUS. */ RETURN_STATUS HAL_SNOR_DeInit(struct SPI_NOR *nor) { return RETURN_SUCCESS; } /** @} */ /** @defgroup SNOR_Exported_Functions_Group5 Other Functions * @{ */ /** * @brief Read flash ID. * @param nor: nor dev. * @param data: address to storage flash ID value. * @return RETURN_STATUS. */ RETURN_STATUS HAL_SNOR_ReadID(struct SPI_NOR *nor, UINT8 *data) { INT32 ret; UINT8 *id = data; ret = SNOR_ReadReg(nor, SPINOR_OP_RDID, id, 3); if (ret) { DEBUG ((DEBUG_SNOR, "error reading JEDEC ID%x %x %x\n", id[0], id[1], id[2])); return RETURN_DEVICE_ERROR; } return RETURN_SUCCESS; } /** * @brief Get flash capacity. * @param nor: nor dev. * @return UINT32: flash capacity, n bytes. */ UINT32 HAL_SNOR_GetCapacity(struct SPI_NOR *nor) { return nor->size; } /** * @brief Check if the flash support. * @param flashId: flash id. * @return HAL_Check. */ BOOLEAN HAL_SNOR_IsFlashSupported(UINT8 *flashId) { UINT32 i; UINT32 id = (flashId[0] << 16) | (flashId[1] << 8) | (flashId[2] << 0); for (i = 0; i < ARRAY_SIZE(s_spiFlashbl); i++) { if (s_spiFlashbl[i].id == id) { return TRUE; } } return FALSE; } RETURN_STATUS HAL_SNOR_ReadUUID(struct SPI_NOR *nor, void *buf) { struct HAL_SPI_MEM_OP op = HAL_SPI_MEM_OP_FORMAT(HAL_SPI_MEM_OP_CMD(SPINOR_OP_READ_UUID, 1), HAL_SPI_MEM_OP_ADDR(4, 0, 1), HAL_SPI_MEM_OP_DUMMY(0, 1), HAL_SPI_MEM_OP_DATA_IN(8, buf, 1)); return HAL_FSPI_SpiXfer(nor->spi, &op); } EFI_STATUS Erase( IN UNI_NOR_FLASH_PROTOCOL *This, IN UINT32 Offset, IN UINT32 ulLen ) { EFI_STATUS Status; UINTN EraseSize; EraseSize = g_nor->sectorSize; // Check input parameters if (Offset % EraseSize || ulLen % EraseSize) { DEBUG((DEBUG_ERROR, "SpiFlash: Either erase offset or length is not multiple of erase size\n")); return EFI_DEVICE_ERROR; } while (ulLen) { Status = HAL_SNOR_Erase(g_nor, Offset, ERASE_SECTOR); if (EFI_ERROR (Status)) { DEBUG((DEBUG_ERROR, "SpiFlash: Error while erase target address\n")); return Status; } Offset += EraseSize; ulLen -= EraseSize; } return EFI_SUCCESS; } UINT32 GetSize( IN UNI_NOR_FLASH_PROTOCOL *This ) { return HAL_SNOR_GetCapacity(g_nor); } EFI_STATUS Write( IN UNI_NOR_FLASH_PROTOCOL *This, IN UINT32 Offset, IN UINT8 *Buffer, UINT32 ulLen ) { EFI_STATUS Status = EFI_SUCCESS; //DEBUG ((EFI_D_ERROR, "[%a]:[%dL]: %x!......................\n", __FUNCTION__,__LINE__,Offset)); Status = HAL_SNOR_ProgData(g_nor, Offset, Buffer, ulLen); if (EFI_ERROR (Status)) { DEBUG((DEBUG_ERROR, "SpiFlash: Error while programming target address\n")); } return Status; } EFI_STATUS Read( IN UNI_NOR_FLASH_PROTOCOL *This, IN UINT32 Offset, IN OUT UINT8 *Buffer, IN UINT32 ulLen ) { EFI_STATUS Status = EFI_SUCCESS; //DEBUG ((EFI_D_ERROR, "[%a]:[%dL]: %x!......................\n", __FUNCTION__,__LINE__,Offset)); Status = HAL_SNOR_ReadData(g_nor, Offset, Buffer, ulLen); return Status; } STATIC EFI_STATUS SpiFlashUpdateBlock ( IN UINT32 Offset, IN UINT32 Align, IN UINTN ToUpdate, IN UINT8 *Buf, IN UINT8 *TmpBuf, IN UINTN EraseSize ) { EFI_STATUS Status; // Read backup if (ToUpdate != EraseSize) { Status = HAL_SNOR_ReadData (g_nor, Offset - Align, TmpBuf, EraseSize); if (EFI_ERROR (Status)) { DEBUG((DEBUG_ERROR, "SpiFlash: Update: Error while reading old data\n")); return Status; } } // Erase entire sector Status = HAL_SNOR_Erase (g_nor, Offset, ERASE_SECTOR); if (EFI_ERROR (Status)) { DEBUG((DEBUG_ERROR, "SpiFlash: Update: Error while erasing block\n")); return Status; } if (Align) { memcpy(&TmpBuf[Align], Buf, ToUpdate); Status = HAL_SNOR_ProgData (g_nor, Offset - Align, TmpBuf, EraseSize); if (EFI_ERROR (Status)) { DEBUG((DEBUG_ERROR, "SpiFlash: Update: Error while writing new data\n")); return Status; } return EFI_SUCCESS; } // Write new data Status = HAL_SNOR_ProgData (g_nor, Offset, Buf, ToUpdate); if (EFI_ERROR (Status)) { DEBUG((DEBUG_ERROR, "SpiFlash: Update: Error while writing new data\n")); return Status; } // Write backup if (ToUpdate != EraseSize) { Status = HAL_SNOR_ProgData (g_nor, Offset + ToUpdate, &TmpBuf[ToUpdate], EraseSize - ToUpdate); if (EFI_ERROR (Status)) { DEBUG((DEBUG_ERROR, "SpiFlash: Update: Error while writing backup\n")); return Status; } } return EFI_SUCCESS; } EFI_STATUS Update( IN UNI_NOR_FLASH_PROTOCOL *This, IN UINT32 Offset, IN UINT8 *Buffer, UINT32 ulLength ) { EFI_STATUS Status = EFI_SUCCESS; UINT64 SectorSize, ToUpdate, Align, Scale = 1; UINT8 *TmpBuf, *End; //DEBUG ((EFI_D_ERROR, "[%a]:%x %x!......................\n", __FUNCTION__, Offset, ulLength)); SectorSize = g_nor->sectorSize; Align = Offset & (SectorSize -1); End = Buffer + ulLength; TmpBuf = (UINT8 *)AllocateZeroPool (SectorSize); if (TmpBuf == NULL) { DEBUG((DEBUG_ERROR, "SpiFlash: Cannot allocate memory\n")); return EFI_OUT_OF_RESOURCES; } if (End - Buffer >= 200) Scale = (End - Buffer) / 100; for (; Buffer < End; Buffer += ToUpdate, Offset += ToUpdate) { ToUpdate = MIN((UINT64)(End - Buffer), SectorSize); Print (L" \rUpdating, %d%%", 100 - (End - Buffer) / Scale); Status = SpiFlashUpdateBlock (Offset, Align, ToUpdate, Buffer, TmpBuf, SectorSize); if (EFI_ERROR (Status)) { DEBUG((DEBUG_ERROR, "SpiFlash: Error while updating\n")); break; } } Print(L"\n"); FreePool (TmpBuf); return Status; } UNI_NOR_FLASH_PROTOCOL gUniNorFlash = { GetSize, Erase, Write, Read, Update }; #if 0 #define maxest_sector 4 static UINT32 pread32[maxest_sector * 4096 + 64]; static UINT32 pwrite32[maxest_sector * 4096 + 64]; #define FLASH_SKIP_LBA 0x100 /* About 1M space skip */ static RETURN_STATUS SNOR_STRESS_RANDOM_TEST(UINT32 testEndLBA) { UINT32 ret, j; UINT32 testCount, testLBA = 0; UINT32 testSecCount = 1; DEBUG ((EFI_D_ERROR, "---------%a %lx---------\n", __func__, testEndLBA)); DEBUG ((EFI_D_ERROR, "---------%a---------\n", __func__)); for (j = 0; j < testSecCount * (UINT32)g_nor->sectorSize / 4; j++) pwrite32[j] = j + (0xFFFF0000 - j); for (testCount = 0; testCount < testEndLBA;) { testLBA = testCount; pwrite32[0] = testLBA; ret = HAL_SNOR_OverWrite(g_nor, testLBA, testSecCount, pwrite32); if (ret != testSecCount) { return RETURN_DEVICE_ERROR; } pread32[0] = -1; ret = HAL_SNOR_Read(g_nor, testLBA, testSecCount, pread32); if (ret != testSecCount) { return RETURN_DEVICE_ERROR; } for (j = 0; j < testSecCount * (UINT32)g_nor->sectorSize / 4; j++) { if (pwrite32[j] != pread32[j]) { DEBUG ((EFI_D_ERROR, "check not match:row=%lx, num=%lx, write=%lx, read=%lx %lx %lx %lx\n", testLBA, j, pwrite32[j], pread32[j], pread32[j + 1], pread32[j + 2], pread32[j - 1])); while (1) { ; } } } DEBUG ((EFI_D_ERROR, "testCount= %lx testLBA= %lx\n", testCount, testLBA)); testCount += testSecCount; } DEBUG ((EFI_D_ERROR, "---------%a SUCCESS---------\n", __func__)); return RETURN_SUCCESS; } #endif /** Fixup internal data so that EFI can be call in virtual mode. Call the passed in Child Notify event and convert any pointers in lib to virtual mode. @param[in] Event The Event that is being processed @param[in] Context Event Context **/ STATIC VOID EFIAPI NorVirtualNotifyEvent ( IN EFI_EVENT Event, IN VOID *Context ) { // Convert SPI device description EfiConvertPointer (0, (VOID**)&g_nor->spi->instance); EfiConvertPointer (0, (VOID**)&g_nor->spi); EfiConvertPointer (0, (VOID**)&g_nor->info); EfiConvertPointer (0, (VOID**)&g_nor); return; } EFI_STATUS EFIAPI InitializeFlash ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) { EFI_STATUS Status = RETURN_SUCCESS; g_spi = AllocateRuntimeZeroPool (sizeof (struct HAL_FSPI_HOST)); g_nor = AllocateRuntimeZeroPool (sizeof (struct SPI_NOR)); if (g_spi == NULL || g_nor == NULL) { DEBUG ((DEBUG_ERROR, "%a: Cannot allocate memory\n", __FUNCTION__)); return EFI_OUT_OF_RESOURCES; } g_spi->instance = (struct FSPI_REG *)FixedPcdGet32(FspiBaseAddr); NorFspiIomux(); HAL_FSPI_Init(g_spi); g_nor->spi = g_spi; g_nor->spi->mode = HAL_SPI_MODE_3; g_nor->spi->mode |= (HAL_SPI_TX_QUAD | HAL_SPI_RX_QUAD); Status = HAL_SNOR_Init(g_nor); Status = gBS->InstallProtocolInterface ( &ImageHandle, &gUniNorFlashProtocolGuid, EFI_NATIVE_INTERFACE, &gUniNorFlash); if(EFI_SUCCESS != Status) { DEBUG ((EFI_D_ERROR, "[%a]:[%dL]:Install Protocol Interface %r!\n", __FUNCTION__,__LINE__,Status)); } Status = gBS->CreateEventEx (EVT_NOTIFY_SIGNAL, TPL_NOTIFY, NorVirtualNotifyEvent, NULL, &gEfiEventVirtualAddressChangeGuid, &mNorVirtualAddrChangeEvent); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "%a: Failed to register VA change event\n", __FUNCTION__)); goto ErrorSetMemAttr; } return Status; ErrorSetMemAttr: gBS->UninstallProtocolInterface (gImageHandle, &gUniNorFlashProtocolGuid, NULL); FreePool (g_nor); FreePool (g_spi); return Status; }