From c8e741bd18b6918e7cc690a2dd1e84f6399abd9f Mon Sep 17 00:00:00 2001 From: Dominic Clifton Date: Thu, 11 Jun 2020 19:46:07 +0200 Subject: [PATCH] Add W25Q128FV driver (QSPI only) * Enable QSPI flash chip drivers on NUCLEOH743 for some CI visibility. * Rework QSPI flash detection. * Supports chips that are in QSPI mode by default (factory). * Supports chips that are in QSPI mode due to configuration (e.g. Quad Enable in Non-Volatile on W25Q128FV) * Supports chips that need 8 dummy bytes (e.g. W25Q128JV) and ones that don't (e.g. W25N01G) --- make/source.mk | 1 + src/main/drivers/bus_quadspi.h | 2 + src/main/drivers/bus_quadspi_hal.c | 78 +++++ src/main/drivers/flash.c | 80 +++-- src/main/drivers/flash_w25q128fv.c | 438 ++++++++++++++++++++++++++++ src/main/drivers/flash_w25q128fv.h | 7 + src/main/target/NUCLEOH743/target.h | 4 +- 7 files changed, 586 insertions(+), 24 deletions(-) create mode 100644 src/main/drivers/flash_w25q128fv.c create mode 100644 src/main/drivers/flash_w25q128fv.h diff --git a/make/source.mk b/make/source.mk index d678d1ec2..290b85767 100644 --- a/make/source.mk +++ b/make/source.mk @@ -406,6 +406,7 @@ SRC += \ drivers/flash.c \ drivers/flash_m25p16.c \ drivers/flash_w25n01g.c \ + drivers/flash_w25q128fv.c \ drivers/flash_w25m.c \ io/flashfs.c \ $(MSC_SRC) diff --git a/src/main/drivers/bus_quadspi.h b/src/main/drivers/bus_quadspi.h index 4e0e03ffe..76f3da666 100644 --- a/src/main/drivers/bus_quadspi.h +++ b/src/main/drivers/bus_quadspi.h @@ -109,12 +109,14 @@ void quadSpiSetDivisor(QUADSPI_TypeDef *instance, uint16_t divisor); bool quadSpiTransmit1LINE(QUADSPI_TypeDef *instance, uint8_t instruction, uint8_t dummyCycles, const uint8_t *out, int length); bool quadSpiReceive1LINE(QUADSPI_TypeDef *instance, uint8_t instruction, uint8_t dummyCycles, uint8_t *in, int length); +bool quadSpiReceive4LINES(QUADSPI_TypeDef *instance, uint8_t instruction, uint8_t dummyCycles, uint8_t *in, int length); bool quadSpiInstructionWithData1LINE(QUADSPI_TypeDef *instance, uint8_t instruction, uint8_t dummyCycles, const uint8_t *out, int length); bool quadSpiReceiveWithAddress1LINE(QUADSPI_TypeDef *instance, uint8_t instruction, uint8_t dummyCycles, uint32_t address, uint8_t addressSize, uint8_t *in, int length); bool quadSpiReceiveWithAddress4LINES(QUADSPI_TypeDef *instance, uint8_t instruction, uint8_t dummyCycles, uint32_t address, uint8_t addressSize, uint8_t *in, int length); bool quadSpiTransmitWithAddress1LINE(QUADSPI_TypeDef *instance, uint8_t instruction, uint8_t dummyCycles, uint32_t address, uint8_t addressSize, const uint8_t *out, int length); +bool quadSpiTransmitWithAddress4LINES(QUADSPI_TypeDef *instance, uint8_t instruction, uint8_t dummyCycles, uint32_t address, uint8_t addressSize, const uint8_t *out, int length); bool quadSpiInstructionWithAddress1LINE(QUADSPI_TypeDef *instance, uint8_t instruction, uint8_t dummyCycles, uint32_t address, uint8_t addressSize); diff --git a/src/main/drivers/bus_quadspi_hal.c b/src/main/drivers/bus_quadspi_hal.c index 5eb5c7f85..655b2a1d6 100644 --- a/src/main/drivers/bus_quadspi_hal.c +++ b/src/main/drivers/bus_quadspi_hal.c @@ -286,6 +286,44 @@ bool quadSpiReceive1LINE(QUADSPI_TypeDef *instance, uint8_t instruction, uint8_t return true; } +bool quadSpiReceive4LINES(QUADSPI_TypeDef *instance, uint8_t instruction, uint8_t dummyCycles, uint8_t *in, int length) +{ + QUADSPIDevice device = quadSpiDeviceByInstance(instance); + HAL_StatusTypeDef status; + + QSPI_CommandTypeDef cmd; + cmd.InstructionMode = QSPI_INSTRUCTION_4_LINES; + cmd.AddressMode = QSPI_ADDRESS_NONE; + cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; + cmd.DataMode = QSPI_DATA_4_LINES; + cmd.DummyCycles = dummyCycles; + cmd.DdrMode = QSPI_DDR_MODE_DISABLE; + cmd.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; + cmd.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; + + cmd.Instruction = instruction; + cmd.NbData = length; + + quadSpiSelectDevice(instance); + + status = HAL_QSPI_Command(&quadSpiDevice[device].hquadSpi, &cmd, QUADSPI_DEFAULT_TIMEOUT); + bool timeout = (status != HAL_OK); + if (!timeout) { + status = HAL_QSPI_Receive(&quadSpiDevice[device].hquadSpi, in, QUADSPI_DEFAULT_TIMEOUT); + + timeout = (status != HAL_OK); + } + + quadSpiDeselectDevice(instance); + + if (timeout) { + quadSpiTimeoutUserCallback(instance); + return false; + } + + return true; +} + bool quadSpiReceiveWithAddress1LINE(QUADSPI_TypeDef *instance, uint8_t instruction, uint8_t dummyCycles, uint32_t address, uint8_t addressSize, uint8_t *in, int length) { QUADSPIDevice device = quadSpiDeviceByInstance(instance); @@ -404,6 +442,46 @@ bool quadSpiTransmitWithAddress1LINE(QUADSPI_TypeDef *instance, uint8_t instruct return true; } +bool quadSpiTransmitWithAddress4LINES(QUADSPI_TypeDef *instance, uint8_t instruction, uint8_t dummyCycles, uint32_t address, uint8_t addressSize, const uint8_t *out, int length) +{ + QUADSPIDevice device = quadSpiDeviceByInstance(instance); + HAL_StatusTypeDef status; + + QSPI_CommandTypeDef cmd; + cmd.InstructionMode = QSPI_INSTRUCTION_1_LINE; + cmd.AddressMode = QSPI_ADDRESS_1_LINE; + cmd.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; + cmd.DataMode = QSPI_DATA_4_LINES; + cmd.DummyCycles = dummyCycles; + cmd.DdrMode = QSPI_DDR_MODE_DISABLE; + cmd.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; + cmd.SIOOMode = QSPI_SIOO_INST_EVERY_CMD; + + cmd.Instruction = instruction; + cmd.Address = address; + cmd.AddressSize = quadSpi_addressSizeFromValue(addressSize); + cmd.NbData = length; + + quadSpiSelectDevice(instance); + + status = HAL_QSPI_Command(&quadSpiDevice[device].hquadSpi, &cmd, QUADSPI_DEFAULT_TIMEOUT); + bool timeout = (status != HAL_OK); + + if (!timeout) { + status = HAL_QSPI_Transmit(&quadSpiDevice[device].hquadSpi, (uint8_t *)out, QUADSPI_DEFAULT_TIMEOUT); + timeout = (status != HAL_OK); + } + + quadSpiDeselectDevice(instance); + + if (timeout) { + quadSpiTimeoutUserCallback(instance); + return false; + } + + return true; +} + bool quadSpiInstructionWithAddress1LINE(QUADSPI_TypeDef *instance, uint8_t instruction, uint8_t dummyCycles, uint32_t address, uint8_t addressSize) { QUADSPIDevice device = quadSpiDeviceByInstance(instance); diff --git a/src/main/drivers/flash.c b/src/main/drivers/flash.c index 135dfeb38..5be09aa87 100644 --- a/src/main/drivers/flash.c +++ b/src/main/drivers/flash.c @@ -32,6 +32,7 @@ #include "flash_impl.h" #include "flash_m25p16.h" #include "flash_w25n01g.h" +#include "flash_w25q128fv.h" #include "flash_w25m.h" #include "drivers/bus_spi.h" #include "drivers/bus_quadspi.h" @@ -55,39 +56,72 @@ static int flashPartitions = 0; #ifdef USE_QUADSPI static bool flashQuadSpiInit(const flashConfig_t *flashConfig) { - QUADSPI_TypeDef *quadSpiInstance = quadSpiInstanceByDevice(QUADSPI_CFG_TO_DEV(flashConfig->quadSpiDevice)); - quadSpiSetDivisor(quadSpiInstance, QUADSPI_CLOCK_INITIALISATION); + bool detected = false; - uint8_t readIdResponse[4]; - bool status = quadSpiReceive1LINE(quadSpiInstance, FLASH_INSTRUCTION_RDID, 8, readIdResponse, sizeof(readIdResponse)); - if (!status) { - return false; - } + enum { TRY_1LINE = 0, TRY_4LINE, BAIL}; + int phase = TRY_1LINE; - flashDevice.io.mode = FLASHIO_QUADSPI; - flashDevice.io.handle.quadSpi = quadSpiInstance; + QUADSPI_TypeDef *hqspi = quadSpiInstanceByDevice(QUADSPI_CFG_TO_DEV(flashConfig->quadSpiDevice)); - // Manufacturer, memory type, and capacity - uint32_t chipID = (readIdResponse[0] << 16) | (readIdResponse[1] << 8) | (readIdResponse[2]); + do { + quadSpiSetDivisor(hqspi, QUADSPI_CLOCK_INITIALISATION); -#if defined(USE_FLASH_W25N01G) || defined(USE_FLASH_W25M02G) - quadSpiSetDivisor(quadSpiInstance, QUADSPI_CLOCK_ULTRAFAST); + // 3 bytes for what we need, but some IC's need 8 dummy cycles after the instruction, so read 4 and make two attempts to + // assemble the chip id from the response. + uint8_t readIdResponse[4]; -#if defined(USE_FLASH_W25N01G) - if (w25n01g_detect(&flashDevice, chipID)) { - return true; - } + bool status = false; + switch (phase) { + case TRY_1LINE: + status = quadSpiReceive1LINE(hqspi, FLASH_INSTRUCTION_RDID, 0, readIdResponse, 4); + break; + case TRY_4LINE: + status = quadSpiReceive4LINES(hqspi, FLASH_INSTRUCTION_RDID, 2, readIdResponse, 3); + break; + default: + break; + } + + if (!status) { + phase++; + continue; + } + + flashDevice.io.handle.quadSpi = hqspi; + flashDevice.io.mode = FLASHIO_QUADSPI; + + quadSpiSetDivisor(hqspi, QUADSPI_CLOCK_ULTRAFAST); + + + for (uint8_t offset = 0; offset <= 1 && !detected; offset++) { + + uint32_t chipID = (readIdResponse[offset + 0] << 16) | (readIdResponse[offset + 1] << 8) | (readIdResponse[offset + 2]); + + if (offset == 0) { +#ifdef USE_FLASH_W25Q128FV + if (!detected && w25q128fv_detect(&flashDevice, chipID)) { + detected = true; + } #endif + } + if (offset == 1) { +#ifdef USE_FLASH_W25N01G + if (!detected && w25n01g_detect(&flashDevice, chipID)) { + detected = true; + } +#endif #if defined(USE_FLASH_W25M02G) - if (w25m_detect(&flashDevice, chipID)) { - return true; - } + if (!detected && w25m_detect(&flashDevice, chipID)) { + detected = true; + } #endif + } + } + phase++; + } while (phase != BAIL && !detected); -#endif - - return false; + return detected; } #endif // USE_QUADSPI diff --git a/src/main/drivers/flash_w25q128fv.c b/src/main/drivers/flash_w25q128fv.c new file mode 100644 index 000000000..5d078094c --- /dev/null +++ b/src/main/drivers/flash_w25q128fv.c @@ -0,0 +1,438 @@ +/* + * This file is part of Cleanflight and Betaflight. + * + * Cleanflight and Betaflight are free software. You can redistribute + * this software and/or modify this software under the terms of the + * GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Cleanflight and Betaflight are distributed in the hope that they + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software. + * + * If not, see . + * + * Author: Dominic Clifton - Initial implementation and testing. + */ + +#include +#include + +#include "platform.h" + +#if defined(USE_FLASH_W25Q128FV) && defined(USE_QUADSPI) + +#define USE_FLASH_WRITES_USING_4LINES +#define USE_FLASH_READS_USING_4LINES + +#include "build/debug.h" +#include "common/utils.h" + +#include "drivers/flash.h" +#include "drivers/flash_impl.h" +#include "drivers/flash_w25q128fv.h" +#include "drivers/bus_quadspi.h" + +// JEDEC ID +#define JEDEC_ID_WINBOND_W25Q128FV_SPI 0xEF4018 +#define JEDEC_ID_WINBOND_W25Q128FV_QUADSPI 0xEF6018 +#define JEDEC_ID_WINBOND_W25Q128JV_QUADSPI 0xEF7018 + +// Device size parameters +#define W25Q128FV_PAGE_SIZE 2048 +#define W25Q128FV_PAGES_PER_BLOCK 64 +#define W25Q128FV_BLOCKS_PER_DIE 1024 +#define W25Q128FV_BLOCK_SIZE (W25Q128FV_PAGES_PER_BLOCK * W25Q128FV_PAGE_SIZE) + +// Sizes +#define W25Q128FV_STATUS_REGISTER_BITS 8 +#define W25Q128FV_ADDRESS_BITS 24 + + +// Instructions +#define W25Q128FV_INSTRUCTION_RDID 0x9F + +#define W25Q128FV_INSTRUCTION_ENABLE_RESET 0x66 +#define W25Q128FV_INSTRUCTION_RESET_DEVICE 0x99 + +#define W25Q128FV_INSTRUCTION_READ_STATUS1_REG 0x05 +#define W25Q128FV_INSTRUCTION_READ_STATUS2_REG 0x35 +#define W25Q128FV_INSTRUCTION_READ_STATUS3_REG 0x15 + +#define W25Q128FV_INSTRUCTION_WRITE_STATUS1_REG 0x01 +#define W25Q128FV_INSTRUCTION_WRITE_STATUS2_REG 0x31 +#define W25Q128FV_INSTRUCTION_WRITE_STATUS3_REG 0x11 + +#define W25Q128FV_INSTRUCTION_WRITE_ENABLE 0x06 +#define W25Q128FV_INSTRUCTION_VOLATILE_WRITE_ENABLE 0x50 +#define W25Q128FV_INSTRUCTION_BLOCK_ERASE_64KB 0xD8 +#define W25Q128FV_INSTRUCTION_CHIP_ERASE 0xC7 + +#define W25Q128FV_INSTRUCTION_ENTER_QPI_MODE 0x38 + +#define W25Q128FV_INSTRUCTION_FAST_READ 0x0B +#define W25Q128FV_INSTRUCTION_FAST_READ_QUAD_OUTPUT 0x6B + +#define W25Q128FV_INSTRUCTION_PAGE_PROGRAM 0x02 +#define W25Q128FV_INSTRUCTION_QUAD_PAGE_PROGRAM 0x32 + +#define W25Q128FV_SR1_BIT_WRITE_IN_PROGRESS (1 << 0) +#define W25Q128FV_SR1_BIT_WRITE_ENABLED (1 << 1) + +#define W25Q128FV_SR2_BIT_QUAD_ENABLE (1 << 1) + + +//#define W25Q128FV_INSTRUCTION_WRITE_DISABLE 0x04 +//#define W25Q128FV_INSTRUCTION_PAGE_PROGRAM 0x02 + +// Timings (2ms minimum to avoid 1 tick advance in consecutive calls to HAL_GetTick). +#define W25Q128FV_TIMEOUT_PAGE_READ_MS 4 +#define W25Q128FV_TIMEOUT_RESET_MS 2 // tRST = 30us +#define W25Q128FV_TIMEOUT_BLOCK_ERASE_64KB_MS 2000 // tBE2max = 2000ms, tBE2typ = 150ms +#define W25Q128FV_TIMEOUT_CHIP_ERASE_MS (200 * 1000) // tCEmax 200s, tCEtyp = 40s + +#define W25Q128FV_TIMEOUT_PAGE_PROGRAM_MS 2 // tPPmax = 700us, tPPtyp = 250us +#define W25Q128FV_TIMEOUT_WRITE_ENABLE_MS 2 + + +typedef enum { + INITIAL_MODE_SPI = 0, + INITIAL_MODE_QUADSPI, +} w25q128fv_initialMode_e; + +typedef struct w25q128fvState_s { + w25q128fv_initialMode_e initialMode; + uint32_t currentWriteAddress; +} w25q128fvState_t; + +w25q128fvState_t w25q128fvState = { 0 }; + +static bool w25q128fv_waitForReady(flashDevice_t *fdevice); +static void w25q128fv_waitForTimeout(flashDevice_t *fdevice); + +static void w25q128fv_setTimeout(flashDevice_t *fdevice, uint32_t timeoutMillis) +{ + uint32_t now = HAL_GetTick(); + fdevice->timeoutAt = now + timeoutMillis; +} + +static void w25q128fv_performOneByteCommand(flashDeviceIO_t *io, uint8_t command) +{ + QUADSPI_TypeDef *quadSpi = io->handle.quadSpi; + quadSpiTransmit1LINE(quadSpi, command, 0, NULL, 0); +} + +static void w25q128fv_performCommandWithAddress(flashDeviceIO_t *io, uint8_t command, uint32_t address) +{ + QUADSPI_TypeDef *quadSpi = io->handle.quadSpi; + + quadSpiInstructionWithAddress1LINE(quadSpi, command, 0, address & 0xffffff, W25Q128FV_ADDRESS_BITS); +} + +static void w25q128fv_writeEnable(flashDevice_t *fdevice) +{ + w25q128fv_performOneByteCommand(&fdevice->io, W25Q128FV_INSTRUCTION_WRITE_ENABLE); +} + +static uint8_t w25q128fv_readRegister(flashDeviceIO_t *io, uint8_t command) +{ + QUADSPI_TypeDef *quadSpi = io->handle.quadSpi; + + uint8_t in[1]; + quadSpiReceive1LINE(quadSpi, command, 0, in, W25Q128FV_STATUS_REGISTER_BITS / 8); + + return in[0]; +} + +static void w25q128fv_writeRegister(flashDeviceIO_t *io, uint8_t command, uint8_t data) +{ + QUADSPI_TypeDef *quadSpi = io->handle.quadSpi; + + quadSpiTransmit1LINE(quadSpi, command, 0, &data, W25Q128FV_STATUS_REGISTER_BITS / 8); +} + +static void w25q128fv_deviceReset(flashDevice_t *fdevice) +{ + flashDeviceIO_t *io = &fdevice->io; + + w25q128fv_waitForReady(fdevice); + w25q128fv_performOneByteCommand(io, W25Q128FV_INSTRUCTION_ENABLE_RESET); + w25q128fv_performOneByteCommand(io, W25Q128FV_INSTRUCTION_RESET_DEVICE); + + w25q128fv_setTimeout(fdevice, W25Q128FV_TIMEOUT_RESET_MS); + w25q128fv_waitForTimeout(fdevice); + + w25q128fv_waitForReady(fdevice); + +#ifdef DISABLE_NONVOLATILE_QE_MODE // Use this if you encounter a chip with it's QE bit enabled when it shouldn't be. + w25q128fv_performOneByteCommand(io, W25Q128FV_INSTRUCTION_WRITE_ENABLE); + w25q128fv_writeRegister(io, W25Q128FV_INSTRUCTION_WRITE_STATUS2_REG, 0x00); +#endif + + +#if defined(USE_FLASH_WRITES_USING_4LINES) || defined(USE_FLASH_READS_USING_4LINES) + uint8_t registerValue = w25q128fv_readRegister(io, W25Q128FV_INSTRUCTION_READ_STATUS2_REG); + + // + // WARNING: DO NOT ENABLE QE bit if IO2/IO3 are connected to GND or VCC. + // + // See datasheet https://www.winbond.com/resource-files/w25q128fv%20rev.m%2005132016%20kms.pdf + // W25Q128FV - Revision M - 7.1.10 Quad Enable + // + // There is no such warning for the W25Q128JV in the same documentation section + // See datasheet https://www.winbond.com/resource-files/w25q128jv%20revg%2004082019%20plus.pdf + // W25Q128JV - Revision G - 7.1.4 Quad Enable + // + + if ((registerValue & W25Q128FV_SR2_BIT_QUAD_ENABLE) == 0) { + // Enable QUADSPI mode. + registerValue = w25q128fv_readRegister(io, W25Q128FV_INSTRUCTION_READ_STATUS2_REG); + + uint8_t newValue = registerValue; + newValue |= W25Q128FV_SR2_BIT_QUAD_ENABLE; + + //w25q128fv_performOneByteCommand(io, W25Q128FV_INSTRUCTION_WRITE_ENABLE); + w25q128fv_performOneByteCommand(io, W25Q128FV_INSTRUCTION_VOLATILE_WRITE_ENABLE); + w25q128fv_writeRegister(io, W25Q128FV_INSTRUCTION_WRITE_STATUS2_REG, newValue); + } +#endif +} + +bool w25q128fv_isReady(flashDevice_t *fdevice) +{ + uint8_t status = w25q128fv_readRegister(&fdevice->io, W25Q128FV_INSTRUCTION_READ_STATUS1_REG); + + bool busy = (status & W25Q128FV_SR1_BIT_WRITE_IN_PROGRESS); + + return !busy; +} + +static bool w25q128fv_isWritable(flashDevice_t *fdevice) +{ + uint8_t status = w25q128fv_readRegister(&fdevice->io, W25Q128FV_INSTRUCTION_READ_STATUS1_REG); + + bool writable = (status & W25Q128FV_SR1_BIT_WRITE_ENABLED); + + return writable; +} + +bool w25q128fv_hasTimedOut(flashDevice_t *fdevice) +{ + uint32_t now = HAL_GetTick(); + if (cmp32(now, fdevice->timeoutAt) >= 0) { + return true; + } + return false; +} + +void w25q128fv_waitForTimeout(flashDevice_t *fdevice) +{ + while (!w25q128fv_hasTimedOut(fdevice)) { } + + fdevice->timeoutAt = 0; +} + +bool w25q128fv_waitForReady(flashDevice_t *fdevice) +{ + bool ready = true; + while (!w25q128fv_isReady(fdevice)) { + if (w25q128fv_hasTimedOut(fdevice)) { + ready = false; + break; + } + } + fdevice->timeoutAt = 0; + + return ready; +} + +const flashVTable_t w25q128fv_vTable; + +static void w25q128fv_deviceInit(flashDevice_t *flashdev); + +bool w25q128fv_detect(flashDevice_t *fdevice, uint32_t chipID) +{ + switch (chipID) { + case JEDEC_ID_WINBOND_W25Q128FV_SPI: + case JEDEC_ID_WINBOND_W25Q128FV_QUADSPI: + case JEDEC_ID_WINBOND_W25Q128JV_QUADSPI: + fdevice->geometry.sectors = 256; + fdevice->geometry.pagesPerSector = 256; + fdevice->geometry.pageSize = 256; + break; + + default: + // Unsupported chip + fdevice->geometry.sectors = 0; + fdevice->geometry.pagesPerSector = 0; + fdevice->geometry.sectorSize = 0; + fdevice->geometry.totalSize = 0; + return false; + } + + // use the chip id to determine the initial interface mode on cold-boot. + switch (chipID) { + case JEDEC_ID_WINBOND_W25Q128FV_SPI: + w25q128fvState.initialMode = INITIAL_MODE_SPI; + break; + + case JEDEC_ID_WINBOND_W25Q128JV_QUADSPI: + case JEDEC_ID_WINBOND_W25Q128FV_QUADSPI: + w25q128fvState.initialMode = INITIAL_MODE_QUADSPI; + break; + + default: + break; + } + + fdevice->geometry.flashType = FLASH_TYPE_NOR; + fdevice->geometry.sectorSize = fdevice->geometry.pagesPerSector * fdevice->geometry.pageSize; + fdevice->geometry.totalSize = fdevice->geometry.sectorSize * fdevice->geometry.sectors; + + w25q128fv_deviceReset(fdevice); + + w25q128fv_deviceInit(fdevice); + + fdevice->vTable = &w25q128fv_vTable; + + return true; +} + +void w25q128fv_eraseSector(flashDevice_t *fdevice, uint32_t address) +{ + + w25q128fv_waitForReady(fdevice); + + w25q128fv_writeEnable(fdevice); + + w25q128fv_performCommandWithAddress(&fdevice->io, W25Q128FV_INSTRUCTION_BLOCK_ERASE_64KB, address); + + w25q128fv_setTimeout(fdevice, W25Q128FV_TIMEOUT_BLOCK_ERASE_64KB_MS); +} + +void w25q128fv_eraseCompletely(flashDevice_t *fdevice) +{ + w25q128fv_waitForReady(fdevice); + + w25q128fv_writeEnable(fdevice); + + w25q128fv_performOneByteCommand(&fdevice->io, W25Q128FV_INSTRUCTION_CHIP_ERASE); + + w25q128fv_setTimeout(fdevice, W25Q128FV_TIMEOUT_CHIP_ERASE_MS); +} + + +static void w25q128fv_loadProgramData(flashDevice_t *fdevice, const uint8_t *data, int length) +{ + w25q128fv_waitForReady(fdevice); + + QUADSPI_TypeDef *quadSpi = fdevice->io.handle.quadSpi; + +#ifdef USE_FLASH_WRITES_USING_4LINES + quadSpiTransmitWithAddress4LINES(quadSpi, W25Q128FV_INSTRUCTION_QUAD_PAGE_PROGRAM, 0, w25q128fvState.currentWriteAddress, W25Q128FV_ADDRESS_BITS, data, length); +#else + quadSpiTransmitWithAddress1LINE(quadSpi, W25Q128FV_INSTRUCTION_PAGE_PROGRAM, 0, w25q128fvState.currentWriteAddress, W25Q128FV_ADDRESS_BITS, data, length); +#endif + + w25q128fv_setTimeout(fdevice, W25Q128FV_TIMEOUT_PAGE_PROGRAM_MS); + + w25q128fvState.currentWriteAddress += length; +} + +void w25q128fv_pageProgramBegin(flashDevice_t *fdevice, uint32_t address) +{ + UNUSED(fdevice); + w25q128fvState.currentWriteAddress = address; +} + +void w25q128fv_pageProgramContinue(flashDevice_t *fdevice, const uint8_t *data, int length) +{ + w25q128fv_waitForReady(fdevice); + + w25q128fv_writeEnable(fdevice); + + // verify write enable is set. + w25q128fv_setTimeout(fdevice, W25Q128FV_TIMEOUT_WRITE_ENABLE_MS); + bool writable = false; + do { + writable = w25q128fv_isWritable(fdevice); + } while (!writable && w25q128fv_hasTimedOut(fdevice)); + + if (!writable) { + return; // TODO report failure somehow. + } + + w25q128fv_loadProgramData(fdevice, data, length); + +} + +void w25q128fv_pageProgramFinish(flashDevice_t *fdevice) +{ + UNUSED(fdevice); +} + +void w25q128fv_pageProgram(flashDevice_t *fdevice, uint32_t address, const uint8_t *data, int length) +{ + w25q128fv_pageProgramBegin(fdevice, address); + w25q128fv_pageProgramContinue(fdevice, data, length); + w25q128fv_pageProgramFinish(fdevice); +} + +void w25q128fv_flush(flashDevice_t *fdevice) +{ + UNUSED(fdevice); +} + +int w25q128fv_readBytes(flashDevice_t *fdevice, uint32_t address, uint8_t *buffer, int length) +{ + if (!w25q128fv_waitForReady(fdevice)) { + return 0; + } + + QUADSPI_TypeDef *quadSpi = fdevice->io.handle.quadSpi; +#ifdef USE_FLASH_READS_USING_4LINES + bool status = quadSpiReceiveWithAddress4LINES(quadSpi, W25Q128FV_INSTRUCTION_FAST_READ_QUAD_OUTPUT, 8, address, W25Q128FV_ADDRESS_BITS, buffer, length); +#else + bool status = quadSpiReceiveWithAddress1LINE(quadSpi, W25Q128FV_INSTRUCTION_FAST_READ, 8, address, W25Q128FV_ADDRESS_BITS, buffer, length); +#endif + w25q128fv_setTimeout(fdevice, W25Q128FV_TIMEOUT_PAGE_READ_MS); + + if (!status) { + return 0; + } + + return length; +} + + + +const flashGeometry_t* w25q128fv_getGeometry(flashDevice_t *fdevice) +{ + return &fdevice->geometry; +} + +const flashVTable_t w25q128fv_vTable = { + .isReady = w25q128fv_isReady, + .waitForReady = w25q128fv_waitForReady, + .eraseSector = w25q128fv_eraseSector, + .eraseCompletely = w25q128fv_eraseCompletely, + .pageProgramBegin = w25q128fv_pageProgramBegin, + .pageProgramContinue = w25q128fv_pageProgramContinue, + .pageProgramFinish = w25q128fv_pageProgramFinish, + .pageProgram = w25q128fv_pageProgram, + .flush = w25q128fv_flush, + .readBytes = w25q128fv_readBytes, + .getGeometry = w25q128fv_getGeometry, +}; + +static void w25q128fv_deviceInit(flashDevice_t *flashdev) +{ + UNUSED(flashdev); +} + +#endif diff --git a/src/main/drivers/flash_w25q128fv.h b/src/main/drivers/flash_w25q128fv.h new file mode 100644 index 000000000..d28de244f --- /dev/null +++ b/src/main/drivers/flash_w25q128fv.h @@ -0,0 +1,7 @@ + +#pragma once + +#ifdef USE_FLASH_W25Q128FV + +bool w25q128fv_detect(flashDevice_t *fdevice, uint32_t chipID); +#endif diff --git a/src/main/target/NUCLEOH743/target.h b/src/main/target/NUCLEOH743/target.h index d97fb173e..7f4ab29af 100644 --- a/src/main/target/NUCLEOH743/target.h +++ b/src/main/target/NUCLEOH743/target.h @@ -121,7 +121,7 @@ #define SPI6_MISO_PIN NONE #define SPI6_MOSI_PIN NONE -// #define USE_QUADSPI +#define USE_QUADSPI #define USE_QUADSPI_DEVICE_1 #define QUADSPI1_SCK_PIN NONE // PB2 @@ -224,6 +224,8 @@ #define USE_FLASH_CHIP #define USE_FLASH_M25P16 #define USE_FLASH_W25M +#define USE_FLASH_W25Q128FV +#define USE_FLASH_W25N01G #define FLASH_SPI_INSTANCE NULL #define FLASH_CS_PIN NONE #define USE_FLASHFS