From 36bf7e9263ee1d9815952ccd083501206ac79cb9 Mon Sep 17 00:00:00 2001 From: Andrew Leech <> Date: Fri, 26 Jan 2024 15:31:19 +1100 Subject: [PATCH] stm32/qspi: Use the flash size configured in spiflash init. Signed-off-by: Andrew Leech --- drivers/bus/qspi.h | 3 +- drivers/bus/softqspi.c | 2 +- drivers/memory/spiflash.c | 10 +- ports/stm32/boards/STM32F769DISC/board_init.c | 3 +- ports/stm32/octospi.c | 2 +- ports/stm32/qspi.c | 177 +++++++++++++----- ports/stm32/qspi.h | 2 +- 7 files changed, 144 insertions(+), 55 deletions(-) diff --git a/drivers/bus/qspi.h b/drivers/bus/qspi.h index 669ff544a4..160cc63d4a 100644 --- a/drivers/bus/qspi.h +++ b/drivers/bus/qspi.h @@ -37,6 +37,7 @@ enum { MP_QSPI_IOCTL_DEINIT, MP_QSPI_IOCTL_BUS_ACQUIRE, MP_QSPI_IOCTL_BUS_RELEASE, + MP_QSPI_IOCTL_FLASH_SIZE, }; enum qspi_tranfer_mode { @@ -45,7 +46,7 @@ enum qspi_tranfer_mode { }; typedef struct _mp_qspi_proto_t { - int (*ioctl)(void *self, uint32_t cmd); + int (*ioctl)(void *self, uint32_t cmd, uint32_t arg); int (*write_cmd_data)(void *self, uint8_t cmd, size_t len, uint32_t data); int (*write_cmd_addr_data)(void *self, uint8_t cmd, uint32_t addr, size_t len, const uint8_t *src); int (*read_cmd)(void *self, uint8_t cmd, size_t len, uint32_t *dest); diff --git a/drivers/bus/softqspi.c b/drivers/bus/softqspi.c index 0fe1a16d90..d5eaf5e6a1 100644 --- a/drivers/bus/softqspi.c +++ b/drivers/bus/softqspi.c @@ -56,7 +56,7 @@ static void nibble_write(mp_soft_qspi_obj_t *self, uint8_t v) { mp_hal_pin_write(self->io3, (v >> 3) & 1); } -static int mp_soft_qspi_ioctl(void *self_in, uint32_t cmd) { +static int mp_soft_qspi_ioctl(void *self_in, uint32_t cmd, uint32_t arg) { mp_soft_qspi_obj_t *self = (mp_soft_qspi_obj_t*)self_in; switch (cmd) { diff --git a/drivers/memory/spiflash.c b/drivers/memory/spiflash.c index 9ac35cd35c..49647e2dbb 100644 --- a/drivers/memory/spiflash.c +++ b/drivers/memory/spiflash.c @@ -80,14 +80,14 @@ static external_flash_device generic_config = GENERIC; static void mp_spiflash_acquire_bus(mp_spiflash_t *self) { const mp_spiflash_config_t *c = self->config; if (c->bus_kind == MP_SPIFLASH_BUS_QSPI) { - c->bus.u_qspi.proto->ioctl(c->bus.u_qspi.data, MP_QSPI_IOCTL_BUS_ACQUIRE); + c->bus.u_qspi.proto->ioctl(c->bus.u_qspi.data, MP_QSPI_IOCTL_BUS_ACQUIRE, 0); } } static void mp_spiflash_release_bus(mp_spiflash_t *self) { const mp_spiflash_config_t *c = self->config; if (c->bus_kind == MP_SPIFLASH_BUS_QSPI) { - c->bus.u_qspi.proto->ioctl(c->bus.u_qspi.data, MP_QSPI_IOCTL_BUS_RELEASE); + c->bus.u_qspi.proto->ioctl(c->bus.u_qspi.data, MP_QSPI_IOCTL_BUS_RELEASE, 0); } } @@ -198,7 +198,7 @@ int mp_spiflash_init(mp_spiflash_t *self) { mp_hal_pin_output(self->config->bus.u_spi.cs); self->config->bus.u_spi.proto->ioctl(self->config->bus.u_spi.data, MP_SPI_IOCTL_INIT); } else { - self->config->bus.u_qspi.proto->ioctl(self->config->bus.u_qspi.data, MP_QSPI_IOCTL_INIT); + self->config->bus.u_qspi.proto->ioctl(self->config->bus.u_qspi.data, MP_QSPI_IOCTL_INIT, 0); } mp_spiflash_acquire_bus(self); @@ -327,6 +327,10 @@ int mp_spiflash_init(mp_spiflash_t *self) { } } + if (self->config->bus_kind == MP_SPIFLASH_BUS_QSPI) { + self->config->bus.u_qspi.proto->ioctl(self->config->bus.u_qspi.data, MP_QSPI_IOCTL_FLASH_SIZE, self->device->total_size); + } + mp_spiflash_release_bus(self); return ret; } diff --git a/ports/stm32/boards/STM32F769DISC/board_init.c b/ports/stm32/boards/STM32F769DISC/board_init.c index 578dc9adb6..2b639f3123 100644 --- a/ports/stm32/boards/STM32F769DISC/board_init.c +++ b/ports/stm32/boards/STM32F769DISC/board_init.c @@ -1,5 +1,6 @@ #include "storage.h" #include "qspi.h" +#include "mpconfigboard.h" // This configuration is needed for mboot to be able to write to the external QSPI flash @@ -21,6 +22,6 @@ spi_bdev_t spi_bdev; // This init function is needed to memory map the QSPI flash early in the boot process void board_early_init(void) { - qspi_init(); + qspi_init(MICROPY_HW_BDEV_SPIFLASH_SIZE_BYTES); qspi_memory_map(); } diff --git a/ports/stm32/octospi.c b/ports/stm32/octospi.c index bf5ab7d09f..feabe905d7 100644 --- a/ports/stm32/octospi.c +++ b/ports/stm32/octospi.c @@ -92,7 +92,7 @@ void octospi_init(void) { OCTOSPI1->CR |= OCTOSPI_CR_EN; } -static int octospi_ioctl(void *self_in, uint32_t cmd) { +static int octospi_ioctl(void *self_in, uint32_t cmd, uint32_t arg) { (void)self_in; switch (cmd) { case MP_QSPI_IOCTL_INIT: diff --git a/ports/stm32/qspi.c b/ports/stm32/qspi.c index dbce690453..54342eab09 100644 --- a/ports/stm32/qspi.c +++ b/ports/stm32/qspi.c @@ -32,7 +32,7 @@ #include "qspi.h" #include "pin_static_af.h" -#if defined(MICROPY_HW_QSPIFLASH_SIZE_BITS_LOG2) +#if MICROPY_HW_ENABLE_QSPI || defined(MICROPY_HW_QSPIFLASH_SIZE_BITS_LOG2) #define QSPI_MAP_ADDR (0x90000000) @@ -52,17 +52,18 @@ #define MICROPY_HW_QSPI_CS_HIGH_CYCLES 2 // nCS stays high for 2 cycles #endif -#ifndef MICROPY_HW_QSPI_MPU_REGION_SIZE -#define MICROPY_HW_QSPI_MPU_REGION_SIZE ((1 << (MICROPY_HW_QSPIFLASH_SIZE_BITS_LOG2 - 3)) >> 20) +#ifndef MICROPY_HW_QSPIFLASH_SIZE_BITS_LOG2 #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 +// Fast Read command in 32bit and 24bit addressing. +#define QSPI_FAST_READ_A4_CMD 0xec +#define QSPI_FAST_READ_A3_CMD 0xeb + +// this formula computes the log2 of "m" +#define BITS_TO_LOG2(m) ((m) - 1) / (((m) - 1) % 255 + 1) / 255 % 255 * 8 + 7 - 86 / (((m) - 1) % 255 + 12) + +#define MBytes (1024 * 1024) +static size_t qspi_memory_size_bytes = 0; static inline void qspi_mpu_disable_all(void) { // Configure MPU to disable access to entire QSPI region, to prevent CPU @@ -72,6 +73,8 @@ static inline void qspi_mpu_disable_all(void) { mpu_config_end(irq_state); } +#if 1 + static inline void qspi_mpu_enable_mapped(void) { // Configure MPU to allow access to only the valid part of external SPI flash. // The memory accesses to the mapped QSPI are faster if the MPU is not used @@ -83,36 +86,106 @@ static inline void qspi_mpu_enable_mapped(void) { // other enabled region overlaps the disabled subregion, and the access is // unprivileged or the background region is disabled, the MPU issues a fault. uint32_t irq_state = mpu_config_start(); - #if MICROPY_HW_QSPI_MPU_REGION_SIZE > 128 - mpu_config_region(MPU_REGION_QSPI1, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0xFF, MPU_REGION_SIZE_256MB)); - #elif MICROPY_HW_QSPI_MPU_REGION_SIZE > 64 - mpu_config_region(MPU_REGION_QSPI1, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x0F, MPU_REGION_SIZE_256MB)); - #elif MICROPY_HW_QSPI_MPU_REGION_SIZE > 32 - mpu_config_region(MPU_REGION_QSPI1, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x03, MPU_REGION_SIZE_256MB)); - #elif MICROPY_HW_QSPI_MPU_REGION_SIZE > 16 - mpu_config_region(MPU_REGION_QSPI1, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x01, MPU_REGION_SIZE_256MB)); - #elif MICROPY_HW_QSPI_MPU_REGION_SIZE > 8 - mpu_config_region(MPU_REGION_QSPI1, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x01, MPU_REGION_SIZE_256MB)); - mpu_config_region(MPU_REGION_QSPI2, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x0F, MPU_REGION_SIZE_32MB)); - #elif MICROPY_HW_QSPI_MPU_REGION_SIZE > 4 - mpu_config_region(MPU_REGION_QSPI1, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x01, MPU_REGION_SIZE_256MB)); - mpu_config_region(MPU_REGION_QSPI2, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x03, MPU_REGION_SIZE_32MB)); - #elif MICROPY_HW_QSPI_MPU_REGION_SIZE > 2 - mpu_config_region(MPU_REGION_QSPI1, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x01, MPU_REGION_SIZE_256MB)); - mpu_config_region(MPU_REGION_QSPI2, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x01, MPU_REGION_SIZE_32MB)); - #elif MICROPY_HW_QSPI_MPU_REGION_SIZE > 1 - mpu_config_region(MPU_REGION_QSPI1, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x01, MPU_REGION_SIZE_256MB)); - mpu_config_region(MPU_REGION_QSPI2, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x0F, MPU_REGION_SIZE_32MB)); - mpu_config_region(MPU_REGION_QSPI3, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x01, MPU_REGION_SIZE_16MB)); - #else - mpu_config_region(MPU_REGION_QSPI1, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x01, MPU_REGION_SIZE_256MB)); - mpu_config_region(MPU_REGION_QSPI2, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x01, MPU_REGION_SIZE_32MB)); - mpu_config_region(MPU_REGION_QSPI3, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x03, MPU_REGION_SIZE_4MB)); - #endif + + if (qspi_memory_size_bytes > (128 * MBytes)) { + mpu_config_region(MPU_REGION_QSPI1, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0xFF, MPU_REGION_SIZE_256MB)); + } else if (qspi_memory_size_bytes > (64 * MBytes)) { + mpu_config_region(MPU_REGION_QSPI1, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x0F, MPU_REGION_SIZE_256MB)); + } else if (qspi_memory_size_bytes > (32 * MBytes)) { + mpu_config_region(MPU_REGION_QSPI1, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x03, MPU_REGION_SIZE_256MB)); + } else if (qspi_memory_size_bytes > (16 * MBytes)) { + mpu_config_region(MPU_REGION_QSPI1, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x01, MPU_REGION_SIZE_256MB)); + } else if (qspi_memory_size_bytes > (8 * MBytes)) { + mpu_config_region(MPU_REGION_QSPI1, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x01, MPU_REGION_SIZE_256MB)); + mpu_config_region(MPU_REGION_QSPI2, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x0F, MPU_REGION_SIZE_32MB)); + } else if (qspi_memory_size_bytes > (4 * MBytes)) { + mpu_config_region(MPU_REGION_QSPI1, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x01, MPU_REGION_SIZE_256MB)); + mpu_config_region(MPU_REGION_QSPI2, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x03, MPU_REGION_SIZE_32MB)); + } else if (qspi_memory_size_bytes > (2 * MBytes)) { + mpu_config_region(MPU_REGION_QSPI1, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x01, MPU_REGION_SIZE_256MB)); + mpu_config_region(MPU_REGION_QSPI2, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x01, MPU_REGION_SIZE_32MB)); + } else if (qspi_memory_size_bytes > (1 * MBytes)) { + mpu_config_region(MPU_REGION_QSPI1, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x01, MPU_REGION_SIZE_256MB)); + mpu_config_region(MPU_REGION_QSPI2, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x0F, MPU_REGION_SIZE_32MB)); + mpu_config_region(MPU_REGION_QSPI3, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x01, MPU_REGION_SIZE_16MB)); + } else { + mpu_config_region(MPU_REGION_QSPI1, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x01, MPU_REGION_SIZE_256MB)); + mpu_config_region(MPU_REGION_QSPI2, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x01, MPU_REGION_SIZE_32MB)); + mpu_config_region(MPU_REGION_QSPI3, QSPI_MAP_ADDR, MPU_CONFIG_NOACCESS(0x03, MPU_REGION_SIZE_4MB)); + } mpu_config_end(irq_state); } -void qspi_init(void) { +#else + +// This variant of the function is harder to read, but 76 bytes smaller. + +static inline void qspi_mpu_enable_mapped(void) { + // Configure MPU to allow access to only the valid part of external SPI flash. + // The memory accesses to the mapped QSPI are faster if the MPU is not used + // for the memory-mapped region, so 3 MPU regions are used to disable access + // to everything except the valid address space, using holes in the bottom + // of the regions and nesting them. + // Note: Disabling a subregion (by setting its corresponding SRD bit to 1) + // means another region overlapping the disabled range matches instead. If no + // other enabled region overlaps the disabled subregion, and the access is + // unprivileged or the background region is disabled, the MPU issues a fault. + uint32_t irq_state = mpu_config_start(); + + static const uint8_t region_definitions[][7] = { + // Each row per MB region total size, specifying region srd and size for MPU_REGION_QSPI1, 2 and 3. + {128, 0xFF, MPU_REGION_SIZE_256MB, 0, 0, 0, 0}, + { 64, 0x0F, MPU_REGION_SIZE_256MB, 0, 0, 0, 0}, + { 32, 0x03, MPU_REGION_SIZE_256MB, 0, 0, 0, 0}, + { 16, 0x01, MPU_REGION_SIZE_256MB, 0, 0, 0, 0}, + { 8, 0x01, MPU_REGION_SIZE_256MB, + 0x0F, MPU_REGION_SIZE_32MB, 0, 0}, + { 4, 0x01, MPU_REGION_SIZE_256MB, + 0x03, MPU_REGION_SIZE_32MB, 0, 0}, + { 2, 0x01, MPU_REGION_SIZE_256MB, + 0x01, MPU_REGION_SIZE_32MB, 0, 0}, + { 1, 0x01, MPU_REGION_SIZE_256MB, + 0x0F, MPU_REGION_SIZE_32MB, + 0x01, MPU_REGION_SIZE_16MB}, + { 0, 0x01, MPU_REGION_SIZE_256MB, + 0x01, MPU_REGION_SIZE_32MB, + 0x03, MPU_REGION_SIZE_4MB}, + }; + size_t qspi_memory_size_mbytes = qspi_memory_size_bytes / 1024 / 1024; + + for (uint8_t i = 0; i < 9; ++i) { + if (qspi_memory_size_mbytes > region_definitions[i][0]) { + uint32_t attr_size_1 = MPU_CONFIG_NOACCESS(region_definitions[i][1], region_definitions[i][2]); + mpu_config_region(MPU_REGION_QSPI1, QSPI_MAP_ADDR, attr_size_1); + if (region_definitions[i][3] > 0) { + uint32_t attr_size_2 = MPU_CONFIG_NOACCESS(region_definitions[i][3], region_definitions[i][4]); + mpu_config_region(MPU_REGION_QSPI2, QSPI_MAP_ADDR, attr_size_2); + } + if (region_definitions[i][5] > 0) { + uint32_t attr_size_3 = MPU_CONFIG_NOACCESS(region_definitions[i][5], region_definitions[i][6]); + mpu_config_region(MPU_REGION_QSPI3, QSPI_MAP_ADDR, attr_size_3); + } + break; + } + } + mpu_config_end(irq_state); +} + +#endif + +void qspi_set_memory_size(size_t memory_size_bytes) { + qspi_memory_size_bytes = memory_size_bytes; + size_t QSPIFLASH_SIZE_BITS_LOG2 = BITS_TO_LOG2(qspi_memory_size_bytes * 8); + QUADSPI->DCR = + (QSPIFLASH_SIZE_BITS_LOG2 - 3 - 1) << QUADSPI_DCR_FSIZE_Pos + | (MICROPY_HW_QSPI_CS_HIGH_CYCLES - 1) << QUADSPI_DCR_CSHT_Pos + | 0 << QUADSPI_DCR_CKMODE_Pos // CLK idles at low state + ; +} + +void qspi_init(size_t memory_size_bytes) { + qspi_memory_size_bytes = memory_size_bytes; + qspi_mpu_disable_all(); // Configure pins @@ -143,15 +216,20 @@ void qspi_init(void) { | 1 << QUADSPI_CR_EN_Pos // enable the peripheral ; - QUADSPI->DCR = - (MICROPY_HW_QSPIFLASH_SIZE_BITS_LOG2 - 3 - 1) << QUADSPI_DCR_FSIZE_Pos - | (MICROPY_HW_QSPI_CS_HIGH_CYCLES - 1) << QUADSPI_DCR_CSHT_Pos - | 0 << QUADSPI_DCR_CKMODE_Pos // CLK idles at low state - ; + if (qspi_memory_size_bytes) { + qspi_set_memory_size(qspi_memory_size_bytes); + } } -void qspi_memory_map(void) { +void qspi_memory_map() { // Enable memory-mapped mode + uint8_t cmd = QSPI_FAST_READ_A3_CMD; + uint8_t adsize = 2; + if (qspi_memory_size_bytes > (16 * MBytes)) { + // Flash chips over 16MB require 32bit addressing. + cmd = QSPI_FAST_READ_A4_CMD; + adsize = 3; + } QUADSPI->ABR = 0; // disable continuous read mode @@ -163,20 +241,20 @@ void qspi_memory_map(void) { | 4 << QUADSPI_CCR_DCYC_Pos // 4 dummy cycles | 0 << QUADSPI_CCR_ABSIZE_Pos // 8-bit alternate byte | 3 << QUADSPI_CCR_ABMODE_Pos // alternate byte on 4 lines - | QSPI_ADSIZE << QUADSPI_CCR_ADSIZE_Pos + | adsize << QUADSPI_CCR_ADSIZE_Pos | 3 << QUADSPI_CCR_ADMODE_Pos // address on 4 lines | 1 << QUADSPI_CCR_IMODE_Pos // instruction on 1 line - | QSPI_CMD << QUADSPI_CCR_INSTRUCTION_Pos + | cmd << QUADSPI_CCR_INSTRUCTION_Pos ; qspi_mpu_enable_mapped(); } -static int qspi_ioctl(void *self_in, uint32_t cmd) { +static int qspi_ioctl(void *self_in, uint32_t cmd, uint32_t arg) { (void)self_in; switch (cmd) { case MP_QSPI_IOCTL_INIT: - qspi_init(); + qspi_init(0); break; case MP_QSPI_IOCTL_BUS_ACQUIRE: // Disable memory-mapped region during bus access @@ -192,6 +270,11 @@ static int qspi_ioctl(void *self_in, uint32_t cmd) { // Switch to memory-map mode when bus is idle qspi_memory_map(); break; + case MP_QSPI_IOCTL_FLASH_SIZE: + if (arg > 0) { + qspi_set_memory_size(arg); + } + return qspi_memory_size_bytes; } return 0; // success } diff --git a/ports/stm32/qspi.h b/ports/stm32/qspi.h index c774b12582..e1b9c8402d 100644 --- a/ports/stm32/qspi.h +++ b/ports/stm32/qspi.h @@ -30,7 +30,7 @@ extern const mp_qspi_proto_t qspi_proto; -void qspi_init(void); +void qspi_init(size_t memory_size_bytes); void qspi_memory_map(void); #endif // MICROPY_INCLUDED_STM32_QSPI_H