mirror of
https://github.com/BrianPugh/gnwmanager.git
synced 2025-12-06 19:15:56 +01:00
895 lines
34 KiB
C
895 lines
34 KiB
C
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
|
|
#include "flash.h"
|
|
#include "main.h"
|
|
#include "utils.h"
|
|
|
|
//#define DBG(...) printf(__VA_ARGS__)
|
|
#define DBG(...)
|
|
|
|
// Convenience macro to access the struct for a command
|
|
// in the active flash configuration.
|
|
#define CMD(cmd_name) &flash.config->commands[CMD_##cmd_name]
|
|
|
|
#define TMO_DEFAULT 1000
|
|
|
|
// 3-byte JEDEC ID to uint32_t
|
|
#define JEDEC_ID(_x0, _x1, _x2) ( (uint32_t) ( \
|
|
((_x0) ) | \
|
|
((_x1) << 8) | \
|
|
((_x2) << 16) \
|
|
))
|
|
|
|
// Convenience macro to initialize a flash_cmd_t struct
|
|
#define CMD_DEF(_cmd, _instr_lines, _addr_lines, _addr_size, _data_lines, _dummy) \
|
|
{ \
|
|
.cmd = (_cmd), \
|
|
.instr_lines = (_instr_lines), \
|
|
.addr_lines = (_addr_lines), \
|
|
.addr_size = (_addr_size), \
|
|
.data_lines = (_data_lines), \
|
|
.dummy = (_dummy) \
|
|
}
|
|
|
|
// Convenience macro to initialize a flash_config_t struct
|
|
#define FLASH_CONFIG_DEF(_commands, _erase1_size, _erase2_size, _erase3_size, _erase4_size, _set_quad, _init_fn) \
|
|
{ \
|
|
.commands = (_commands), \
|
|
.erase_sizes = { (_erase1_size), (_erase2_size), (_erase3_size), (_erase4_size) }, \
|
|
.set_quad = (_set_quad), \
|
|
.init_fn = (_init_fn), \
|
|
}
|
|
|
|
#define JEDEC_CONFIG_DEF(_x0, _x1, _x2, _name, _config, _size) \
|
|
{ \
|
|
.jedec_id.u32 = JEDEC_ID((_x0), (_x1), (_x2)), \
|
|
.name = (_name), \
|
|
.config = (_config), \
|
|
.size = (_size), \
|
|
}
|
|
|
|
// Generic Status Register (SR) bits
|
|
#define STATUS_WIP_Pos (0U)
|
|
#define STATUS_WIP_Msk (1UL << STATUS_WIP_Pos)
|
|
#define STATUS_WEL_Pos (1U)
|
|
#define STATUS_WEL_Msk (1UL << STATUS_WEL_Pos)
|
|
|
|
// MX and ISSI specific
|
|
#define STATUS_BP0_Pos (2U)
|
|
#define STATUS_BP0_Msk (1UL << STATUS_BP0_Pos)
|
|
#define STATUS_BP1_Pos (3U)
|
|
#define STATUS_BP1_Msk (1UL << STATUS_BP1_Pos)
|
|
#define STATUS_BP2_Pos (4U)
|
|
#define STATUS_BP2_Msk (1UL << STATUS_BP2_Pos)
|
|
#define STATUS_BP3_Pos (5U)
|
|
#define STATUS_BP3_Msk (1UL << STATUS_BP3_Pos)
|
|
#define STATUS_QE_Pos (6U)
|
|
#define STATUS_QE_Msk (1UL << STATUS_QE_Pos)
|
|
#define STATUS_SRWD_Pos (7U)
|
|
#define STATUS_SRWD_Msk (1UL << STATUS_SRWD_Pos)
|
|
|
|
// S (Spansion/Cypress/Infineon) specific CR bits
|
|
#define S_CR_QUAD_Pos (1U)
|
|
#define S_CR_QUAD_Msk (1UL << S_CR_QUAD_Pos)
|
|
|
|
// WB (Winbond) specific SR1-3 (status register) bits
|
|
#define WB_SR1_PROTECT_Msk 0xFC
|
|
#define WB_SR2_PROTECT_Msk 0x41 // excluding OTP security register lock bits
|
|
#define WB_SR2_QE_Pos 1
|
|
#define WB_SR3_PROTECT_Msk 0x04
|
|
#define WB_SR3_DRV_Msk (0b11<<5)
|
|
#define WB_SR3_DRV_Val_50 (0b10<<5) // 50%
|
|
#define WB_SR3_ADS_Pos 0
|
|
|
|
typedef enum {
|
|
LINES_0, // Mapped to HAL_OSPI_*_NONE
|
|
LINES_1, // Mapped to HAL_OSPI_*_1_LINE
|
|
LINES_4, // Mapped to HAL_OSPI_*_4_LINES
|
|
} lines_t;
|
|
|
|
typedef enum {
|
|
ADDR_SIZE_8B, // Mapped to HAL_OSPI_ADDRESS_8_BITS
|
|
ADDR_SIZE_16B, // Mapped to HAL_OSPI_ADDRESS_16_BITS
|
|
ADDR_SIZE_24B, // Mapped to HAL_OSPI_ADDRESS_24_BITS
|
|
ADDR_SIZE_32B, // Mapped to HAL_OSPI_ADDRESS_32_BITS
|
|
} addr_size_t;
|
|
|
|
const uint32_t instruction_line_map[] = {
|
|
[LINES_0] = HAL_OSPI_INSTRUCTION_NONE,
|
|
[LINES_1] = HAL_OSPI_INSTRUCTION_1_LINE,
|
|
[LINES_4] = HAL_OSPI_INSTRUCTION_4_LINES,
|
|
};
|
|
|
|
const uint32_t address_line_map[] = {
|
|
[LINES_0] = HAL_OSPI_ADDRESS_NONE,
|
|
[LINES_1] = HAL_OSPI_ADDRESS_1_LINE,
|
|
[LINES_4] = HAL_OSPI_ADDRESS_4_LINES,
|
|
};
|
|
|
|
const uint32_t address_size_map[] = {
|
|
[ADDR_SIZE_8B] = HAL_OSPI_ADDRESS_8_BITS,
|
|
[ADDR_SIZE_16B] = HAL_OSPI_ADDRESS_16_BITS,
|
|
[ADDR_SIZE_24B] = HAL_OSPI_ADDRESS_24_BITS,
|
|
[ADDR_SIZE_32B] = HAL_OSPI_ADDRESS_32_BITS,
|
|
};
|
|
|
|
const uint32_t data_line_map[] = {
|
|
[LINES_0] = HAL_OSPI_DATA_NONE,
|
|
[LINES_1] = HAL_OSPI_DATA_1_LINE,
|
|
[LINES_4] = HAL_OSPI_DATA_4_LINES,
|
|
};
|
|
|
|
enum {
|
|
CMD_WRSR, // Write Status Register
|
|
CMD_RDSR, // Read Status Register
|
|
CMD_RDCR, // Read Configuration Register
|
|
CMD_RDAR, // Read Any Register
|
|
CMD_WREN, // Write Enable
|
|
CMD_RDID, // Read Identification
|
|
CMD_RSTEN, // Reset Enable
|
|
CMD_RST, // Reset
|
|
|
|
CMD_CE, // Chip Erase
|
|
CMD_ERASE1, // Usually 4kB 20h
|
|
CMD_ERASE2, // Usually 32kB 52h
|
|
CMD_ERASE3, // Usually 64kB d8h
|
|
CMD_ERASE4, // Usually unsupported
|
|
|
|
CMD_PP, // Page Program
|
|
CMD_READ, // Read Data Bytes
|
|
|
|
CMD_COUNT,
|
|
};
|
|
|
|
typedef void (*init_fn_t)(void);
|
|
|
|
typedef struct {
|
|
uint8_t cmd; // Command / Instruction
|
|
uint8_t instr_lines : 2; // Instruction Lines
|
|
lines_t addr_lines : 2; // Address Lines
|
|
addr_size_t addr_size : 2; // Address Size
|
|
uint8_t data_lines : 2; // Data Lines
|
|
uint8_t dummy; // Dummy Cycles
|
|
} flash_cmd_t;
|
|
|
|
typedef struct {
|
|
const flash_cmd_t *commands;
|
|
uint32_t erase_sizes[4]; // [0] = ERASE1, ... [3] = ERASE4
|
|
bool set_quad; // If quad mode should be enabled
|
|
init_fn_t init_fn; // Chip/vendor specific init function
|
|
} flash_config_t;
|
|
|
|
typedef union {
|
|
uint32_t u32;
|
|
uint8_t u8[4];
|
|
} jedec_id_t;
|
|
|
|
typedef struct {
|
|
jedec_id_t jedec_id;
|
|
const char *name;
|
|
const flash_config_t *config;
|
|
uint32_t size;
|
|
} jedec_config_t;
|
|
|
|
const flash_cmd_t cmds_spi_24b[CMD_COUNT] = {
|
|
// cmd cmd i_lines a_lines a_size d_lines dummy
|
|
[CMD_WRSR] = CMD_DEF(0x01, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_1, 0),
|
|
[CMD_RDSR] = CMD_DEF(0x05, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_1, 0),
|
|
[CMD_RDCR] = CMD_DEF(0x15, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_1, 0),
|
|
[CMD_WREN] = CMD_DEF(0x06, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_0, 0),
|
|
[CMD_RDID] = CMD_DEF(0x9F, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_1, 0),
|
|
[CMD_RSTEN] = CMD_DEF(0x66, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_0, 0),
|
|
[CMD_RST] = CMD_DEF(0x99, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_0, 0),
|
|
[CMD_CE] = CMD_DEF(0x60, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_0, 0), // CE Chip Erase
|
|
[CMD_ERASE1] = CMD_DEF(0x20, LINES_1, LINES_1, ADDR_SIZE_24B, LINES_0, 0), // SE Sector Erase
|
|
[CMD_ERASE2] = CMD_DEF(0x52, LINES_1, LINES_1, ADDR_SIZE_24B, LINES_0, 0), // BE32K Block Erase 32K
|
|
[CMD_ERASE3] = CMD_DEF(0xD8, LINES_1, LINES_1, ADDR_SIZE_24B, LINES_0, 0), // BE Block Erase 64K
|
|
[CMD_ERASE4] = { },
|
|
[CMD_PP] = CMD_DEF(0x02, LINES_1, LINES_1, ADDR_SIZE_24B, LINES_1, 0), // PP
|
|
[CMD_READ] = CMD_DEF(0x0B, LINES_1, LINES_1, ADDR_SIZE_24B, LINES_1, 8), // FAST_READ dummy=8
|
|
};
|
|
|
|
const flash_cmd_t cmds_quad_24b_mx[CMD_COUNT] = {
|
|
// cmd cmd i_lines a_lines a_size d_lines dummy
|
|
[CMD_WRSR] = CMD_DEF(0x01, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_1, 0),
|
|
[CMD_RDSR] = CMD_DEF(0x05, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_1, 0),
|
|
[CMD_RDCR] = CMD_DEF(0x15, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_1, 0),
|
|
[CMD_WREN] = CMD_DEF(0x06, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_0, 0),
|
|
[CMD_RDID] = CMD_DEF(0x9F, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_1, 0),
|
|
[CMD_RSTEN] = CMD_DEF(0x66, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_0, 0),
|
|
[CMD_RST] = CMD_DEF(0x99, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_0, 0),
|
|
[CMD_CE] = CMD_DEF(0x60, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_0, 0), // CE Chip Erase
|
|
[CMD_ERASE1] = CMD_DEF(0x20, LINES_1, LINES_1, ADDR_SIZE_24B, LINES_0, 0), // SE Sector Erase
|
|
[CMD_ERASE2] = CMD_DEF(0x52, LINES_1, LINES_1, ADDR_SIZE_24B, LINES_0, 0), // BE32K Block Erase 32K
|
|
[CMD_ERASE3] = CMD_DEF(0xD8, LINES_1, LINES_1, ADDR_SIZE_24B, LINES_0, 0), // BE Block Erase 64K
|
|
[CMD_ERASE4] = { },
|
|
[CMD_PP] = CMD_DEF(0x38, LINES_1, LINES_4, ADDR_SIZE_24B, LINES_4, 0), // 4PP
|
|
[CMD_READ] = CMD_DEF(0xEB, LINES_1, LINES_4, ADDR_SIZE_24B, LINES_4, 6), // 4READ dummy=6
|
|
};
|
|
|
|
const flash_cmd_t cmds_quad_32b_mx[CMD_COUNT] = {
|
|
// cmd cmd i_lines a_lines a_size d_lines dummy
|
|
[CMD_WRSR] = CMD_DEF(0x01, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_1, 0),
|
|
[CMD_RDSR] = CMD_DEF(0x05, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_1, 0),
|
|
[CMD_RDCR] = CMD_DEF(0x15, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_1, 0),
|
|
[CMD_WREN] = CMD_DEF(0x06, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_0, 0),
|
|
[CMD_RDID] = CMD_DEF(0x9F, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_1, 0),
|
|
[CMD_RSTEN] = CMD_DEF(0x66, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_0, 0),
|
|
[CMD_RST] = CMD_DEF(0x99, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_0, 0),
|
|
[CMD_CE] = CMD_DEF(0x60, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_0, 0), // CE Chip Erase
|
|
[CMD_ERASE1] = CMD_DEF(0x21, LINES_1, LINES_1, ADDR_SIZE_32B, LINES_0, 0), // SE Sector Erase
|
|
[CMD_ERASE2] = CMD_DEF(0x5C, LINES_1, LINES_1, ADDR_SIZE_32B, LINES_0, 0), // BE32K Block Erase 32K
|
|
[CMD_ERASE3] = CMD_DEF(0xDC, LINES_1, LINES_1, ADDR_SIZE_32B, LINES_0, 0), // BE Block Erase 64K
|
|
[CMD_ERASE4] = { },
|
|
[CMD_PP] = CMD_DEF(0x3E, LINES_1, LINES_4, ADDR_SIZE_32B, LINES_4, 0), // 4PP4B
|
|
[CMD_READ] = CMD_DEF(0xEC, LINES_1, LINES_4, ADDR_SIZE_32B, LINES_4, 6), // 4READ4B dummy=6
|
|
};
|
|
|
|
const flash_cmd_t cmds_quad_32b_mx54[CMD_COUNT] = {
|
|
// cmd cmd i_lines a_lines a_size d_lines dummy
|
|
[CMD_WRSR] = CMD_DEF(0x01, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_1, 0),
|
|
[CMD_RDSR] = CMD_DEF(0x05, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_1, 0),
|
|
[CMD_RDCR] = CMD_DEF(0x15, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_1, 0),
|
|
[CMD_WREN] = CMD_DEF(0x06, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_0, 0),
|
|
[CMD_RDID] = CMD_DEF(0x9F, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_1, 0),
|
|
[CMD_RSTEN] = CMD_DEF(0x66, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_0, 0),
|
|
[CMD_RST] = CMD_DEF(0x99, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_0, 0),
|
|
[CMD_CE] = CMD_DEF(0x60, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_0, 0), // CE Chip Erase
|
|
[CMD_ERASE1] = CMD_DEF(0x20, LINES_1, LINES_1, ADDR_SIZE_32B, LINES_0, 0), // SE Sector Erase
|
|
[CMD_ERASE2] = CMD_DEF(0x52, LINES_1, LINES_1, ADDR_SIZE_32B, LINES_0, 0), // BE32K Block Erase 32K
|
|
[CMD_ERASE3] = CMD_DEF(0xD8, LINES_1, LINES_1, ADDR_SIZE_32B, LINES_0, 0), // BE Block Erase 64K
|
|
[CMD_ERASE4] = { },
|
|
[CMD_PP] = CMD_DEF(0x38, LINES_1, LINES_4, ADDR_SIZE_32B, LINES_4, 0), // 4PP
|
|
[CMD_READ] = CMD_DEF(0xEB, LINES_1, LINES_4, ADDR_SIZE_32B, LINES_4, 10), // 4READ dummy=10
|
|
};
|
|
|
|
const flash_cmd_t cmds_quad_32b_s[CMD_COUNT] = {
|
|
// cmd cmd i_lines a_lines a_size d_lines dummy
|
|
[CMD_WRSR] = CMD_DEF(0x01, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_1, 0),
|
|
[CMD_RDSR] = CMD_DEF(0x05, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_1, 0),
|
|
[CMD_RDCR] = CMD_DEF(0x35, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_1, 0),
|
|
[CMD_RDAR] = CMD_DEF(0x65, LINES_1, LINES_1, ADDR_SIZE_24B, LINES_1, 8),
|
|
[CMD_WREN] = CMD_DEF(0x06, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_0, 0),
|
|
[CMD_RDID] = CMD_DEF(0x9F, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_1, 0),
|
|
[CMD_RSTEN] = CMD_DEF(0x66, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_0, 0),
|
|
[CMD_RST] = CMD_DEF(0x99, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_0, 0),
|
|
[CMD_CE] = CMD_DEF(0x60, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_0, 0), // BE Bulk Erase
|
|
[CMD_ERASE1] = CMD_DEF(0xDC, LINES_1, LINES_1, ADDR_SIZE_32B, LINES_0, 0), // 4SE Sector Erase 256K
|
|
[CMD_ERASE2] = { },
|
|
[CMD_ERASE3] = { },
|
|
[CMD_ERASE4] = { },
|
|
[CMD_PP] = CMD_DEF(0x12, LINES_1, LINES_1, ADDR_SIZE_32B, LINES_1, 0), // 4PP (no 4PP4B)
|
|
[CMD_READ] = CMD_DEF(0xEC, LINES_1, LINES_4, ADDR_SIZE_32B, LINES_4, 2 + 8), // 4QIOR
|
|
};
|
|
|
|
const flash_cmd_t cmds_quad_24b_issi[CMD_COUNT] = {
|
|
// cmd cmd i_lines a_lines a_size d_lines dummy
|
|
[CMD_WRSR] = CMD_DEF(0x01, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_1, 0),
|
|
[CMD_RDSR] = CMD_DEF(0x05, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_1, 0),
|
|
[CMD_RDCR] = CMD_DEF(0x15, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_1, 0),
|
|
[CMD_WREN] = CMD_DEF(0x06, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_0, 0),
|
|
[CMD_RDID] = CMD_DEF(0x9F, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_1, 0),
|
|
[CMD_RSTEN] = CMD_DEF(0x66, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_0, 0),
|
|
[CMD_RST] = CMD_DEF(0x99, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_0, 0),
|
|
[CMD_CE] = CMD_DEF(0x60, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_0, 0), // CE Chip Erase
|
|
[CMD_ERASE1] = CMD_DEF(0x20, LINES_1, LINES_1, ADDR_SIZE_24B, LINES_0, 0), // SE Sector Erase
|
|
[CMD_ERASE2] = CMD_DEF(0x52, LINES_1, LINES_1, ADDR_SIZE_24B, LINES_0, 0), // BE32K Block Erase 32K
|
|
[CMD_ERASE3] = CMD_DEF(0xD8, LINES_1, LINES_1, ADDR_SIZE_24B, LINES_0, 0), // BE Block Erase 64K
|
|
[CMD_ERASE4] = { },
|
|
[CMD_PP] = CMD_DEF(0x38, LINES_1, LINES_1, ADDR_SIZE_24B, LINES_4, 0), // PPQ
|
|
[CMD_READ] = CMD_DEF(0xEB, LINES_1, LINES_4, ADDR_SIZE_24B, LINES_4, 6), // FRQIO dummy=6
|
|
};
|
|
|
|
const flash_cmd_t cmds_quad_24b_wb[CMD_COUNT] = {
|
|
// cmd cmd i_lines a_lines a_size d_lines dummy
|
|
[CMD_WRSR] = CMD_DEF(0x01, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_1, 0),
|
|
[CMD_RDSR] = CMD_DEF(0x05, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_1, 0),
|
|
[CMD_RDCR] = CMD_DEF(0x15, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_1, 0),
|
|
[CMD_WREN] = CMD_DEF(0x06, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_0, 0),
|
|
[CMD_RDID] = CMD_DEF(0x9F, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_1, 0),
|
|
[CMD_RSTEN] = CMD_DEF(0x66, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_0, 0),
|
|
[CMD_RST] = CMD_DEF(0x99, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_0, 0),
|
|
[CMD_CE] = CMD_DEF(0x60, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_0, 0), // Chip Erase
|
|
[CMD_ERASE1] = CMD_DEF(0x20, LINES_1, LINES_1, ADDR_SIZE_24B, LINES_0, 0), // Sector Erase 4KB
|
|
[CMD_ERASE2] = CMD_DEF(0x52, LINES_1, LINES_1, ADDR_SIZE_24B, LINES_0, 0), // Block Erase 32KB
|
|
[CMD_ERASE3] = CMD_DEF(0xD8, LINES_1, LINES_1, ADDR_SIZE_24B, LINES_0, 0), // Block Erase 64KB
|
|
[CMD_ERASE4] = { },
|
|
[CMD_PP] = CMD_DEF(0x32, LINES_1, LINES_1, ADDR_SIZE_24B, LINES_4, 0), // Quad Input Page Program
|
|
[CMD_READ] = CMD_DEF(0xEB, LINES_1, LINES_4, ADDR_SIZE_24B, LINES_4, 6), // Fast Read Quad I/O
|
|
};
|
|
|
|
const flash_cmd_t cmds_quad_32b_wb[CMD_COUNT] = {
|
|
// cmd cmd i_lines a_lines a_size d_lines dummy
|
|
[CMD_WRSR] = CMD_DEF(0x01, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_1, 0),
|
|
[CMD_RDSR] = CMD_DEF(0x05, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_1, 0),
|
|
[CMD_RDCR] = CMD_DEF(0x15, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_1, 0),
|
|
[CMD_WREN] = CMD_DEF(0x06, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_0, 0),
|
|
[CMD_RDID] = CMD_DEF(0x9F, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_1, 0),
|
|
[CMD_RSTEN] = CMD_DEF(0x66, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_0, 0),
|
|
[CMD_RST] = CMD_DEF(0x99, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_0, 0),
|
|
[CMD_CE] = CMD_DEF(0x60, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_0, 0), // Chip Erase
|
|
[CMD_ERASE1] = CMD_DEF(0x21, LINES_1, LINES_1, ADDR_SIZE_32B, LINES_0, 0), // Sector Erase 4KB with 4-Byte Address
|
|
[CMD_ERASE2] = CMD_DEF(0xDC, LINES_1, LINES_1, ADDR_SIZE_32B, LINES_0, 0), // Block Erase 64KB with 4-Byte Address
|
|
[CMD_ERASE3] = { },
|
|
[CMD_ERASE4] = { },
|
|
[CMD_PP] = CMD_DEF(0x34, LINES_1, LINES_1, ADDR_SIZE_32B, LINES_4, 0), // Quad Page Program with 4-Byte Address
|
|
[CMD_READ] = CMD_DEF(0xEC, LINES_1, LINES_4, ADDR_SIZE_32B, LINES_4, 6), // Fast Read Quad I/O with 4-Byte Address
|
|
};
|
|
|
|
static void init_spansion(void);
|
|
static void init_mx_issi(void);
|
|
static void init_winbond(void);
|
|
|
|
const flash_config_t config_spi_24b = FLASH_CONFIG_DEF(cmds_spi_24b, 0x01000, 0x8000, 0x10000, 0, false, NULL);
|
|
const flash_config_t config_quad_24b_mx = FLASH_CONFIG_DEF(cmds_quad_24b_mx, 0x01000, 0x8000, 0x10000, 0, true, init_mx_issi);
|
|
const flash_config_t config_quad_32b_mx = FLASH_CONFIG_DEF(cmds_quad_32b_mx, 0x01000, 0x8000, 0x10000, 0, true, init_mx_issi);
|
|
const flash_config_t config_quad_32b_mx54 = FLASH_CONFIG_DEF(cmds_quad_32b_mx54, 0x01000, 0x8000, 0x10000, 0, true, init_mx_issi);
|
|
const flash_config_t config_quad_32b_s = FLASH_CONFIG_DEF(cmds_quad_32b_s, 0x40000, 0, 0, 0, true, init_spansion);
|
|
const flash_config_t config_quad_24b_issi = FLASH_CONFIG_DEF(cmds_quad_24b_issi, 0x01000, 0x8000, 0x10000, 0, true, init_mx_issi);
|
|
const flash_config_t config_quad_24b_wb = FLASH_CONFIG_DEF(cmds_quad_24b_wb, 0x01000, 0x8000, 0x10000, 0, true, init_winbond);
|
|
const flash_config_t config_quad_32b_wb = FLASH_CONFIG_DEF(cmds_quad_32b_wb, 0x01000, 0x10000, 0, 0, true, init_winbond);
|
|
|
|
const jedec_config_t jedec_map[] = {
|
|
#if (EXTFLASH_FORCE_SPI == 0)
|
|
// MX 24 bit address
|
|
JEDEC_CONFIG_DEF(0xC2, 0x25, 0x34, "MX25U8035F", &config_quad_24b_mx, 1 << 20), // Stock 1MB (Mario)
|
|
JEDEC_CONFIG_DEF(0xC2, 0x25, 0x36, "MX25U3232F", &config_quad_24b_mx, 4 << 20), // Stock 4MB (Zelda)
|
|
JEDEC_CONFIG_DEF(0xC2, 0x25, 0x37, "MX25U6432F", &config_quad_24b_mx, 8 << 20), // 8MB
|
|
JEDEC_CONFIG_DEF(0xC2, 0x25, 0x38, "MX25U1283xF", &config_quad_24b_mx, 16 << 20), // 16MB MX25U12832F, MX25U12835F
|
|
JEDEC_CONFIG_DEF(0xC2, 0x20, 0x18, "MX25L12873", &config_quad_24b_mx, 16 << 20), // 16 MB, 3.3v
|
|
|
|
// MX 32 bit address
|
|
JEDEC_CONFIG_DEF(0xC2, 0x25, 0x39, "MX25U25635F", &config_quad_32b_mx, 32 << 20), // 32 MB
|
|
JEDEC_CONFIG_DEF(0xC2, 0x25, 0x3A, "MX25U51245G", &config_quad_32b_mx, 64 << 20), // 64 MB
|
|
JEDEC_CONFIG_DEF(0xC2, 0x24, 0x3A, "MX25U51245G", &config_quad_32b_mx, 64 << 20), // 64 MB variant of unknown cause supplied by DigiKey (not listed in datasheet)
|
|
JEDEC_CONFIG_DEF(0xC2, 0x20, 0x1A, "MX25L51245G", &config_quad_32b_mx, 64 << 20), // 64 MB, 3.3v
|
|
JEDEC_CONFIG_DEF(0xC2, 0x95, 0x3A, "MX25U51245G-54", &config_quad_32b_mx54, 64 << 20), // 64 MB
|
|
JEDEC_CONFIG_DEF(0xC2, 0x25, 0x3B, "MX66U1G45G", &config_quad_32b_mx, 128 << 20), // 128 MB
|
|
JEDEC_CONFIG_DEF(0xC2, 0x25, 0x3C, "MX66U2G45G", &config_quad_32b_mx, 256 << 20), // 256 MB
|
|
|
|
// Cypress/Infineon 32 bit address
|
|
// These chips only have 64kB erase size which won't work well with the rest of the code.
|
|
JEDEC_CONFIG_DEF(0x01, 0x02, 0x20, "S25FS512S", &config_quad_32b_s, 64 << 20), // 64 MB
|
|
JEDEC_CONFIG_DEF(0x34, 0x2B, 0x1A, "S25FS512S", &config_quad_32b_s, 64 << 20), // 64 MB
|
|
|
|
// ISSI 24 bit
|
|
JEDEC_CONFIG_DEF(0x9D, 0x70, 0x18, "IS25WP128F", &config_quad_24b_issi, 16 << 20), // 16MB
|
|
|
|
// Winbond 24 bit address
|
|
JEDEC_CONFIG_DEF(0xEF, 0x60, 0x18, "W25Q128JW-Q/N", &config_quad_24b_wb, 16 << 20), // 16MB, tested with W25Q128JWSIQ
|
|
JEDEC_CONFIG_DEF(0xEF, 0x80, 0x18, "W25Q128JW-M", &config_quad_24b_wb, 16 << 20), // 16MB
|
|
|
|
// Winbond 32 bit address
|
|
JEDEC_CONFIG_DEF(0xEF, 0x60, 0x20, "W25Q512NW-Q/N", &config_quad_32b_wb, 64 << 20), // 64MB
|
|
JEDEC_CONFIG_DEF(0xEF, 0x80, 0x20, "W25Q512NW-M", &config_quad_32b_wb, 64 << 20), // 64MB, tested with W25Q512NWEIM
|
|
#endif
|
|
};
|
|
|
|
// Driver struct
|
|
static struct {
|
|
OSPI_HandleTypeDef *hospi;
|
|
jedec_id_t jedec_id;
|
|
const flash_config_t *config;
|
|
const char *name;
|
|
uint32_t size;
|
|
bool mem_mapped_enabled;
|
|
} flash = {
|
|
.config = &config_spi_24b, // Default config to use to probe status etc.
|
|
.name = "Unknown",
|
|
};
|
|
|
|
static void set_ospi_cmd(OSPI_RegularCmdTypeDef *ospi_cmd,
|
|
const flash_cmd_t *cmd,
|
|
uint32_t address,
|
|
uint8_t *data,
|
|
size_t len)
|
|
{
|
|
memset(ospi_cmd, 0x0, sizeof(*ospi_cmd));
|
|
|
|
ospi_cmd->OperationType = HAL_OSPI_OPTYPE_COMMON_CFG;
|
|
ospi_cmd->FlashId = 0;
|
|
ospi_cmd->Instruction = cmd->cmd;
|
|
ospi_cmd->InstructionSize = HAL_OSPI_INSTRUCTION_8_BITS;
|
|
ospi_cmd->InstructionMode = instruction_line_map[cmd->instr_lines];
|
|
|
|
ospi_cmd->AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE;
|
|
ospi_cmd->DummyCycles = cmd->dummy;
|
|
ospi_cmd->DQSMode = HAL_OSPI_DQS_DISABLE;
|
|
ospi_cmd->SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD;
|
|
ospi_cmd->InstructionDtrMode = HAL_OSPI_INSTRUCTION_DTR_DISABLE;
|
|
|
|
ospi_cmd->Address = address;
|
|
ospi_cmd->AddressSize = address_size_map[cmd->addr_size];
|
|
ospi_cmd->AddressMode = address_line_map[cmd->addr_lines];
|
|
|
|
ospi_cmd->NbData = len;
|
|
ospi_cmd->DataMode = data_line_map[cmd->data_lines];
|
|
}
|
|
|
|
static void OSPI_ReadBytes(const flash_cmd_t *cmd,
|
|
uint32_t address,
|
|
uint8_t *data,
|
|
size_t len)
|
|
{
|
|
OSPI_RegularCmdTypeDef ospi_cmd;
|
|
|
|
// DBG("RB %d 0x%08x 0x%08X %d\n", cmd->cmd, address, data, len);
|
|
|
|
assert(flash.mem_mapped_enabled == false);
|
|
|
|
set_ospi_cmd(&ospi_cmd,
|
|
cmd,
|
|
address,
|
|
(uint8_t *) data,
|
|
len);
|
|
|
|
wdog_refresh();
|
|
|
|
HAL_StatusTypeDef res;
|
|
res = HAL_OSPI_Command(flash.hospi, &ospi_cmd, HAL_OSPI_TIMEOUT_DEFAULT_VALUE);
|
|
if (res != HAL_OK) {
|
|
Error_Handler();
|
|
}
|
|
|
|
if (HAL_OSPI_Receive(flash.hospi, data, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
|
|
Error_Handler();
|
|
}
|
|
}
|
|
|
|
static void OSPI_WriteBytes(const flash_cmd_t *cmd,
|
|
uint32_t address,
|
|
const uint8_t *data,
|
|
size_t len)
|
|
{
|
|
OSPI_RegularCmdTypeDef ospi_cmd;
|
|
|
|
assert(flash.mem_mapped_enabled == false);
|
|
|
|
set_ospi_cmd(&ospi_cmd,
|
|
cmd,
|
|
address,
|
|
(uint8_t *) data,
|
|
len);
|
|
|
|
wdog_refresh();
|
|
|
|
if (HAL_OSPI_Command(flash.hospi, &ospi_cmd, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
|
|
Error_Handler();
|
|
}
|
|
|
|
if (len > 0) {
|
|
if (HAL_OSPI_Transmit(flash.hospi, (uint8_t *) data, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
|
|
Error_Handler();
|
|
}
|
|
}
|
|
}
|
|
|
|
static uint8_t get_status(uint8_t mask){
|
|
uint8_t status;
|
|
OSPI_ReadBytes(CMD(RDSR), 0, &status, 1);
|
|
return status & mask;
|
|
}
|
|
|
|
static void wait_for_status(uint8_t mask, uint8_t value, uint32_t timeout)
|
|
{
|
|
uint8_t status;
|
|
|
|
uint32_t t0 = HAL_GetTick();
|
|
|
|
do {
|
|
status = get_status(mask);
|
|
wdog_refresh();
|
|
|
|
if ((timeout > 0) && (HAL_GetTick() > t0 + timeout)) {
|
|
assert(!"Status poll timeout!");
|
|
break;
|
|
}
|
|
} while (status != value);
|
|
}
|
|
|
|
bool OSPI_ChipIdle(void){ // Returns True once chip is ready for another cmd.
|
|
uint8_t status;
|
|
status = get_status(STATUS_WIP_Msk);
|
|
return status == 0;
|
|
}
|
|
|
|
void OSPI_EnableMemoryMappedMode(void)
|
|
{
|
|
OSPI_MemoryMappedTypeDef sMemMappedCfg;
|
|
OSPI_RegularCmdTypeDef ospi_cmd;
|
|
const flash_cmd_t *cmd = CMD(READ);
|
|
|
|
if(flash.mem_mapped_enabled){
|
|
return;
|
|
}
|
|
|
|
set_ospi_cmd(&ospi_cmd, cmd, 0, NULL, 0);
|
|
|
|
// Memory-mapped mode configuration for linear burst read operations
|
|
ospi_cmd.OperationType = HAL_OSPI_OPTYPE_READ_CFG;
|
|
if (HAL_OSPI_Command(flash.hospi, &ospi_cmd, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
|
|
Error_Handler();
|
|
}
|
|
|
|
// Use read instruction for write (in order to not alter the flash by accident)
|
|
ospi_cmd.OperationType = HAL_OSPI_OPTYPE_WRITE_CFG;
|
|
if (HAL_OSPI_Command(flash.hospi, &ospi_cmd, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) {
|
|
Error_Handler();
|
|
}
|
|
|
|
// Disable timeout counter for memory mapped mode
|
|
sMemMappedCfg.TimeOutActivation = HAL_OSPI_TIMEOUT_COUNTER_DISABLE;
|
|
sMemMappedCfg.TimeOutPeriod = 0;
|
|
|
|
// Enable memory mapped mode
|
|
if (HAL_OSPI_MemoryMapped(flash.hospi, &sMemMappedCfg) != HAL_OK) {
|
|
Error_Handler();
|
|
}
|
|
|
|
flash.mem_mapped_enabled = true;
|
|
}
|
|
|
|
void OSPI_DisableMemoryMappedMode(void)
|
|
{
|
|
if(flash.mem_mapped_enabled){
|
|
HAL_OSPI_Abort(flash.hospi);
|
|
flash.mem_mapped_enabled = false;
|
|
}
|
|
|
|
// This will *ONLY* work if you absolutely don't
|
|
// look at the memory mapped address.
|
|
// See here:
|
|
// https://community.st.com/s/question/0D50X00009XkaJuSAJ/stm32f7-qspi-exit-memory-mapped-mode
|
|
// Even having a debugger open at 0x9000_0000 will break this.
|
|
}
|
|
|
|
static void _OSPI_Erase(const flash_cmd_t *cmd, uint32_t address)
|
|
{
|
|
OSPI_WriteBytes(cmd, address, NULL, 0);
|
|
}
|
|
|
|
void OSPI_ChipErase(bool blocking)
|
|
{
|
|
OSPI_NOR_WriteEnable();
|
|
_OSPI_Erase(CMD(CE), 0); // Chip Erase
|
|
if(blocking){
|
|
wait_for_status(STATUS_WIP_Msk, 0, 0);
|
|
}
|
|
}
|
|
|
|
bool OSPI_Erase(uint32_t *address, uint32_t *size, bool blocking)
|
|
{
|
|
// Performs one erase command per call with the largest size possible.
|
|
// Sets *address and *size to values that should be passed to
|
|
// OSPI_Erase in the next iteration.
|
|
// Returns true when done.
|
|
|
|
assert(address != NULL);
|
|
assert(size != NULL);
|
|
|
|
if(blocking){
|
|
// wait until a previous command is no longer in progress
|
|
wait_for_status(STATUS_WIP_Msk, 0, 0);
|
|
}
|
|
else if(get_status(STATUS_WIP_Msk)){
|
|
// A write is in progress, no action to be performed right now
|
|
return false;
|
|
}
|
|
|
|
if(*size == 0)
|
|
return true;
|
|
|
|
uint32_t req_address = *address;
|
|
uint32_t req_size = *size;
|
|
|
|
DBG("E 0x%lx %ld\n", req_address, req_size);
|
|
|
|
// Assumes that erase sizes are sorted: 4 > 3 > 2 > 1.
|
|
// Assumes that erase sizes are powers of two.
|
|
|
|
const flash_cmd_t * erase_cmd[] = {
|
|
CMD(ERASE1),
|
|
CMD(ERASE2),
|
|
CMD(ERASE3),
|
|
CMD(ERASE4),
|
|
};
|
|
|
|
for (int i = 3; i >= 0; i--) {
|
|
uint32_t erase_size = flash.config->erase_sizes[i];
|
|
|
|
if (erase_size == 0) {
|
|
continue;
|
|
}
|
|
|
|
if ((req_size >= erase_size) && ((req_address & (erase_size - 1)) == 0)) {
|
|
*size = req_size - erase_size;
|
|
*address = req_address + erase_size;
|
|
|
|
DBG("Erasing block (%ld): 0x%08lx (%ld left)\n", erase_size, req_address, *size);
|
|
|
|
OSPI_NOR_WriteEnable();
|
|
_OSPI_Erase(erase_cmd[i], req_address);
|
|
if(blocking){
|
|
wait_for_status(STATUS_WIP_Msk, 0, 0);
|
|
}
|
|
|
|
return (*size == 0);
|
|
}
|
|
}
|
|
|
|
DBG("No suitable erase command found for addr=%08lx size=%ld!\n", *address, *size);
|
|
|
|
assert(!"Unsupported erase operation!");
|
|
|
|
return false;
|
|
}
|
|
|
|
void OSPI_EraseSync(uint32_t address, uint32_t size)
|
|
{
|
|
bool ret;
|
|
|
|
do {
|
|
ret = OSPI_Erase(&address, &size, true);
|
|
} while (ret == false);
|
|
}
|
|
|
|
void OSPI_PageProgram(uint32_t address,
|
|
const uint8_t *buffer,
|
|
size_t buffer_size)
|
|
{
|
|
assert(buffer_size <= 256);
|
|
|
|
DBG("PP cmd=%02X addr=0x%lx buf=%p len=%d\n", (*CMD(PP)).cmd, address, buffer, buffer_size);
|
|
|
|
OSPI_WriteBytes(CMD(PP), address, buffer, buffer_size);
|
|
|
|
// Wait for Write In Progress Bit to become zero
|
|
wait_for_status(STATUS_WIP_Msk, 0, TMO_DEFAULT);
|
|
}
|
|
|
|
void OSPI_NOR_WriteEnable(void)
|
|
{
|
|
OSPI_WriteBytes(CMD(WREN), 0, NULL, 0);
|
|
|
|
// Wait for Write Enable Latch to be set
|
|
wait_for_status(STATUS_WEL_Msk, STATUS_WEL_Msk, TMO_DEFAULT);
|
|
}
|
|
|
|
void OSPI_Program(uint32_t address,
|
|
const uint8_t *buffer,
|
|
size_t buffer_size)
|
|
{
|
|
unsigned iterations = (buffer_size + 255) / 256;
|
|
unsigned dest_page = address / 256;
|
|
|
|
assert((address & 0xff) == 0);
|
|
|
|
for (int i = 0; i < iterations; i++) {
|
|
OSPI_NOR_WriteEnable();
|
|
OSPI_PageProgram((i + dest_page) * 256,
|
|
buffer + (i * 256),
|
|
buffer_size > 256 ? 256 : buffer_size);
|
|
buffer_size -= 256;
|
|
}
|
|
}
|
|
|
|
void OSPI_ReadJedecId(uint8_t dest[3])
|
|
{
|
|
OSPI_ReadBytes(CMD(RDID), 0, dest, 3);
|
|
}
|
|
|
|
void OSPI_ReadSR(uint8_t dest[1])
|
|
{
|
|
OSPI_ReadBytes(CMD(RDSR), 0, dest, 1);
|
|
}
|
|
|
|
void OSPI_ReadCR(uint8_t dest[1])
|
|
{
|
|
OSPI_ReadBytes(CMD(RDCR), 0, dest, 1);
|
|
}
|
|
|
|
static void init_mx_issi(void)
|
|
{
|
|
// Shared code for both MX and ISSI
|
|
|
|
uint8_t rd_status;
|
|
|
|
DBG("%s\n", __FUNCTION__);
|
|
|
|
OSPI_ReadBytes(CMD(RDSR), 0, &rd_status, 1);
|
|
|
|
if (flash.config->set_quad && ((rd_status & STATUS_QE_Msk) == 0)) {
|
|
// WRSR - Write Status Register
|
|
// Set Quad Enable bit (6) in status register. Other bits = 0.
|
|
uint8_t wr_status = STATUS_QE_Msk;
|
|
|
|
DBG("Setting QE bit.\n");
|
|
|
|
// Set the QE bit
|
|
OSPI_NOR_WriteEnable();
|
|
OSPI_WriteBytes(CMD(WRSR), 0, &wr_status, 1);
|
|
wait_for_status(STATUS_WIP_Msk, 0, TMO_DEFAULT);
|
|
|
|
OSPI_ReadBytes(CMD(RDSR), 0, &rd_status, 1);
|
|
DBG("QE bit set. Status: %02X\n", rd_status);
|
|
}
|
|
}
|
|
|
|
static void init_spansion(void)
|
|
{
|
|
uint8_t rd_sr1;
|
|
uint8_t rd_sr2;
|
|
uint8_t rd_cr1;
|
|
uint8_t rd_cr2;
|
|
uint8_t rd_cr3;
|
|
uint8_t rd_cr4;
|
|
|
|
// SR[1-2]V
|
|
OSPI_ReadBytes(CMD(RDSR), 0x00, &rd_sr1, 1);
|
|
OSPI_ReadBytes(CMD(RDAR), 0x00800001, &rd_sr2, 1);
|
|
|
|
// CR[1-4]NV
|
|
OSPI_ReadBytes(CMD(RDCR), 0x00, &rd_cr1, 1);
|
|
OSPI_ReadBytes(CMD(RDAR), 0x03, &rd_cr2, 1);
|
|
OSPI_ReadBytes(CMD(RDAR), 0x04, &rd_cr3, 1);
|
|
OSPI_ReadBytes(CMD(RDAR), 0x05, &rd_cr4, 1);
|
|
|
|
DBG("SR1: %02X SR2: %02X CR: %02X %02X %02X %02X\n", rd_sr1, rd_sr2, rd_cr1, rd_cr2, rd_cr3, rd_cr4);
|
|
|
|
if (flash.config->set_quad && ((rd_cr1 & S_CR_QUAD_Msk) == 0)) {
|
|
// WRSR/WRR writes to {status, config}
|
|
// Clear SR1V and set bit 1 (QUAD) in CR1NV
|
|
uint8_t wr_sr[] = {0x00, S_CR_QUAD_Msk};
|
|
|
|
DBG("Setting QUAD in CR1V.\n");
|
|
|
|
// Enable write to be allowed to change the registers
|
|
OSPI_NOR_WriteEnable();
|
|
|
|
OSPI_WriteBytes(CMD(WRSR), 0, wr_sr, sizeof(wr_sr));
|
|
|
|
// Wait until WIP bit is cleared
|
|
wait_for_status(STATUS_WIP_Msk, 0, TMO_DEFAULT);
|
|
|
|
OSPI_ReadBytes(CMD(RDSR), 0, &rd_sr1, 1);
|
|
OSPI_ReadBytes(CMD(RDCR), 0, &rd_cr1, 1);
|
|
DBG("QUAD bit set. SR: %02X CR: %02X\n", rd_sr1, rd_cr1);
|
|
}
|
|
}
|
|
|
|
static void init_winbond(void)
|
|
{
|
|
// cmd i_lines a_lines a_size d_lines dummy
|
|
const flash_cmd_t cmd_rdsr2 = CMD_DEF(0x35, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_1, 0);
|
|
const flash_cmd_t cmd_rdsr3 = CMD_DEF(0x15, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_1, 0);
|
|
const flash_cmd_t cmd_wrsr2 = CMD_DEF(0x31, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_1, 0);
|
|
const flash_cmd_t cmd_wrsr3 = CMD_DEF(0x11, LINES_1, LINES_0, ADDR_SIZE_24B, LINES_1, 0);
|
|
|
|
const bool is_quad = flash.config->set_quad;
|
|
|
|
uint8_t sr1, sr2, sr3;
|
|
|
|
OSPI_ReadBytes(CMD(RDSR), 0, &sr1, 1);
|
|
OSPI_ReadBytes(&cmd_rdsr2, 0, &sr2, 1);
|
|
OSPI_ReadBytes(&cmd_rdsr3, 0, &sr3, 1);
|
|
DBG("Winbond SR1: %02X SR2: %02X SR3: %02X\n", sr1, sr2, sr3);
|
|
|
|
// try to clear writeable protect bits if set
|
|
|
|
if (sr1 & WB_SR1_PROTECT_Msk) {
|
|
DBG("clearing SR1 protect bits\n");
|
|
sr1 = 0;
|
|
|
|
OSPI_NOR_WriteEnable();
|
|
OSPI_WriteBytes(CMD(WRSR), 0, &sr1, 1);
|
|
wait_for_status(STATUS_WIP_Msk, 0, TMO_DEFAULT);
|
|
|
|
OSPI_ReadBytes(CMD(RDSR), 0, &sr1, 1);
|
|
if (sr1 & WB_SR1_PROTECT_Msk)
|
|
DBG("SR1: %02X, change failed\n", sr1);
|
|
}
|
|
|
|
if ((sr2 & WB_SR2_PROTECT_Msk) || (is_quad && !(sr2 & 1<<WB_SR2_QE_Pos))) {
|
|
DBG("clearing SR2 protect bits\n");
|
|
sr2 = 0;
|
|
|
|
if (is_quad) {
|
|
DBG("and enabling quad mode\n");
|
|
sr2 = 1<<WB_SR2_QE_Pos;
|
|
}
|
|
|
|
OSPI_NOR_WriteEnable();
|
|
OSPI_WriteBytes(&cmd_wrsr2, 0, &sr2, 1);
|
|
wait_for_status(STATUS_WIP_Msk, 0, TMO_DEFAULT);
|
|
|
|
OSPI_ReadBytes(&cmd_rdsr2, 0, &sr2, 1);
|
|
if ((sr2 & WB_SR2_PROTECT_Msk) || (is_quad && !(sr2 & 1<<WB_SR2_QE_Pos)))
|
|
DBG("SR2: %02X, change failed\n", sr2);
|
|
}
|
|
|
|
if ((sr3 & WB_SR3_PROTECT_Msk) || ((sr3 & WB_SR3_DRV_Msk) != WB_SR3_DRV_Val_50)) {
|
|
DBG("clearing SR3 protect bits, setting drive strength 50%%\n");
|
|
sr3 = WB_SR3_DRV_Val_50;
|
|
|
|
OSPI_NOR_WriteEnable();
|
|
OSPI_WriteBytes(&cmd_wrsr3, 0, &sr3, 1);
|
|
wait_for_status(STATUS_WIP_Msk, 0, TMO_DEFAULT);
|
|
|
|
OSPI_ReadBytes(&cmd_rdsr3, 0, &sr3, 1);
|
|
if ((sr3 & WB_SR3_PROTECT_Msk) || ((sr3 & WB_SR3_DRV_Msk) != WB_SR3_DRV_Val_50))
|
|
DBG("SR3: %02X, change failed\n", sr3);
|
|
}
|
|
|
|
if (is_quad && !(sr2 & 1<<WB_SR2_QE_Pos)) {
|
|
DBG("Windbond quad mode not enabled, falling back to SPI\n");
|
|
flash.config = &config_spi_24b;
|
|
flash.name = "Winbond SPI";
|
|
}
|
|
}
|
|
|
|
const char* OSPI_GetFlashName(void)
|
|
{
|
|
return flash.name;
|
|
}
|
|
|
|
uint32_t OSPI_GetSmallestEraseSize(void)
|
|
{
|
|
// Assumes that erase sizes are sorted: 4 > 3 > 2 > 1.
|
|
return flash.config->erase_sizes[0];
|
|
}
|
|
|
|
int OSPI_Init(OSPI_HandleTypeDef *hospi)
|
|
{
|
|
uint8_t status;
|
|
|
|
flash.hospi = hospi;
|
|
|
|
// Enable Reset
|
|
OSPI_WriteBytes(CMD(RSTEN), 0, NULL, 0);
|
|
HAL_Delay(2);
|
|
|
|
// Reset
|
|
OSPI_WriteBytes(CMD(RST), 0, NULL, 0);
|
|
HAL_Delay(20);
|
|
|
|
// Read ID
|
|
OSPI_ReadBytes(CMD(RDID), 0, &flash.jedec_id.u8[0], 3);
|
|
DBG("JEDEC_ID: %02X %02X %02X\n", flash.jedec_id.u8[0], flash.jedec_id.u8[1], flash.jedec_id.u8[2]);
|
|
|
|
// Check for known bad IDs
|
|
if (((flash.jedec_id.u32 & 0xffffff) == 0xffffff) ||
|
|
((flash.jedec_id.u32 & 0xffffff) == 0x000000)) {
|
|
// Can't communicate with the external flash! Please check the soldering.
|
|
return -1;
|
|
}
|
|
|
|
OSPI_ReadBytes(CMD(RDSR), 0, &status, 1);
|
|
DBG("Status: %02X\n", status);
|
|
|
|
for (int i = 0; i < ARRAY_SIZE(jedec_map); i++) {
|
|
if ((flash.jedec_id.u32 & 0xffffff) == (jedec_map[i].jedec_id.u32 & 0xffffff)) {
|
|
flash.config = jedec_map[i].config;
|
|
flash.name = jedec_map[i].name;
|
|
flash.size = jedec_map[i].size;
|
|
DBG("Found config: %s\n", flash.name);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (flash.config->init_fn) {
|
|
flash.config->init_fn();
|
|
}
|
|
|
|
OSPI_EnableMemoryMappedMode();
|
|
|
|
return 0;
|
|
}
|
|
|
|
uint32_t OSPI_GetSize(void){
|
|
return flash.size;
|
|
}
|