drivers, stm32: Support SPI/QSPI flash chips over 16MB.
With a SPI flash that has more than 16MB, 32-bit addressing is required rather than the standard 24-bit. This commit adds support for 32-bit addressing so that the SPI flash commands (read/write/erase) are selected automatically depending on the size of the address being used at each operation.
This commit is contained in:
parent
b72cb0ca1b
commit
30501d3f54
@ -28,6 +28,8 @@
|
|||||||
|
|
||||||
#include "py/mphal.h"
|
#include "py/mphal.h"
|
||||||
|
|
||||||
|
#define MP_SPI_ADDR_IS_32B(addr) (addr & 0xff000000)
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
MP_QSPI_IOCTL_INIT,
|
MP_QSPI_IOCTL_INIT,
|
||||||
MP_QSPI_IOCTL_DEINIT,
|
MP_QSPI_IOCTL_DEINIT,
|
||||||
@ -54,4 +56,19 @@ typedef struct _mp_soft_qspi_obj_t {
|
|||||||
|
|
||||||
extern const mp_qspi_proto_t mp_soft_qspi_proto;
|
extern const mp_qspi_proto_t mp_soft_qspi_proto;
|
||||||
|
|
||||||
|
static inline uint8_t mp_spi_set_addr_buff(uint8_t *buf, uint32_t addr) {
|
||||||
|
if (MP_SPI_ADDR_IS_32B(addr)) {
|
||||||
|
buf[0] = addr >> 24;
|
||||||
|
buf[1] = addr >> 16;
|
||||||
|
buf[2] = addr >> 8;
|
||||||
|
buf[3] = addr;
|
||||||
|
return 4;
|
||||||
|
} else {
|
||||||
|
buf[0] = addr >> 16;
|
||||||
|
buf[1] = addr >> 8;
|
||||||
|
buf[2] = addr;
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#endif // MICROPY_INCLUDED_DRIVERS_BUS_QSPI_H
|
#endif // MICROPY_INCLUDED_DRIVERS_BUS_QSPI_H
|
||||||
|
@ -168,9 +168,10 @@ STATIC void mp_soft_qspi_write_cmd_data(void *self_in, uint8_t cmd, size_t len,
|
|||||||
|
|
||||||
STATIC void mp_soft_qspi_write_cmd_addr_data(void *self_in, uint8_t cmd, uint32_t addr, size_t len, const uint8_t *src) {
|
STATIC void mp_soft_qspi_write_cmd_addr_data(void *self_in, uint8_t cmd, uint32_t addr, size_t len, const uint8_t *src) {
|
||||||
mp_soft_qspi_obj_t *self = (mp_soft_qspi_obj_t*)self_in;
|
mp_soft_qspi_obj_t *self = (mp_soft_qspi_obj_t*)self_in;
|
||||||
uint8_t cmd_buf[4] = {cmd, addr >> 16, addr >> 8, addr};
|
uint8_t cmd_buf[5] = {cmd};
|
||||||
|
uint8_t addr_len = mp_spi_set_addr_buff(&cmd_buf[1], addr);
|
||||||
CS_LOW(self);
|
CS_LOW(self);
|
||||||
mp_soft_qspi_transfer(self, 4, cmd_buf, NULL);
|
mp_soft_qspi_transfer(self, addr_len + 1, cmd_buf, NULL);
|
||||||
mp_soft_qspi_transfer(self, len, src, NULL);
|
mp_soft_qspi_transfer(self, len, src, NULL);
|
||||||
CS_HIGH(self);
|
CS_HIGH(self);
|
||||||
}
|
}
|
||||||
@ -186,10 +187,11 @@ STATIC uint32_t mp_soft_qspi_read_cmd(void *self_in, uint8_t cmd, size_t len) {
|
|||||||
|
|
||||||
STATIC void mp_soft_qspi_read_cmd_qaddr_qdata(void *self_in, uint8_t cmd, uint32_t addr, size_t len, uint8_t *dest) {
|
STATIC void mp_soft_qspi_read_cmd_qaddr_qdata(void *self_in, uint8_t cmd, uint32_t addr, size_t len, uint8_t *dest) {
|
||||||
mp_soft_qspi_obj_t *self = (mp_soft_qspi_obj_t*)self_in;
|
mp_soft_qspi_obj_t *self = (mp_soft_qspi_obj_t*)self_in;
|
||||||
uint8_t cmd_buf[7] = {cmd, addr >> 16, addr >> 8, addr};
|
uint8_t cmd_buf[7] = {cmd};
|
||||||
|
uint8_t addr_len = mp_spi_set_addr_buff(&cmd_buf[1], addr);
|
||||||
CS_LOW(self);
|
CS_LOW(self);
|
||||||
mp_soft_qspi_transfer(self, 1, cmd_buf, NULL);
|
mp_soft_qspi_transfer(self, 1, cmd_buf, NULL);
|
||||||
mp_soft_qspi_qwrite(self, 6, &cmd_buf[1]); // 3 addr bytes, 1 extra byte (0), 2 dummy bytes (4 dummy cycles)
|
mp_soft_qspi_qwrite(self, addr_len + 3, &cmd_buf[1]); // 3/4 addr bytes, 1 extra byte (0), 2 dummy bytes (4 dummy cycles)
|
||||||
mp_soft_qspi_qread(self, len, dest);
|
mp_soft_qspi_qread(self, len, dest);
|
||||||
CS_HIGH(self);
|
CS_HIGH(self);
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,12 @@
|
|||||||
#define CMD_CHIP_ERASE (0xc7)
|
#define CMD_CHIP_ERASE (0xc7)
|
||||||
#define CMD_C4READ (0xeb)
|
#define CMD_C4READ (0xeb)
|
||||||
|
|
||||||
|
// 32 bit addressing commands
|
||||||
|
#define CMD_WRITE_32 (0x12)
|
||||||
|
#define CMD_READ_32 (0x13)
|
||||||
|
#define CMD_SEC_ERASE_32 (0x21)
|
||||||
|
#define CMD_C4READ_32 (0xec)
|
||||||
|
|
||||||
#define WAIT_SR_TIMEOUT (1000000)
|
#define WAIT_SR_TIMEOUT (1000000)
|
||||||
|
|
||||||
#define PAGE_SIZE (256) // maximum bytes we can write in one SPI transfer
|
#define PAGE_SIZE (256) // maximum bytes we can write in one SPI transfer
|
||||||
@ -76,20 +82,28 @@ STATIC void mp_spiflash_write_cmd_data(mp_spiflash_t *self, uint8_t cmd, size_t
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
STATIC void mp_spiflash_write_cmd_addr_data(mp_spiflash_t *self, uint8_t cmd, uint32_t addr, size_t len, const uint8_t *src) {
|
STATIC void mp_spiflash_transfer_cmd_addr_data(mp_spiflash_t *self, uint8_t cmd, uint32_t addr, size_t len, const uint8_t *src, uint8_t *dest) {
|
||||||
const mp_spiflash_config_t *c = self->config;
|
const mp_spiflash_config_t *c = self->config;
|
||||||
if (c->bus_kind == MP_SPIFLASH_BUS_SPI) {
|
if (c->bus_kind == MP_SPIFLASH_BUS_SPI) {
|
||||||
uint8_t buf[4] = {cmd, addr >> 16, addr >> 8, addr};
|
uint8_t buf[5] = {cmd, 0};
|
||||||
|
uint8_t buff_len = 1 + mp_spi_set_addr_buff(&buf[1], addr);
|
||||||
mp_hal_pin_write(c->bus.u_spi.cs, 0);
|
mp_hal_pin_write(c->bus.u_spi.cs, 0);
|
||||||
c->bus.u_spi.proto->transfer(c->bus.u_spi.data, 4, buf, NULL);
|
c->bus.u_spi.proto->transfer(c->bus.u_spi.data, buff_len, buf, NULL);
|
||||||
if (len) {
|
if (len && (src != NULL)) {
|
||||||
c->bus.u_spi.proto->transfer(c->bus.u_spi.data, len, src, NULL);
|
c->bus.u_spi.proto->transfer(c->bus.u_spi.data, len, src, NULL);
|
||||||
|
} else if (len && (dest != NULL)) {
|
||||||
|
c->bus.u_spi.proto->transfer(c->bus.u_spi.data, len, dest, dest);
|
||||||
}
|
}
|
||||||
|
|
||||||
mp_hal_pin_write(c->bus.u_spi.cs, 1);
|
mp_hal_pin_write(c->bus.u_spi.cs, 1);
|
||||||
|
} else {
|
||||||
|
if (dest != NULL) {
|
||||||
|
c->bus.u_qspi.proto->read_cmd_qaddr_qdata(c->bus.u_qspi.data, cmd, addr, len, dest);
|
||||||
} else {
|
} else {
|
||||||
c->bus.u_qspi.proto->write_cmd_addr_data(c->bus.u_qspi.data, cmd, addr, len, src);
|
c->bus.u_qspi.proto->write_cmd_addr_data(c->bus.u_qspi.data, cmd, addr, len, src);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
STATIC uint32_t mp_spiflash_read_cmd(mp_spiflash_t *self, uint8_t cmd, size_t len) {
|
STATIC uint32_t mp_spiflash_read_cmd(mp_spiflash_t *self, uint8_t cmd, size_t len) {
|
||||||
const mp_spiflash_config_t *c = self->config;
|
const mp_spiflash_config_t *c = self->config;
|
||||||
@ -107,25 +121,19 @@ STATIC uint32_t mp_spiflash_read_cmd(mp_spiflash_t *self, uint8_t cmd, size_t le
|
|||||||
|
|
||||||
STATIC void mp_spiflash_read_data(mp_spiflash_t *self, uint32_t addr, size_t len, uint8_t *dest) {
|
STATIC void mp_spiflash_read_data(mp_spiflash_t *self, uint32_t addr, size_t len, uint8_t *dest) {
|
||||||
const mp_spiflash_config_t *c = self->config;
|
const mp_spiflash_config_t *c = self->config;
|
||||||
|
uint8_t cmd;
|
||||||
if (c->bus_kind == MP_SPIFLASH_BUS_SPI) {
|
if (c->bus_kind == MP_SPIFLASH_BUS_SPI) {
|
||||||
uint8_t buf[4] = {CMD_READ, addr >> 16, addr >> 8, addr};
|
cmd = MP_SPI_ADDR_IS_32B(addr) ? CMD_READ_32 : CMD_READ;
|
||||||
mp_hal_pin_write(c->bus.u_spi.cs, 0);
|
|
||||||
c->bus.u_spi.proto->transfer(c->bus.u_spi.data, 4, buf, NULL);
|
|
||||||
c->bus.u_spi.proto->transfer(c->bus.u_spi.data, len, dest, dest);
|
|
||||||
mp_hal_pin_write(c->bus.u_spi.cs, 1);
|
|
||||||
} else {
|
} else {
|
||||||
c->bus.u_qspi.proto->read_cmd_qaddr_qdata(c->bus.u_qspi.data, CMD_C4READ, addr, len, dest);
|
cmd = MP_SPI_ADDR_IS_32B(addr) ? CMD_C4READ_32 : CMD_C4READ;
|
||||||
}
|
}
|
||||||
|
mp_spiflash_transfer_cmd_addr_data(self, cmd, addr, len, NULL, dest);
|
||||||
}
|
}
|
||||||
|
|
||||||
STATIC void mp_spiflash_write_cmd(mp_spiflash_t *self, uint8_t cmd) {
|
STATIC void mp_spiflash_write_cmd(mp_spiflash_t *self, uint8_t cmd) {
|
||||||
mp_spiflash_write_cmd_data(self, cmd, 0, 0);
|
mp_spiflash_write_cmd_data(self, cmd, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
STATIC void mp_spiflash_write_cmd_addr(mp_spiflash_t *self, uint8_t cmd, uint32_t addr) {
|
|
||||||
mp_spiflash_write_cmd_addr_data(self, cmd, addr, 0, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
STATIC int mp_spiflash_wait_sr(mp_spiflash_t *self, uint8_t mask, uint8_t val, uint32_t timeout) {
|
STATIC int mp_spiflash_wait_sr(mp_spiflash_t *self, uint8_t mask, uint8_t val, uint32_t timeout) {
|
||||||
uint8_t sr;
|
uint8_t sr;
|
||||||
do {
|
do {
|
||||||
@ -210,7 +218,8 @@ STATIC int mp_spiflash_erase_block_internal(mp_spiflash_t *self, uint32_t addr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// erase the sector
|
// erase the sector
|
||||||
mp_spiflash_write_cmd_addr(self, CMD_SEC_ERASE, addr);
|
uint8_t cmd = MP_SPI_ADDR_IS_32B(addr) ? CMD_SEC_ERASE_32 : CMD_SEC_ERASE;
|
||||||
|
mp_spiflash_transfer_cmd_addr_data(self, cmd, addr, 0, NULL, NULL);
|
||||||
|
|
||||||
// wait WIP=0
|
// wait WIP=0
|
||||||
return mp_spiflash_wait_wip0(self);
|
return mp_spiflash_wait_wip0(self);
|
||||||
@ -227,7 +236,8 @@ STATIC int mp_spiflash_write_page(mp_spiflash_t *self, uint32_t addr, size_t len
|
|||||||
}
|
}
|
||||||
|
|
||||||
// write the page
|
// write the page
|
||||||
mp_spiflash_write_cmd_addr_data(self, CMD_WRITE, addr, len, src);
|
uint8_t cmd = MP_SPI_ADDR_IS_32B(addr) ? CMD_WRITE_32 : CMD_WRITE;
|
||||||
|
mp_spiflash_transfer_cmd_addr_data(self, cmd, addr, len, src, NULL);
|
||||||
|
|
||||||
// wait WIP=0
|
// wait WIP=0
|
||||||
return mp_spiflash_wait_wip0(self);
|
return mp_spiflash_wait_wip0(self);
|
||||||
|
@ -52,6 +52,14 @@
|
|||||||
#define MICROPY_HW_QSPI_CS_HIGH_CYCLES 2 // nCS stays high for 2 cycles
|
#define MICROPY_HW_QSPI_CS_HIGH_CYCLES 2 // nCS stays high for 2 cycles
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if (MICROPY_HW_QSPIFLASH_SIZE_BITS_LOG2 - 3 - 1) >= 24
|
||||||
|
#define QSPI_CMD 0xec
|
||||||
|
#define QSPI_ADSIZE 3
|
||||||
|
#else
|
||||||
|
#define QSPI_CMD 0xeb
|
||||||
|
#define QSPI_ADSIZE 2
|
||||||
|
#endif
|
||||||
|
|
||||||
static inline void qspi_mpu_disable_all(void) {
|
static inline void qspi_mpu_disable_all(void) {
|
||||||
// Configure MPU to disable access to entire QSPI region, to prevent CPU
|
// Configure MPU to disable access to entire QSPI region, to prevent CPU
|
||||||
// speculative execution from accessing this region and modifying QSPI registers.
|
// speculative execution from accessing this region and modifying QSPI registers.
|
||||||
@ -116,6 +124,7 @@ void qspi_memory_map(void) {
|
|||||||
// Enable memory-mapped mode
|
// Enable memory-mapped mode
|
||||||
|
|
||||||
QUADSPI->ABR = 0; // disable continuous read mode
|
QUADSPI->ABR = 0; // disable continuous read mode
|
||||||
|
|
||||||
QUADSPI->CCR =
|
QUADSPI->CCR =
|
||||||
0 << QUADSPI_CCR_DDRM_Pos // DDR mode disabled
|
0 << QUADSPI_CCR_DDRM_Pos // DDR mode disabled
|
||||||
| 0 << QUADSPI_CCR_SIOO_Pos // send instruction every transaction
|
| 0 << QUADSPI_CCR_SIOO_Pos // send instruction every transaction
|
||||||
@ -124,10 +133,10 @@ void qspi_memory_map(void) {
|
|||||||
| 4 << QUADSPI_CCR_DCYC_Pos // 4 dummy cycles
|
| 4 << QUADSPI_CCR_DCYC_Pos // 4 dummy cycles
|
||||||
| 0 << QUADSPI_CCR_ABSIZE_Pos // 8-bit alternate byte
|
| 0 << QUADSPI_CCR_ABSIZE_Pos // 8-bit alternate byte
|
||||||
| 3 << QUADSPI_CCR_ABMODE_Pos // alternate byte on 4 lines
|
| 3 << QUADSPI_CCR_ABMODE_Pos // alternate byte on 4 lines
|
||||||
| 2 << QUADSPI_CCR_ADSIZE_Pos // 24-bit address size
|
| QSPI_ADSIZE << QUADSPI_CCR_ADSIZE_Pos
|
||||||
| 3 << QUADSPI_CCR_ADMODE_Pos // address on 4 lines
|
| 3 << QUADSPI_CCR_ADMODE_Pos // address on 4 lines
|
||||||
| 1 << QUADSPI_CCR_IMODE_Pos // instruction on 1 line
|
| 1 << QUADSPI_CCR_IMODE_Pos // instruction on 1 line
|
||||||
| 0xeb << QUADSPI_CCR_INSTRUCTION_Pos // quad read opcode
|
| QSPI_CMD << QUADSPI_CCR_INSTRUCTION_Pos
|
||||||
;
|
;
|
||||||
|
|
||||||
qspi_mpu_enable_mapped();
|
qspi_mpu_enable_mapped();
|
||||||
@ -203,6 +212,8 @@ STATIC void qspi_write_cmd_data(void *self_in, uint8_t cmd, size_t len, uint32_t
|
|||||||
STATIC void qspi_write_cmd_addr_data(void *self_in, uint8_t cmd, uint32_t addr, size_t len, const uint8_t *src) {
|
STATIC void qspi_write_cmd_addr_data(void *self_in, uint8_t cmd, uint32_t addr, size_t len, const uint8_t *src) {
|
||||||
(void)self_in;
|
(void)self_in;
|
||||||
|
|
||||||
|
uint8_t adsize = MP_SPI_ADDR_IS_32B(addr) ? 3 : 2;
|
||||||
|
|
||||||
QUADSPI->FCR = QUADSPI_FCR_CTCF; // clear TC flag
|
QUADSPI->FCR = QUADSPI_FCR_CTCF; // clear TC flag
|
||||||
|
|
||||||
if (len == 0) {
|
if (len == 0) {
|
||||||
@ -213,7 +224,7 @@ STATIC void qspi_write_cmd_addr_data(void *self_in, uint8_t cmd, uint32_t addr,
|
|||||||
| 0 << QUADSPI_CCR_DMODE_Pos // no data
|
| 0 << QUADSPI_CCR_DMODE_Pos // no data
|
||||||
| 0 << QUADSPI_CCR_DCYC_Pos // 0 dummy cycles
|
| 0 << QUADSPI_CCR_DCYC_Pos // 0 dummy cycles
|
||||||
| 0 << QUADSPI_CCR_ABMODE_Pos // no alternate byte
|
| 0 << QUADSPI_CCR_ABMODE_Pos // no alternate byte
|
||||||
| 2 << QUADSPI_CCR_ADSIZE_Pos // 24-bit address size
|
| adsize << QUADSPI_CCR_ADSIZE_Pos // 32/24-bit address size
|
||||||
| 1 << QUADSPI_CCR_ADMODE_Pos // address on 1 line
|
| 1 << QUADSPI_CCR_ADMODE_Pos // address on 1 line
|
||||||
| 1 << QUADSPI_CCR_IMODE_Pos // instruction on 1 line
|
| 1 << QUADSPI_CCR_IMODE_Pos // instruction on 1 line
|
||||||
| cmd << QUADSPI_CCR_INSTRUCTION_Pos // write opcode
|
| cmd << QUADSPI_CCR_INSTRUCTION_Pos // write opcode
|
||||||
@ -230,7 +241,7 @@ STATIC void qspi_write_cmd_addr_data(void *self_in, uint8_t cmd, uint32_t addr,
|
|||||||
| 1 << QUADSPI_CCR_DMODE_Pos // data on 1 line
|
| 1 << QUADSPI_CCR_DMODE_Pos // data on 1 line
|
||||||
| 0 << QUADSPI_CCR_DCYC_Pos // 0 dummy cycles
|
| 0 << QUADSPI_CCR_DCYC_Pos // 0 dummy cycles
|
||||||
| 0 << QUADSPI_CCR_ABMODE_Pos // no alternate byte
|
| 0 << QUADSPI_CCR_ABMODE_Pos // no alternate byte
|
||||||
| 2 << QUADSPI_CCR_ADSIZE_Pos // 24-bit address size
|
| adsize << QUADSPI_CCR_ADSIZE_Pos // 32/24-bit address size
|
||||||
| 1 << QUADSPI_CCR_ADMODE_Pos // address on 1 line
|
| 1 << QUADSPI_CCR_ADMODE_Pos // address on 1 line
|
||||||
| 1 << QUADSPI_CCR_IMODE_Pos // instruction on 1 line
|
| 1 << QUADSPI_CCR_IMODE_Pos // instruction on 1 line
|
||||||
| cmd << QUADSPI_CCR_INSTRUCTION_Pos // write opcode
|
| cmd << QUADSPI_CCR_INSTRUCTION_Pos // write opcode
|
||||||
@ -285,6 +296,9 @@ STATIC uint32_t qspi_read_cmd(void *self_in, uint8_t cmd, size_t len) {
|
|||||||
|
|
||||||
STATIC void qspi_read_cmd_qaddr_qdata(void *self_in, uint8_t cmd, uint32_t addr, size_t len, uint8_t *dest) {
|
STATIC void qspi_read_cmd_qaddr_qdata(void *self_in, uint8_t cmd, uint32_t addr, size_t len, uint8_t *dest) {
|
||||||
(void)self_in;
|
(void)self_in;
|
||||||
|
|
||||||
|
uint8_t adsize = MP_SPI_ADDR_IS_32B(addr) ? 3 : 2;
|
||||||
|
|
||||||
QUADSPI->FCR = QUADSPI_FCR_CTCF; // clear TC flag
|
QUADSPI->FCR = QUADSPI_FCR_CTCF; // clear TC flag
|
||||||
|
|
||||||
QUADSPI->DLR = len - 1; // number of bytes to read
|
QUADSPI->DLR = len - 1; // number of bytes to read
|
||||||
@ -297,7 +311,7 @@ STATIC void qspi_read_cmd_qaddr_qdata(void *self_in, uint8_t cmd, uint32_t addr,
|
|||||||
| 4 << QUADSPI_CCR_DCYC_Pos // 4 dummy cycles
|
| 4 << QUADSPI_CCR_DCYC_Pos // 4 dummy cycles
|
||||||
| 0 << QUADSPI_CCR_ABSIZE_Pos // 8-bit alternate byte
|
| 0 << QUADSPI_CCR_ABSIZE_Pos // 8-bit alternate byte
|
||||||
| 3 << QUADSPI_CCR_ABMODE_Pos // alternate byte on 4 lines
|
| 3 << QUADSPI_CCR_ABMODE_Pos // alternate byte on 4 lines
|
||||||
| 2 << QUADSPI_CCR_ADSIZE_Pos // 24-bit address size
|
| adsize << QUADSPI_CCR_ADSIZE_Pos // 32 or 24-bit address size
|
||||||
| 3 << QUADSPI_CCR_ADMODE_Pos // address on 4 lines
|
| 3 << QUADSPI_CCR_ADMODE_Pos // address on 4 lines
|
||||||
| 1 << QUADSPI_CCR_IMODE_Pos // instruction on 1 line
|
| 1 << QUADSPI_CCR_IMODE_Pos // instruction on 1 line
|
||||||
| cmd << QUADSPI_CCR_INSTRUCTION_Pos // quad read opcode
|
| cmd << QUADSPI_CCR_INSTRUCTION_Pos // quad read opcode
|
||||||
|
Loading…
Reference in New Issue
Block a user