feat: SPI flash driver (#4012)
Co-authored-by: Richard Li <richard.li@ces.hk>
This commit is contained in:
co-authored by
Richard Li
parent
7400a598a1
commit
cb16786a74
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Authors:
|
||||
* Dr. Richard Li <richard.li@ces.hk>
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* FrFTL - Flash Resident Flash Translation Layer
|
||||
*
|
||||
* This is a FTL designed for NOR flash, logical to physical mapping uses 2 layers
|
||||
* of translation tables all resident in flash. It comes with mechanisms to ensure
|
||||
* the integrity of the data in previous state when power out occurs in the middle
|
||||
* of flash programming.
|
||||
*
|
||||
* It can be used to back the FatFS library by ChaN and included the support of
|
||||
* CTRL_SYNC and CTRL_TRIM functions for best performance.
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
bool (*flashRead)(uint32_t addr, uint8_t* buf, uint32_t len);
|
||||
bool (*flashProgram)(uint32_t addr, const uint8_t* buf, uint32_t len);
|
||||
bool (*flashErase)(uint32_t addr);
|
||||
bool (*isFlashErased)(uint32_t addr);
|
||||
} FrFTLOps;
|
||||
|
||||
typedef struct {
|
||||
const FrFTLOps* callbacks;
|
||||
uint32_t mttPhysicalPageNo;
|
||||
uint16_t physicalPageCount;
|
||||
uint8_t ttPageCount;
|
||||
uint32_t usableSectorCount;
|
||||
uint16_t writeFrontier;
|
||||
uint32_t* physicalPageState;
|
||||
bool physicalPageStateResolved;
|
||||
uint16_t pageBufferSize;
|
||||
void *pageBuffer;
|
||||
void* bufferHead; // LRU most used
|
||||
void* bufferTail; // LRU least used
|
||||
void* hashTable; // Fast cache lookup
|
||||
uint32_t memoryUsed;
|
||||
} FrFTL;
|
||||
|
||||
bool ftlInit(FrFTL* ftl, const FrFTLOps* cb, uint16_t flashSizeInMB);
|
||||
void ftlDeInit(FrFTL* ftl);
|
||||
|
||||
bool ftlWrite(FrFTL* ftl, uint32_t startSectorNo, uint32_t noOfSectors, const uint8_t* buf);
|
||||
bool ftlRead(FrFTL* ftl, uint32_t sectorNo, uint8_t* buffer);
|
||||
|
||||
bool ftlTrim(FrFTL* ftl, uint32_t startSectorNo, uint32_t noOfSectors);
|
||||
bool ftlSync(FrFTL* ftl);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -58,6 +58,21 @@ int fatfsRegisterDriver(const diskio_driver_t* drv, uint8_t lun)
|
||||
return 1;
|
||||
}
|
||||
|
||||
void fatfsUnregisterDrivers()
|
||||
{
|
||||
for (uint8_t i = 0; i < _fatfs_n_drives; i++) {
|
||||
auto& drive = _fatfs_drives[i];
|
||||
if (drive.initialized) {
|
||||
disk_ioctl(i, CTRL_SYNC, 0);
|
||||
if (drive.drv->deinit) {
|
||||
drive.drv->deinit(drive.lun);
|
||||
}
|
||||
drive.initialized = false;
|
||||
}
|
||||
}
|
||||
_fatfs_n_drives = 0;
|
||||
}
|
||||
|
||||
const diskio_driver_t* fatfsGetDriver(uint8_t pdrv)
|
||||
{
|
||||
if (pdrv >= _fatfs_n_drives) {
|
||||
|
||||
@@ -28,6 +28,8 @@ struct diskio_driver_t
|
||||
{
|
||||
DSTATUS (*initialize)(BYTE pdrv);
|
||||
|
||||
DSTATUS (*deinit)(BYTE pdrv);
|
||||
|
||||
DSTATUS (*status)(BYTE pdrv);
|
||||
|
||||
DRESULT (*read)(BYTE pdrv, BYTE* buff, DWORD sector, UINT count);
|
||||
@@ -40,6 +42,9 @@ struct diskio_driver_t
|
||||
// returns 1 if successful, 0 otherwise
|
||||
int fatfsRegisterDriver(const diskio_driver_t* drv, uint8_t lun);
|
||||
|
||||
// gracefully tear down all drivers
|
||||
void fatfsUnregisterDrivers();
|
||||
|
||||
// returns a pyhsical disk driver or NULL
|
||||
const diskio_driver_t* fatfsGetDriver(uint8_t pdrv);
|
||||
|
||||
|
||||
@@ -30,6 +30,9 @@
|
||||
#elif defined(STORAGE_USE_SDCARD_SPI)
|
||||
#include "diskio_spi.h"
|
||||
#define _STORAGE_DRIVER sdcard_spi_driver
|
||||
#elif defined(STORAGE_USE_SPI_FLASH)
|
||||
#include "diskio_spi_flash.h"
|
||||
#define _STORAGE_DRIVER spi_flash_diskio_driver
|
||||
#else
|
||||
#error "No supported storage driver configured"
|
||||
#endif
|
||||
@@ -62,6 +65,11 @@ void storageInit()
|
||||
}
|
||||
}
|
||||
|
||||
void storageDeInit()
|
||||
{
|
||||
fatfsUnregisterDrivers();
|
||||
}
|
||||
|
||||
void storagePreMountHook()
|
||||
{
|
||||
#if defined(DISK_CACHE)
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
// Note: can be used to attach the pyhsical volume driver
|
||||
// to the generic FatFs driver
|
||||
void storageInit();
|
||||
void storageDeInit();
|
||||
|
||||
// Called before the storage is mounted
|
||||
void storagePreMountHook();
|
||||
|
||||
@@ -78,6 +78,11 @@ if(PCB STREQUAL X10 OR PCB STREQUAL X12S OR PCB STREQUAL NV14)
|
||||
../../../../../thirdparty/libopenui/thirdparty/lz4/lz4.c
|
||||
../../../../../targets/common/arm/stm32/diskio_sdio.cpp
|
||||
../../../../../targets/common/arm/stm32/rtc_driver.cpp
|
||||
../../../../../targets/common/arm/stm32/diskio_spi_flash.cpp
|
||||
../../../../../targets/common/arm/stm32/spi_flash.cpp
|
||||
../../../../../targets/common/arm/stm32/stm32_spi.cpp
|
||||
../../../../../drivers/frftl.cpp
|
||||
../../../../../crc.cpp
|
||||
../../../../../targets/${TARGET_DIR}/sdram_driver.c
|
||||
../../../../../targets/${TARGET_DIR}/haptic_driver.cpp
|
||||
)
|
||||
|
||||
@@ -41,15 +41,21 @@ UINT BlockCount;
|
||||
|
||||
void sdInit(void)
|
||||
{
|
||||
static FATFS fatFS __DMA;
|
||||
|
||||
storageInit();
|
||||
|
||||
static FATFS fatFS __DMA;
|
||||
if (f_mount(&fatFS, "", 1) == FR_OK) {
|
||||
f_chdir("/");
|
||||
}
|
||||
}
|
||||
|
||||
void sdDone()
|
||||
{
|
||||
// unmount
|
||||
f_mount(0, "", 0);
|
||||
storageDeInit();
|
||||
}
|
||||
|
||||
FRESULT openBinDir(MemoryType mt)
|
||||
{
|
||||
FRESULT fr = f_chdir(getBinaryPath(mt));
|
||||
|
||||
@@ -550,11 +550,13 @@ int bootloaderMain()
|
||||
|
||||
if (state != ST_FLASHING && state != ST_USB) {
|
||||
if (pwrOffPressed()) {
|
||||
sdDone();
|
||||
boardOff();
|
||||
}
|
||||
}
|
||||
|
||||
if (state == ST_REBOOT) {
|
||||
sdDone();
|
||||
#if !defined(SIMU)
|
||||
#if defined(RTC_BACKUP_RAM)
|
||||
rtcInit();
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it 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.
|
||||
*/
|
||||
|
||||
#include "diskio_spi_flash.h"
|
||||
#include "spi_flash.h"
|
||||
|
||||
#if !defined(DISABLE_FLASH_FTL)
|
||||
#define USE_FLASH_FTL
|
||||
#endif
|
||||
|
||||
#if defined(USE_FLASH_FTL)
|
||||
#include "drivers/frftl.h"
|
||||
|
||||
static FrFTL _frftl;
|
||||
|
||||
static bool flashRead(uint32_t addr, uint8_t* buf, uint32_t len)
|
||||
{
|
||||
flashSpiRead(addr, buf, len);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool flashWrite(uint32_t addr, const uint8_t *buf, uint32_t len)
|
||||
{
|
||||
uint32_t pageSize = flashSpiGetPageSize();
|
||||
if(len % pageSize != 0)
|
||||
return false;
|
||||
|
||||
while(len > 0) {
|
||||
flashSpiWrite(addr, buf, pageSize);
|
||||
len -= pageSize;
|
||||
buf += pageSize;
|
||||
addr += pageSize;
|
||||
}
|
||||
|
||||
if(len != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool flashErase(uint32_t addr)
|
||||
{
|
||||
flashSpiErase(addr);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool isFlashErased(uint32_t addr)
|
||||
{
|
||||
return flashSpiIsErased(addr);
|
||||
}
|
||||
|
||||
static const FrFTLOps _frftl_cb = {
|
||||
.flashRead = flashRead,
|
||||
.flashProgram = flashWrite,
|
||||
.flashErase = flashErase,
|
||||
.isFlashErased = isFlashErased,
|
||||
};
|
||||
#endif
|
||||
|
||||
static DSTATUS spi_flash_initialize(BYTE lun)
|
||||
{
|
||||
if (!flashSpiInit()) {
|
||||
return STA_NOINIT;
|
||||
}
|
||||
|
||||
#if defined(USE_FLASH_FTL)
|
||||
int flashSize = flashSpiGetSize();
|
||||
int flashSizeMB = flashSize / 1024 / 1024;
|
||||
|
||||
if (!ftlInit(&_frftl, &_frftl_cb, flashSizeMB)) {
|
||||
return STA_NOINIT;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DSTATUS spi_flash_status (BYTE lun)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DRESULT spi_flash_read(BYTE lun, BYTE * buff, DWORD sector, UINT count)
|
||||
{
|
||||
#if defined(USE_FLASH_FTL)
|
||||
while(count) {
|
||||
|
||||
if(!ftlRead(&_frftl, sector, (uint8_t*)buff)) {
|
||||
return RES_ERROR;
|
||||
}
|
||||
|
||||
buff += 512;
|
||||
sector++;
|
||||
count --;
|
||||
}
|
||||
#else
|
||||
flashSpiRead((uint32_t)sector * 512, buff, count * 512);
|
||||
#endif
|
||||
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
static DRESULT spi_flash_write(BYTE lun, const BYTE *buff, DWORD sector, UINT count)
|
||||
{
|
||||
#if defined(USE_FLASH_FTL)
|
||||
if (!ftlWrite(&_frftl, sector, count, (uint8_t*)buff)) {
|
||||
return RES_ERROR;
|
||||
}
|
||||
#else
|
||||
// write in page size
|
||||
uint32_t address = (uint32_t)sector * 512;
|
||||
count <<= 1;
|
||||
while(count) {
|
||||
if (!flashSpiWrite(address, buff, 256)) {
|
||||
return RES_ERROR;
|
||||
}
|
||||
buff += 256;
|
||||
address += 256;
|
||||
count -= 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
static DRESULT spi_flash_ioctl(BYTE lun, BYTE ctrl, void *buff)
|
||||
{
|
||||
DRESULT res = RES_OK;
|
||||
|
||||
switch (ctrl) {
|
||||
|
||||
case GET_SECTOR_COUNT: /* Get number of sectors on the disk (DWORD) */
|
||||
#if defined(USE_FLASH_FTL)
|
||||
*(DWORD*)buff = _frftl.usableSectorCount;
|
||||
#else
|
||||
*(DWORD*)buff = flashSpiGetSize() / 512;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case GET_SECTOR_SIZE: /* Get R/W sector size (WORD) */
|
||||
*(WORD*)buff = 512;
|
||||
break;
|
||||
|
||||
case GET_BLOCK_SIZE : /* Get erase block size in unit of sector (DWORD) */
|
||||
*(DWORD*)buff = 4096 / 512;
|
||||
break;
|
||||
|
||||
case CTRL_SYNC:
|
||||
#if defined(USE_FLASH_FTL)
|
||||
if (!ftlSync(&_frftl)) {
|
||||
res = RES_ERROR;
|
||||
}
|
||||
#else
|
||||
flashSpiSync();
|
||||
#endif
|
||||
break;
|
||||
|
||||
case CTRL_TRIM:
|
||||
#if defined(USE_FLASH_FTL)
|
||||
if (!ftlTrim(&_frftl, *(DWORD*)buff, 1 + *((DWORD*)buff + 1) - *(DWORD*)buff)) {
|
||||
res = RES_ERROR;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
const diskio_driver_t spi_flash_diskio_driver = {
|
||||
.initialize = spi_flash_initialize,
|
||||
.status = spi_flash_status,
|
||||
.read = spi_flash_read,
|
||||
.write = spi_flash_write,
|
||||
.ioctl = spi_flash_ioctl,
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "hal/fatfs_diskio.h"
|
||||
|
||||
extern const diskio_driver_t spi_flash_diskio_driver;
|
||||
@@ -0,0 +1,378 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it 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.
|
||||
*/
|
||||
|
||||
#include "stm32_spi.h"
|
||||
#include "delays_driver.h"
|
||||
|
||||
#include "hal.h"
|
||||
|
||||
#define CS_HIGH() stm32_spi_unselect(&_flash_spi)
|
||||
#define CS_LOW() stm32_spi_select(&_flash_spi)
|
||||
|
||||
#define MIN(a,b) (a < b ? a : b)
|
||||
|
||||
#define FLASH_SECTOR_SIZE 4096
|
||||
#define FLASH_SECTOR_MASK (FLASH_SECTOR_SIZE - 1)
|
||||
|
||||
#define FLASH_BLOCK_SIZE 32768
|
||||
#define FLASH_BLOCK_MASK (FLASH_BLOCK_SIZE - 1)
|
||||
|
||||
#define FLASH_PAGE_SIZE 256
|
||||
#define FLASH_PAGE_MASK (FLASH_PAGE_SIZE - 1)
|
||||
|
||||
#define FLASH_CMD_READ_ID 0x90
|
||||
#define FLASH_CMD_READ_JEDEC_ID 0x9f
|
||||
#define FLASH_CMD_READ_SFDP 0x5a
|
||||
|
||||
#define FLASH_CMD_WRITE 0x02
|
||||
#define FLASH_CMD_READ 0x03
|
||||
|
||||
#define FLASH_CMD_STATUS 0x05
|
||||
#define FLASH_CMD_WRITE_ENABLE 0x06
|
||||
|
||||
#define FLASH_CMD_SECTOR_ERASE 0x20
|
||||
#define FLASH_CMD_BLOCK_ERASE 0x52
|
||||
#define FLASH_CMD_CHIP_ERASE 0xc7
|
||||
|
||||
#define FLASH_CMD_EN4B 0xb7
|
||||
|
||||
|
||||
struct SpiFlashDescriptor {
|
||||
uint16_t id;
|
||||
uint8_t log2Size;
|
||||
bool use4BytesAddress;
|
||||
};
|
||||
|
||||
// * RadioMaster/Eachine TX16S, RadioKing TX18S and Jumper T18 use GD25Q127C (16 MByte)
|
||||
// * FlySky PL18, Paladin EV and NV14 use WinBond W25Q64JV (8 MByte)
|
||||
|
||||
// TODO:
|
||||
// - fallback for flash size discovery (no JEDEC SFDP; see 'pico-bootrom')
|
||||
|
||||
|
||||
const stm32_spi_t _flash_spi = {
|
||||
.SPIx = FLASH_SPI,
|
||||
.SPI_GPIOx = FLASH_SPI_GPIO,
|
||||
.SPI_Pins = FLASH_SPI_SCK_GPIO_PIN | FLASH_SPI_MISO_GPIO_PIN | FLASH_SPI_MOSI_GPIO_PIN,
|
||||
.CS_GPIOx = FLASH_SPI_CS_GPIO,
|
||||
.CS_Pin = FLASH_SPI_CS_GPIO_PIN,
|
||||
.DMA = nullptr,
|
||||
.DMA_Channel = 0,
|
||||
.txDMA_Stream = 0,
|
||||
.rxDMA_Stream = 0,
|
||||
};
|
||||
|
||||
static SpiFlashDescriptor _flashDescriptor;
|
||||
|
||||
static inline void flash_select() { stm32_spi_select(&_flash_spi); }
|
||||
static inline void flash_unselect() { stm32_spi_unselect(&_flash_spi); }
|
||||
|
||||
static inline uint8_t flash_rw_byte(uint8_t value)
|
||||
{
|
||||
return stm32_spi_transfer_byte(&_flash_spi, value);
|
||||
}
|
||||
|
||||
static inline uint8_t flash_read_byte()
|
||||
{
|
||||
return flash_rw_byte(0xFF);
|
||||
}
|
||||
|
||||
static void flash_rw_bytes(const uint8_t* tx, uint8_t* rx, uint16_t count)
|
||||
{
|
||||
stm32_spi_transfer_bytes(&_flash_spi, tx, rx, count);
|
||||
}
|
||||
|
||||
static inline void flash_put_cmd_addr(uint8_t cmd, uint32_t addr)
|
||||
{
|
||||
addr |= cmd << 24;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
flash_rw_byte(addr >> 24);
|
||||
addr <<= 8;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void flash_put_cmd_4b_addr(uint8_t cmd, uint32_t addr)
|
||||
{
|
||||
flash_rw_byte(cmd);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
flash_rw_byte(addr >> 24);
|
||||
addr <<= 8;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void flash_do_cmd(uint8_t cmd, const uint8_t* tx, uint8_t* rx, uint16_t count)
|
||||
{
|
||||
flash_select();
|
||||
flash_rw_byte(cmd);
|
||||
flash_rw_bytes(tx, rx, count);
|
||||
flash_unselect();
|
||||
}
|
||||
|
||||
static void flash_wait_for_not_busy()
|
||||
{
|
||||
uint8_t status;
|
||||
do {
|
||||
flash_do_cmd(FLASH_CMD_STATUS, 0, &status, 1);
|
||||
} while (status & 0x01);
|
||||
}
|
||||
|
||||
static void flash_enable_write()
|
||||
{
|
||||
flash_do_cmd(FLASH_CMD_WRITE_ENABLE, 0, 0, 0);
|
||||
}
|
||||
|
||||
static void flash_read_sfdp(uint32_t addr, uint8_t *rx, uint16_t count)
|
||||
{
|
||||
flash_select();
|
||||
flash_put_cmd_addr(FLASH_CMD_READ_SFDP, addr);
|
||||
flash_read_byte(); // dummy byte
|
||||
flash_rw_bytes(0, rx, count);
|
||||
flash_unselect();
|
||||
}
|
||||
|
||||
static inline uint32_t bytes_to_u32le(const uint8_t *b) {
|
||||
return b[0] | (b[1] << 8) | (b[2] << 16) | (b[3] << 24);
|
||||
}
|
||||
|
||||
static bool flash_read_id(SpiFlashDescriptor* desc)
|
||||
{
|
||||
uint8_t rxbuf[2];
|
||||
|
||||
flash_select();
|
||||
flash_put_cmd_addr(FLASH_CMD_READ_ID, 0);
|
||||
flash_rw_bytes(0, rxbuf, 2);
|
||||
flash_unselect();
|
||||
|
||||
uint16_t id = (rxbuf[0] << 8) | rxbuf[1];
|
||||
if (id == 0xFFFF) {
|
||||
return false;
|
||||
}
|
||||
|
||||
desc->id = id;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool flash_read_sfdp(SpiFlashDescriptor* desc)
|
||||
{
|
||||
uint8_t rxbuf[16];
|
||||
|
||||
// check magic signature
|
||||
flash_read_sfdp(0, rxbuf, 16);
|
||||
if (rxbuf[0] != 'S' || rxbuf[1] != 'F' || rxbuf[2] != 'D' || rxbuf[3] != 'P') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// check JEDEC ID is 0
|
||||
if (rxbuf[8] != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// read param table
|
||||
uint32_t param_table_ptr = bytes_to_u32le(rxbuf + 12) & 0xffffffu;
|
||||
flash_read_sfdp(param_table_ptr, rxbuf, 8);
|
||||
|
||||
// 1st DWORD
|
||||
uint32_t param_table_dword = bytes_to_u32le(rxbuf);
|
||||
|
||||
if ((param_table_dword & 3) != 1) {
|
||||
// 4 kilobyte erase is unavailable
|
||||
return false;
|
||||
}
|
||||
|
||||
if (((param_table_dword >> 17) & 3) != 0) {
|
||||
// 4-byte addressing supported
|
||||
desc->use4BytesAddress = true;
|
||||
flash_do_cmd(FLASH_CMD_EN4B, 0, 0, 0);
|
||||
}
|
||||
|
||||
// 2nd DWORD: flash memory density
|
||||
// - MSB set: array >= 2 Gbit, encoded as log2 of number of bits
|
||||
// - MSB clear: array < 2 Gbit, encoded as direct bit count
|
||||
|
||||
param_table_dword = bytes_to_u32le(rxbuf + 4);
|
||||
if (param_table_dword & (1u << 31)) {
|
||||
param_table_dword &= ~(1u << 31);
|
||||
} else {
|
||||
uint32_t ctr = 0;
|
||||
param_table_dword += 1;
|
||||
while (param_table_dword >>= 1)
|
||||
++ctr;
|
||||
param_table_dword = ctr;
|
||||
}
|
||||
desc->log2Size = param_table_dword - 3;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool flashSpiInit(void)
|
||||
{
|
||||
stm32_spi_init(&_flash_spi);
|
||||
delay_ms(1);
|
||||
|
||||
if (!flash_read_id(&_flashDescriptor)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!flash_read_sfdp(&_flashDescriptor)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void flashSpiSync()
|
||||
{
|
||||
uint8_t status;
|
||||
do {
|
||||
flash_do_cmd(FLASH_CMD_STATUS, 0, &status, 1);
|
||||
} while (status & 0x01);
|
||||
}
|
||||
|
||||
uint32_t flashSpiGetSize()
|
||||
{
|
||||
return (1UL << _flashDescriptor.log2Size);
|
||||
}
|
||||
|
||||
bool flashSpiIsErased(uint32_t address)
|
||||
{
|
||||
// verify sector alignment
|
||||
if((address & FLASH_SECTOR_MASK) != 0)
|
||||
return false;
|
||||
|
||||
flash_wait_for_not_busy();
|
||||
flash_select();
|
||||
|
||||
if (_flashDescriptor.use4BytesAddress) {
|
||||
flash_put_cmd_4b_addr(FLASH_CMD_READ, address);
|
||||
} else {
|
||||
flash_put_cmd_addr(FLASH_CMD_READ, address);
|
||||
}
|
||||
|
||||
bool ret = true;
|
||||
for(uint32_t i = 0; i < FLASH_SECTOR_SIZE; i++) {
|
||||
uint8_t byte = flash_rw_byte(0xFF);
|
||||
if (byte != 0xff) {
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
flash_unselect();
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t flashSpiRead(uint32_t address, uint8_t* data, uint32_t size)
|
||||
{
|
||||
size = MIN(size, (uint32_t)(flashSpiGetSize() - address));
|
||||
|
||||
flash_wait_for_not_busy();
|
||||
flash_select();
|
||||
|
||||
if (_flashDescriptor.use4BytesAddress) {
|
||||
flash_put_cmd_4b_addr(FLASH_CMD_READ, address);
|
||||
} else {
|
||||
flash_put_cmd_addr(FLASH_CMD_READ, address);
|
||||
}
|
||||
flash_rw_bytes(0, data, size);
|
||||
|
||||
flash_unselect();
|
||||
return size;
|
||||
}
|
||||
|
||||
uint32_t flashSpiWrite(uint32_t address, const uint8_t* data, uint32_t size)
|
||||
{
|
||||
size = MIN(size, (uint32_t)(flashSpiGetSize() - address));
|
||||
|
||||
// verify page alignment and size
|
||||
if (size != FLASH_PAGE_SIZE || (address & FLASH_PAGE_MASK) != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
flash_wait_for_not_busy();
|
||||
flash_enable_write();
|
||||
|
||||
flash_select();
|
||||
if (_flashDescriptor.use4BytesAddress) {
|
||||
flash_put_cmd_4b_addr(FLASH_CMD_WRITE, address);
|
||||
} else {
|
||||
flash_put_cmd_addr(FLASH_CMD_WRITE, address);
|
||||
}
|
||||
flash_rw_bytes(data, 0, size);
|
||||
flash_unselect();
|
||||
|
||||
flash_wait_for_not_busy();
|
||||
return size;
|
||||
}
|
||||
|
||||
int flashSpiErase(uint32_t address)
|
||||
{
|
||||
if((address & FLASH_SECTOR_MASK) != 0)
|
||||
return -1;
|
||||
|
||||
flash_wait_for_not_busy();
|
||||
flash_enable_write();
|
||||
|
||||
flash_select();
|
||||
if (_flashDescriptor.use4BytesAddress) {
|
||||
flash_put_cmd_4b_addr(FLASH_CMD_SECTOR_ERASE, address);
|
||||
} else {
|
||||
flash_put_cmd_addr(FLASH_CMD_SECTOR_ERASE, address);
|
||||
}
|
||||
flash_unselect();
|
||||
|
||||
flash_wait_for_not_busy();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int flashSpiBlockErase(uint32_t address)
|
||||
{
|
||||
// verify block alignment
|
||||
if((address & FLASH_BLOCK_MASK) != 0)
|
||||
return -1;
|
||||
|
||||
flash_wait_for_not_busy();
|
||||
flash_enable_write();
|
||||
|
||||
flash_select();
|
||||
if (_flashDescriptor.use4BytesAddress) {
|
||||
flash_put_cmd_4b_addr(FLASH_CMD_BLOCK_ERASE, address);
|
||||
} else {
|
||||
flash_put_cmd_addr(FLASH_CMD_BLOCK_ERASE, address);
|
||||
}
|
||||
flash_unselect();
|
||||
|
||||
flash_wait_for_not_busy();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void flashSpiEraseAll()
|
||||
{
|
||||
flash_wait_for_not_busy();
|
||||
flash_enable_write();
|
||||
flash_do_cmd(FLASH_CMD_CHIP_ERASE, 0, 0, 0);
|
||||
flash_wait_for_not_busy();
|
||||
}
|
||||
|
||||
uint16_t flashSpiGetPageSize()
|
||||
{
|
||||
return FLASH_PAGE_SIZE;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) EdgeTX
|
||||
*
|
||||
* Based on code named
|
||||
* opentx - https://github.com/opentx/opentx
|
||||
* th9x - http://code.google.com/p/th9x
|
||||
* er9x - http://code.google.com/p/er9x
|
||||
* gruvin9x - http://code.google.com/p/gruvin9x
|
||||
*
|
||||
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it 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.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
bool flashSpiInit();
|
||||
|
||||
uint32_t flashSpiRead(uint32_t address, uint8_t* data, uint32_t size);
|
||||
uint32_t flashSpiWrite(uint32_t address, const uint8_t* data, uint32_t size);
|
||||
|
||||
uint32_t flashSpiGetSize();
|
||||
uint16_t flashSpiGetPageSize();
|
||||
|
||||
int flashSpiErase(uint32_t address);
|
||||
bool flashSpiIsErased(uint32_t address);
|
||||
int flashSpiBlockErase(uint32_t address);
|
||||
|
||||
void flashSpiEraseAll();
|
||||
void flashSpiSync();
|
||||
@@ -272,6 +272,9 @@ set(FIRMWARE_SRC
|
||||
targets/common/arm/stm32/trainer_driver.cpp
|
||||
targets/common/arm/stm32/ads79xx.cpp
|
||||
targets/common/arm/stm32/diskio_sdio.cpp
|
||||
targets/common/arm/stm32/spi_flash.cpp
|
||||
targets/common/arm/stm32/diskio_spi_flash.cpp
|
||||
drivers/frftl.cpp
|
||||
)
|
||||
|
||||
if(BOOTLOADER)
|
||||
|
||||
@@ -281,6 +281,7 @@
|
||||
#define ADC_EXT_CHANNELS { ADC_CHANNEL_RTC_BATT }
|
||||
#define ADC_DMA DMA2
|
||||
#define ADC_DMA_CHANNEL LL_DMA_CHANNEL_2
|
||||
// TODO: use for SPI1_RX instead?
|
||||
#define ADC_DMA_STREAM LL_DMA_STREAM_0
|
||||
#define ADC_DMA_STREAM_IRQ DMA2_Stream0_IRQn
|
||||
#define ADC_DMA_STREAM_IRQHandler DMA2_Stream0_IRQHandler
|
||||
@@ -624,7 +625,7 @@
|
||||
#define SD_PRESENT_GPIO_PIN GPIO_Pin_5 // PC.05
|
||||
#define SD_PRESENT_LL_GPIO_PIN LL_GPIO_PIN_5 // PC.05
|
||||
#define SD_SDIO_DMA DMA2
|
||||
#define SD_SDIO_DMA_STREAM DMA2_Stream3
|
||||
#define SD_SDIO_DMA_STREAM DMA2_Stream3 // or Stream6
|
||||
#define SD_SDIO_DMA_CHANNEL LL_DMA_CHANNEL_4
|
||||
#define SD_SDIO_DMA_IRQn DMA2_Stream3_IRQn
|
||||
#define SD_SDIO_DMA_IRQHANDLER DMA2_Stream3_IRQHandler
|
||||
@@ -633,35 +634,49 @@
|
||||
#define SD_SDIO_TRANSFER_CLK_DIV SD_SDIO_CLK_DIV(24000000)
|
||||
#define STORAGE_USE_SDIO
|
||||
|
||||
// EEPROM
|
||||
// SPI NOR Flash
|
||||
#if defined(PCBX12S) && PCBREV >= 13
|
||||
#define EEPROM_RCC_AHB1Periph RCC_AHB1Periph_GPIOA
|
||||
#define EEPROM_RCC_APB1Periph RCC_APB1Periph_SPI1
|
||||
#define EEPROM_SPI_CS_GPIO GPIOA
|
||||
#define EEPROM_SPI_CS_GPIO_PIN GPIO_Pin_15 // PA.15
|
||||
#define EEPROM_SPI_SCK_GPIO GPIOA
|
||||
#define EEPROM_SPI_SCK_GPIO_PIN GPIO_Pin_5 // PA.05
|
||||
#define EEPROM_SPI_SCK_GPIO_PinSource GPIO_PinSource5
|
||||
#define EEPROM_SPI_MISO_GPIO GPIOA
|
||||
#define EEPROM_SPI_MISO_GPIO_PIN GPIO_Pin_6 // PA.06
|
||||
#define EEPROM_SPI_MISO_GPIO_PinSource GPIO_PinSource6
|
||||
#define EEPROM_SPI_MOSI_GPIO GPIOA
|
||||
#define EEPROM_SPI_MOSI_GPIO_PIN GPIO_Pin_7 // PA.07
|
||||
#define EEPROM_SPI_MOSI_GPIO_PinSource GPIO_PinSource7
|
||||
#define FLASH_SPI SPI1
|
||||
#define FLASH_SPI_CS_GPIO GPIOA
|
||||
#define FLASH_SPI_CS_GPIO_PIN LL_GPIO_PIN_15 // PA.15
|
||||
#define FLASH_SPI_GPIO GPIOA
|
||||
#define FLASH_SPI_SCK_GPIO_PIN LL_GPIO_PIN_5 // PA.05
|
||||
#define FLASH_SPI_MISO_GPIO_PIN LL_GPIO_PIN_6 // PA.06
|
||||
#define FLASH_SPI_MOSI_GPIO_PIN LL_GPIO_PIN_7 // PA.07
|
||||
// SPI1_TX: DMA2 Stream 3 (SDIO) / Stream 5 (Ext. module timer)
|
||||
// #define FLASH_SPI_TX_DMA_CHANNEL DMA_Channel_3
|
||||
// #define FLASH_SPI_TX_DMA_STREAM DMA2_Stream3
|
||||
// #define FLASH_SPI_TX_DMA_IRQn DMA2_Stream3_IRQn
|
||||
// #define FLASH_SPI_TX_DMA_IRQHandler DMA2_Stream3_IRQHandler
|
||||
// #define FLASH_SPI_TX_DMA_FLAG_TC DMA_IT_TCIF3
|
||||
// #define FLASH_SPI_TX_DMA_STATUS_REG HISR
|
||||
// SPI1_RX: DMA2 Stream 0 / Stream 2
|
||||
// #define FLASH_SPI_RX_DMA_CHANNEL DMA_Channel_3
|
||||
// #define FLASH_SPI_RX_DMA_STREAM DMA2_Stream5
|
||||
// #define FLASH_SPI_RX_DMA_IRQn DMA2_Stream5_IRQn
|
||||
// #define FLASH_SPI_RX_DMA_IRQHandler DMA2_Stream5_IRQHandler
|
||||
// #define FLASH_SPI_RX_DMA_STATUS_REG HISR
|
||||
// #define FLASH_SPI_RX_DMA_FLAG_TC DMA_IT_TCIF5
|
||||
#elif defined(PCBX10)
|
||||
#define EEPROM_RCC_AHB1Periph RCC_AHB1Periph_GPIOI
|
||||
#define EEPROM_RCC_APB1Periph RCC_APB1Periph_SPI2
|
||||
#define EEPROM_SPI_CS_GPIO GPIOI
|
||||
#define EEPROM_SPI_CS_GPIO_PIN GPIO_Pin_0 // PI.00
|
||||
#define EEPROM_SPI_SCK_GPIO GPIOI
|
||||
#define EEPROM_SPI_SCK_GPIO_PIN GPIO_Pin_1 // PI.01
|
||||
#define EEPROM_SPI_SCK_GPIO_PinSource GPIO_PinSource1
|
||||
#define EEPROM_SPI_MISO_GPIO GPIOI
|
||||
#define EEPROM_SPI_MISO_GPIO_PIN GPIO_Pin_2 // PI.02
|
||||
#define EEPROM_SPI_MISO_GPIO_PinSource GPIO_PinSource2
|
||||
#define EEPROM_SPI_MOSI_GPIO GPIOI
|
||||
#define EEPROM_SPI_MOSI_GPIO_PIN GPIO_Pin_3 // PI.03
|
||||
#define EEPROM_SPI_MOSI_GPIO_PinSource GPIO_PinSource3
|
||||
#define FLASH_SPI SPI2
|
||||
#define FLASH_SPI_CS_GPIO GPIOI
|
||||
#define FLASH_SPI_CS_GPIO_PIN LL_GPIO_PIN_0 // PI.00
|
||||
#define FLASH_SPI_GPIO GPIOI
|
||||
#define FLASH_SPI_SCK_GPIO_PIN LL_GPIO_PIN_1 // PI.01
|
||||
#define FLASH_SPI_MISO_GPIO_PIN LL_GPIO_PIN_2 // PI.02
|
||||
#define FLASH_SPI_MOSI_GPIO_PIN LL_GPIO_PIN_3 // PI.03
|
||||
// #define FLASH_SPI_TX_DMA_CHANNEL DMA_Channel_0
|
||||
// #define FLASH_SPI_TX_DMA_STREAM DMA1_Stream4
|
||||
// #define FLASH_SPI_TX_DMA_IRQn DMA1_Stream4_IRQn
|
||||
// #define FLASH_SPI_TX_DMA_IRQHandler DMA1_Stream4_IRQHandler
|
||||
// #define FLASH_SPI_TX_DMA_FLAG_TC DMA_IT_TCIF4
|
||||
// #define FLASH_SPI_TX_DMA_STATUS_REG HISR
|
||||
// #define FLASH_SPI_RX_DMA_CHANNEL DMA_Channel_0
|
||||
// #define FLASH_SPI_RX_DMA_STREAM DMA1_Stream3
|
||||
// #define FLASH_SPI_RX_DMA_IRQn DMA1_Stream3_IRQn
|
||||
// #define FLASH_SPI_RX_DMA_IRQHandler DMA1_Stream3_IRQHandler
|
||||
// #define FLASH_SPI_RX_DMA_STATUS_REG LISR
|
||||
// #define FLASH_SPI_RX_DMA_FLAG_TC DMA_IT_TCIF3
|
||||
#endif
|
||||
|
||||
// Audio
|
||||
@@ -819,7 +834,7 @@
|
||||
#define FLYSKY_HALL_SERIAL_USART_IRQn UART4_IRQn
|
||||
#define FLYSKY_HALL_SERIAL_DMA DMA1
|
||||
#define FLYSKY_HALL_DMA_Stream_RX LL_DMA_STREAM_2
|
||||
#define FLYSKY_HALL_DMA_Stream_TX LL_DMA_STREAM_4
|
||||
// #define FLYSKY_HALL_DMA_Stream_TX LL_DMA_STREAM_4
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -153,6 +153,9 @@ set(FIRMWARE_SRC
|
||||
targets/common/arm/stm32/trainer_driver.cpp
|
||||
targets/common/arm/stm32/pwr_driver.cpp
|
||||
targets/common/arm/stm32/diskio_sdio.cpp
|
||||
targets/common/arm/stm32/spi_flash.cpp
|
||||
targets/common/arm/stm32/diskio_spi_flash.cpp
|
||||
drivers/frftl.cpp
|
||||
targets/common/arm/stm32/vs1053b.cpp
|
||||
)
|
||||
|
||||
|
||||
@@ -307,6 +307,15 @@
|
||||
#define SD_SDIO_TRANSFER_CLK_DIV SD_SDIO_CLK_DIV(24000000)
|
||||
#define STORAGE_USE_SDIO
|
||||
|
||||
// SPI NOR Flash
|
||||
#define FLASH_SPI SPI6
|
||||
#define FLASH_SPI_CS_GPIO GPIOG
|
||||
#define FLASH_SPI_CS_GPIO_PIN LL_GPIO_PIN_6 // PG.06
|
||||
#define FLASH_SPI_GPIO GPIOG
|
||||
#define FLASH_SPI_SCK_GPIO_PIN LL_GPIO_PIN_13 // PG.13
|
||||
#define FLASH_SPI_MISO_GPIO_PIN LL_GPIO_PIN_12 // PG.12
|
||||
#define FLASH_SPI_MOSI_GPIO_PIN LL_GPIO_PIN_14 // PG.14
|
||||
|
||||
// SDRAM
|
||||
#define SDRAM_RCC_AHB1Periph (RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD | RCC_AHB1Periph_GPIOE | RCC_AHB1Periph_GPIOF | RCC_AHB1Periph_GPIOG | RCC_AHB1Periph_GPIOH)
|
||||
#define SDRAM_RCC_AHB3Periph RCC_AHB3Periph_FMC
|
||||
|
||||
Reference in New Issue
Block a user