kopia lustrzana https://github.com/micropython/micropython
Merge 3033161a73
into 7b050b366b
commit
6644c3f2c7
|
@ -37,14 +37,20 @@ enum {
|
|||
MP_QSPI_IOCTL_DEINIT,
|
||||
MP_QSPI_IOCTL_BUS_ACQUIRE,
|
||||
MP_QSPI_IOCTL_BUS_RELEASE,
|
||||
MP_QSPI_IOCTL_FLASH_SIZE,
|
||||
};
|
||||
|
||||
enum qspi_tranfer_mode {
|
||||
MP_QSPI_TRANSFER_CMD_ADDR_DATA,
|
||||
MP_QSPI_TRANSFER_CMD_QADDR_QDATA,
|
||||
};
|
||||
|
||||
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);
|
||||
int (*read_cmd_qaddr_qdata)(void *self, uint8_t cmd, uint32_t addr, size_t len, uint8_t *dest);
|
||||
int (*read_cmd_addr_data)(void *self, uint8_t cmd, uint32_t addr, size_t len, uint8_t *dest, uint8_t mode);
|
||||
} mp_qspi_proto_t;
|
||||
|
||||
typedef struct _mp_soft_qspi_obj_t {
|
||||
|
|
|
@ -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) {
|
||||
|
@ -188,16 +188,28 @@ static int mp_soft_qspi_read_cmd(void *self_in, uint8_t cmd, size_t len, uint32_
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int mp_soft_qspi_read_cmd_qaddr_qdata(void *self_in, uint8_t cmd, uint32_t addr, size_t len, uint8_t *dest) {
|
||||
static int mp_soft_qspi_read_cmd_addr_data(void *self_in, uint8_t cmd, uint32_t addr, size_t len, uint8_t *dest, uint8_t mode) {
|
||||
int ret = 0;
|
||||
mp_soft_qspi_obj_t *self = (mp_soft_qspi_obj_t*)self_in;
|
||||
uint8_t cmd_buf[7] = {cmd};
|
||||
uint8_t addr_len = mp_spi_set_addr_buff(&cmd_buf[1], addr);
|
||||
CS_LOW(self);
|
||||
mp_soft_qspi_transfer(self, 1, cmd_buf, NULL);
|
||||
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);
|
||||
if (mode == MP_QSPI_TRANSFER_CMD_ADDR_DATA) {
|
||||
// cmd, address and data on 1 line.
|
||||
// 3 addr bytes, 1 dummy byte (8 dummy cycles x 1 line)
|
||||
mp_soft_qspi_transfer(self, addr_len + 1, &cmd_buf[1], NULL);
|
||||
mp_soft_qspi_transfer(self, len, NULL, dest);
|
||||
} else if (mode == MP_QSPI_TRANSFER_CMD_QADDR_QDATA) {
|
||||
// cmd 1 line, address and data on 4 lines.
|
||||
// 3/4 addr bytes, 1 extra byte (mode: 0), 2 dummy bytes (4 dummy cycles x 4 lines)
|
||||
mp_soft_qspi_qwrite(self, addr_len + 3, &cmd_buf[1]);
|
||||
mp_soft_qspi_qread(self, len, dest);
|
||||
} else {
|
||||
ret = -1;
|
||||
}
|
||||
CS_HIGH(self);
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
const mp_qspi_proto_t mp_soft_qspi_proto = {
|
||||
|
@ -205,5 +217,5 @@ const mp_qspi_proto_t mp_soft_qspi_proto = {
|
|||
.write_cmd_data = mp_soft_qspi_write_cmd_data,
|
||||
.write_cmd_addr_data = mp_soft_qspi_write_cmd_addr_data,
|
||||
.read_cmd = mp_soft_qspi_read_cmd,
|
||||
.read_cmd_qaddr_qdata = mp_soft_qspi_read_cmd_qaddr_qdata,
|
||||
.read_cmd_addr_data = mp_soft_qspi_read_cmd_addr_data,
|
||||
};
|
||||
|
|
|
@ -29,11 +29,16 @@
|
|||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct {
|
||||
typedef struct _external_flash_device {
|
||||
// Flash size in bytes.
|
||||
uint32_t total_size;
|
||||
|
||||
uint16_t start_up_time_us;
|
||||
|
||||
// Three response bytes to 0x9f JEDEC ID command.
|
||||
// The first field is always a manufacturer_id, however the other two are used
|
||||
// differently be each manufacturer.
|
||||
// All three together will always uniquely identify a chip model.
|
||||
uint8_t manufacturer_id;
|
||||
uint8_t memory_type;
|
||||
uint8_t capacity;
|
||||
|
@ -66,9 +71,12 @@ typedef struct {
|
|||
bool single_status_byte : 1;
|
||||
} external_flash_device;
|
||||
|
||||
// Settings for the Adesto Tech AT25DF081A 1MiB SPI flash. Its on the SAMD21
|
||||
// Xplained board.
|
||||
// typedef struct _external_flash_device ;
|
||||
|
||||
// Settings for the Adesto Tech / Renesas AT25DF081A 1MiB SPI flash.
|
||||
// Its on the SAMD21 Xplained board.
|
||||
// Datasheet: https://www.adestotech.com/wp-content/uploads/doc8715.pdf
|
||||
// https://www.renesas.com/eu/en/document/dst/at25df081a-datasheet
|
||||
#define AT25DF081A { \
|
||||
.total_size = (1 << 20), /* 1 MiB */ \
|
||||
.start_up_time_us = 10000, \
|
||||
|
@ -85,6 +93,42 @@ typedef struct {
|
|||
.single_status_byte = false, \
|
||||
}
|
||||
|
||||
// Settings for the Renesas AT25SF161B 2MiB SPI flash.
|
||||
// Datasheet: https://www.renesas.com/us/en/document/dst/at25sf161b-datasheet?r=1608796
|
||||
#define AT25SF161B { \
|
||||
.total_size = (1 << 21), /* 2 MiB */ \
|
||||
.start_up_time_us = 5000, \
|
||||
.manufacturer_id = 0x1f, \
|
||||
.memory_type = 0x86, \
|
||||
.capacity = 0x01, \
|
||||
.max_clock_speed_mhz = 133, \
|
||||
.quad_enable_bit_mask = 0x02, \
|
||||
.has_sector_protection = true, \
|
||||
.supports_fast_read = true, \
|
||||
.supports_qspi = true, \
|
||||
.supports_qspi_writes = true, \
|
||||
.write_status_register_split = true, \
|
||||
.single_status_byte = false, \
|
||||
}
|
||||
|
||||
// Settings for the Renesas AT25SF641B 8MiB SPI flash.
|
||||
// Datasheet: https://www.renesas.com/us/en/document/dst/at25sf641b-datasheet?r=1608816
|
||||
#define AT25SF641B { \
|
||||
.total_size = (1 << 23), /* 8 MiB */ \
|
||||
.start_up_time_us = 5000, \
|
||||
.manufacturer_id = 0x1f, \
|
||||
.memory_type = 0x88, \
|
||||
.capacity = 0x01, \
|
||||
.max_clock_speed_mhz = 133, \
|
||||
.quad_enable_bit_mask = 0x02, \
|
||||
.has_sector_protection = true, \
|
||||
.supports_fast_read = true, \
|
||||
.supports_qspi = true, \
|
||||
.supports_qspi_writes = true, \
|
||||
.write_status_register_split = true, \
|
||||
.single_status_byte = false, \
|
||||
}
|
||||
|
||||
// Settings for the Gigadevice GD25Q16C 2MiB SPI flash.
|
||||
// Datasheet: http://www.gigadevice.com/datasheet/gd25q16c/
|
||||
#define GD25Q16C { \
|
||||
|
@ -388,6 +432,24 @@ typedef struct {
|
|||
.single_status_byte = true, \
|
||||
}
|
||||
|
||||
// Settings for the Macronix MX25L25673G 32MiB SPI flash.
|
||||
// Datasheet: https://www.macronix.com/Lists/Datasheet/Attachments/8761/MX25L25673G,%203V,%20256Mb,%20v1.7.pdf
|
||||
#define MX25L25673G { \
|
||||
.total_size = (1 << 25), /* 32 MiB */ \
|
||||
.start_up_time_us = 5000, \
|
||||
.manufacturer_id = 0xc2, \
|
||||
.memory_type = 0x20, \
|
||||
.capacity = 0x19, \
|
||||
.max_clock_speed_mhz = 133, \
|
||||
.quad_enable_bit_mask = 0x40, \
|
||||
.has_sector_protection = false, \
|
||||
.supports_fast_read = true, \
|
||||
.supports_qspi = true, \
|
||||
.supports_qspi_writes = true, \
|
||||
.write_status_register_split = false, \
|
||||
.single_status_byte = true, \
|
||||
}
|
||||
|
||||
// Settings for the Macronix MX25R6435F 8MiB SPI flash.
|
||||
// Datasheet: http://www.macronix.com/Lists/Datasheet/Attachments/7428/MX25R6435F,%20Wide%20Range,%2064Mb,%20v1.4.pdf
|
||||
// By default its in lower power mode which can only do 8mhz. In high power mode it can do 80mhz.
|
||||
|
|
|
@ -30,17 +30,19 @@
|
|||
#include "py/mperrno.h"
|
||||
#include "py/mphal.h"
|
||||
#include "drivers/memory/spiflash.h"
|
||||
#include "drivers/memory/external_flash_device.h"
|
||||
|
||||
#define QSPI_QE_MASK (0x02)
|
||||
#define USE_WR_DELAY (1)
|
||||
|
||||
#define CMD_WRSR (0x01)
|
||||
#define CMD_WRCR (0x31)
|
||||
#define CMD_WRITE (0x02)
|
||||
#define CMD_READ (0x03)
|
||||
#define CMD_RDSR (0x05)
|
||||
#define CMD_WREN (0x06)
|
||||
#define CMD_SEC_ERASE (0x20)
|
||||
#define CMD_RDCR (0x35)
|
||||
#define CMD_RD_SFDP (0x5a)
|
||||
#define CMD_RD_DEVID (0x9f)
|
||||
#define CMD_CHIP_ERASE (0xc7)
|
||||
#define CMD_C4READ (0xeb)
|
||||
|
@ -56,17 +58,36 @@
|
|||
#define PAGE_SIZE (256) // maximum bytes we can write in one SPI transfer
|
||||
#define SECTOR_SIZE MP_SPIFLASH_ERASE_BLOCK_SIZE
|
||||
|
||||
#ifndef MICROPY_HW_SPIFLASH_DEVICES
|
||||
#define MICROPY_HW_SPIFLASH_DEVICES
|
||||
#endif
|
||||
|
||||
#if !BUILDING_MBOOT
|
||||
#define diag_printf(...) mp_printf(MICROPY_ERROR_PRINTER, __VA_ARGS__)
|
||||
#else
|
||||
#define diag_printf(...)
|
||||
#endif
|
||||
|
||||
// List of all possible flash devices used by device.
|
||||
// MICROPY_HW_SPIFLASH_DEVICES can be set to a comma separated list in mpconfigboard.h
|
||||
static const external_flash_device possible_devices[] = {
|
||||
MICROPY_HW_SPIFLASH_DEVICES
|
||||
};
|
||||
#define EXTERNAL_FLASH_DEVICE_COUNT MP_ARRAY_SIZE(possible_devices)
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -84,14 +105,15 @@ static int mp_spiflash_write_cmd_data(mp_spiflash_t *self, uint8_t cmd, size_t l
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int 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) {
|
||||
static int 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, uint8_t mode) {
|
||||
int ret = 0;
|
||||
const mp_spiflash_config_t *c = self->config;
|
||||
if (c->bus_kind == MP_SPIFLASH_BUS_SPI) {
|
||||
uint8_t buf[5] = {cmd, 0};
|
||||
uint8_t buf[6] = {cmd, 0};
|
||||
uint8_t buff_len = 1 + mp_spi_set_addr_buff(&buf[1], addr);
|
||||
uint8_t dummy = (cmd == CMD_RD_SFDP)? 1 : 0;
|
||||
mp_hal_pin_write(c->bus.u_spi.cs, 0);
|
||||
c->bus.u_spi.proto->transfer(c->bus.u_spi.data, buff_len, buf, NULL);
|
||||
c->bus.u_spi.proto->transfer(c->bus.u_spi.data, buff_len + dummy, buf, NULL);
|
||||
if (len && (src != NULL)) {
|
||||
c->bus.u_spi.proto->transfer(c->bus.u_spi.data, len, src, NULL);
|
||||
} else if (len && (dest != NULL)) {
|
||||
|
@ -101,7 +123,7 @@ static int mp_spiflash_transfer_cmd_addr_data(mp_spiflash_t *self, uint8_t cmd,
|
|||
mp_hal_pin_write(c->bus.u_spi.cs, 1);
|
||||
} else {
|
||||
if (dest != NULL) {
|
||||
ret = c->bus.u_qspi.proto->read_cmd_qaddr_qdata(c->bus.u_qspi.data, cmd, addr, len, dest);
|
||||
ret = c->bus.u_qspi.proto->read_cmd_addr_data(c->bus.u_qspi.data, cmd, addr, len, dest, mode);
|
||||
} else {
|
||||
ret = c->bus.u_qspi.proto->write_cmd_addr_data(c->bus.u_qspi.data, cmd, addr, len, src);
|
||||
}
|
||||
|
@ -114,7 +136,7 @@ static int mp_spiflash_read_cmd(mp_spiflash_t *self, uint8_t cmd, size_t len, ui
|
|||
if (c->bus_kind == MP_SPIFLASH_BUS_SPI) {
|
||||
mp_hal_pin_write(c->bus.u_spi.cs, 0);
|
||||
c->bus.u_spi.proto->transfer(c->bus.u_spi.data, 1, &cmd, NULL);
|
||||
c->bus.u_spi.proto->transfer(c->bus.u_spi.data, len, (void*)dest, (void*)dest);
|
||||
c->bus.u_spi.proto->transfer(c->bus.u_spi.data, len, (void *)dest, (void *)dest);
|
||||
mp_hal_pin_write(c->bus.u_spi.cs, 1);
|
||||
return 0;
|
||||
} else {
|
||||
|
@ -125,12 +147,15 @@ static int mp_spiflash_read_cmd(mp_spiflash_t *self, uint8_t cmd, size_t len, ui
|
|||
static int 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;
|
||||
uint8_t cmd;
|
||||
uint8_t mode;
|
||||
if (c->bus_kind == MP_SPIFLASH_BUS_SPI) {
|
||||
cmd = MICROPY_HW_SPI_ADDR_IS_32BIT(addr) ? CMD_READ_32 : CMD_READ;
|
||||
mode = MP_QSPI_TRANSFER_CMD_ADDR_DATA;
|
||||
} else {
|
||||
cmd = MICROPY_HW_SPI_ADDR_IS_32BIT(addr) ? CMD_C4READ_32 : CMD_C4READ;
|
||||
mode = MP_QSPI_TRANSFER_CMD_QADDR_QDATA;
|
||||
}
|
||||
return mp_spiflash_transfer_cmd_addr_data(self, cmd, addr, len, NULL, dest);
|
||||
return mp_spiflash_transfer_cmd_addr_data(self, cmd, addr, len, NULL, dest, mode);
|
||||
}
|
||||
|
||||
static int mp_spiflash_write_cmd(mp_spiflash_t *self, uint8_t cmd) {
|
||||
|
@ -164,7 +189,8 @@ static inline void mp_spiflash_deepsleep_internal(mp_spiflash_t *self, int value
|
|||
mp_spiflash_write_cmd(self, value ? 0xb9 : 0xab); // sleep/wake
|
||||
}
|
||||
|
||||
void mp_spiflash_init(mp_spiflash_t *self) {
|
||||
int mp_spiflash_init(mp_spiflash_t *self) {
|
||||
int ret = 0;
|
||||
self->flags = 0;
|
||||
|
||||
if (self->config->bus_kind == MP_SPIFLASH_BUS_SPI) {
|
||||
|
@ -172,7 +198,7 @@ void 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);
|
||||
|
@ -183,30 +209,130 @@ void mp_spiflash_init(mp_spiflash_t *self) {
|
|||
#if defined(CHECK_DEVID)
|
||||
// Validate device id
|
||||
uint32_t devid;
|
||||
int ret = mp_spiflash_read_cmd(self, CMD_RD_DEVID, 3, &devid);
|
||||
ret = mp_spiflash_read_cmd(self, CMD_RD_DEVID, 3, &devid);
|
||||
if (ret != 0 || devid != CHECK_DEVID) {
|
||||
mp_spiflash_release_bus(self);
|
||||
return;
|
||||
return -2;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (self->config->bus_kind == MP_SPIFLASH_BUS_QSPI) {
|
||||
// Set QE bit
|
||||
uint32_t sr = 0, cr = 0;
|
||||
int ret = mp_spiflash_read_cmd(self, CMD_RDSR, 1, &sr);
|
||||
if (ret == 0) {
|
||||
ret = mp_spiflash_read_cmd(self, CMD_RDCR, 1, &cr);
|
||||
// Start with generic configuration, update with exact if found.
|
||||
self->device = &generic_config;
|
||||
|
||||
uint8_t jedec_ids[3];
|
||||
ret = mp_spiflash_read_cmd(self, CMD_RD_DEVID, 3, (uint32_t *)jedec_ids);
|
||||
if (ret != 0) {
|
||||
mp_spiflash_release_bus(self);
|
||||
return -2;
|
||||
}
|
||||
for (uint8_t i = 0; i < EXTERNAL_FLASH_DEVICE_COUNT; i++) {
|
||||
const external_flash_device *possible_device = &possible_devices[i];
|
||||
if (jedec_ids[0] == possible_device->manufacturer_id &&
|
||||
jedec_ids[1] == possible_device->memory_type &&
|
||||
jedec_ids[2] == possible_device->capacity) {
|
||||
self->device = possible_device;
|
||||
break;
|
||||
}
|
||||
uint32_t data = (sr & 0xff) | (cr & 0xff) << 8;
|
||||
if (ret == 0 && !(data & (QSPI_QE_MASK << 8))) {
|
||||
data |= QSPI_QE_MASK << 8;
|
||||
mp_spiflash_write_cmd(self, CMD_WREN);
|
||||
mp_spiflash_write_cmd_data(self, CMD_WRSR, 2, data);
|
||||
mp_spiflash_wait_wip0(self);
|
||||
}
|
||||
// If the flash device is not known, try to autodetect suitable settings.
|
||||
if (self->device == &generic_config) {
|
||||
if (jedec_ids[0] == 0xc2) { // Macronix devices
|
||||
generic_config.quad_enable_bit_mask = 0x04;
|
||||
generic_config.single_status_byte = true;
|
||||
}
|
||||
#if MICROPY_HW_BDEV_SPIFLASH_SIZE_BYTES
|
||||
generic_config.total_size = MICROPY_HW_BDEV_SPIFLASH_SIZE_BYTES;
|
||||
#else
|
||||
// Try to read "Serial Flash Discoverable Parameters"
|
||||
// JEDEC Standard No. 216, 9 x 32bit dwords of data.
|
||||
// Start be reading the headers to confirm sfdp is supported and find the parameter table address.
|
||||
uint32_t sfdp[4] = {0};
|
||||
ret = mp_spiflash_transfer_cmd_addr_data(self, CMD_RD_SFDP, 0, sizeof(sfdp), NULL, (uint8_t *)sfdp, MP_QSPI_TRANSFER_CMD_ADDR_DATA);
|
||||
const char sfdp_header[] = {'S', 'F', 'D', 'P'};
|
||||
if (ret != 0 || sfdp[0] != *(uint32_t *)sfdp_header) {
|
||||
diag_printf("mp_spiflash: sfdp not supported\n");
|
||||
diag_printf("Set MICROPY_HW_SPIFLASH_DEVICES or MICROPY_HW_BDEV_SPIFLASH_SIZE_BYTES");
|
||||
diag_printf("jedec ids: 0x%x 0x%x 0x%x\n", jedec_ids[0], jedec_ids[1], jedec_ids[2]);
|
||||
mp_spiflash_release_bus(self);
|
||||
return -2;
|
||||
} else {
|
||||
// Read the first few SFDP parameter tables.
|
||||
uint32_t sfdp_param_table_addr = sfdp[3] & 0xFFFFFF;
|
||||
ret = mp_spiflash_transfer_cmd_addr_data(self, CMD_RD_SFDP, sfdp_param_table_addr, sizeof(sfdp), NULL, (uint8_t *)sfdp, MP_QSPI_TRANSFER_CMD_ADDR_DATA);
|
||||
// Flash Memory Density
|
||||
uint32_t size = sfdp[1] & ~(1 << 31);
|
||||
if (size != 0) {
|
||||
if (sfdp[1] & (1 << 31)) {
|
||||
// When bit-31 is set to 1, the total bits is 2^size.
|
||||
generic_config.total_size = 1 << size;
|
||||
} else {
|
||||
// When bit-31 is set to 0, the total bits is size + 1.
|
||||
generic_config.total_size = (size + 1) / 8;
|
||||
}
|
||||
}
|
||||
uint8_t opcode_sec_erase = (sfdp[0] >> 8 & 0xFF);
|
||||
if (opcode_sec_erase != 0x20) {
|
||||
diag_printf("mp_spiflash_sec_erase: opcode not supported\n");
|
||||
}
|
||||
if (sfdp[0] & (1 << 21)) {
|
||||
// Supports (1-4-4) Fast Read: Device supports single line opcode,
|
||||
// quad line address, and quad output Fast Read.
|
||||
generic_config.supports_fast_read = true;
|
||||
generic_config.supports_qspi = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (self->config->bus_kind == MP_SPIFLASH_BUS_QSPI) {
|
||||
// Set quad enable (QE) bit.
|
||||
uint32_t sr = 0, cr = 0;
|
||||
ret = mp_spiflash_read_cmd(self, CMD_RDSR, 1, &sr);
|
||||
|
||||
if (ret == 0 && self->device->single_status_byte) {
|
||||
// QE bit is in status byte 1
|
||||
if ((sr & self->device->quad_enable_bit_mask) == 0) {
|
||||
mp_spiflash_write_cmd(self, CMD_WREN);
|
||||
sr |= self->device->quad_enable_bit_mask;
|
||||
mp_spiflash_write_cmd_data(self, CMD_WRSR, 1, sr);
|
||||
}
|
||||
// Verify it's written correctly
|
||||
ret = mp_spiflash_read_cmd(self, CMD_RDSR, 1, &sr);
|
||||
if (ret == 0 && (sr & self->device->quad_enable_bit_mask) == 0) {
|
||||
// QE bit could not be set
|
||||
ret = -1;
|
||||
}
|
||||
}
|
||||
if (ret == 0 && (!self->device->single_status_byte)) {
|
||||
// QE bit is in command register / status byte 2
|
||||
ret = mp_spiflash_read_cmd(self, CMD_RDCR, 1, &cr);
|
||||
if ((cr & self->device->quad_enable_bit_mask) == 0) {
|
||||
mp_spiflash_write_cmd(self, CMD_WREN);
|
||||
cr |= self->device->quad_enable_bit_mask;
|
||||
if (self->device->write_status_register_split) {
|
||||
// Some devices have a separate command to write CR
|
||||
mp_spiflash_write_cmd_data(self, CMD_WRCR, 1, cr);
|
||||
} else {
|
||||
// Other devices expect both SR and CR to be written in one operation
|
||||
uint32_t data = (sr & 0xff) | (cr & 0xff) << 8;
|
||||
mp_spiflash_write_cmd_data(self, CMD_WRSR, 2, data);
|
||||
}
|
||||
}
|
||||
// Verify it's written correctly
|
||||
ret = mp_spiflash_read_cmd(self, CMD_RDCR, 1, &cr);
|
||||
if (ret == 0 && (cr & self->device->quad_enable_bit_mask) == 0) {
|
||||
// QE bit could not be set
|
||||
ret = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void mp_spiflash_deepsleep(mp_spiflash_t *self, int value) {
|
||||
|
@ -235,7 +361,7 @@ static int mp_spiflash_erase_block_internal(mp_spiflash_t *self, uint32_t addr)
|
|||
|
||||
// erase the sector
|
||||
uint8_t cmd = MICROPY_HW_SPI_ADDR_IS_32BIT(addr) ? CMD_SEC_ERASE_32 : CMD_SEC_ERASE;
|
||||
ret = mp_spiflash_transfer_cmd_addr_data(self, cmd, addr, 0, NULL, NULL);
|
||||
ret = mp_spiflash_transfer_cmd_addr_data(self, cmd, addr, 0, NULL, NULL, 0);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
@ -260,7 +386,7 @@ static int mp_spiflash_write_page(mp_spiflash_t *self, uint32_t addr, size_t len
|
|||
|
||||
// write the page
|
||||
uint8_t cmd = MICROPY_HW_SPI_ADDR_IS_32BIT(addr) ? CMD_WRITE_32 : CMD_WRITE;
|
||||
ret = mp_spiflash_transfer_cmd_addr_data(self, cmd, addr, len, src, NULL);
|
||||
ret = mp_spiflash_transfer_cmd_addr_data(self, cmd, addr, len, src, NULL, 0);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
@ -404,7 +530,7 @@ static int mp_spiflash_cached_write_part(mp_spiflash_t *self, uint32_t addr, siz
|
|||
|
||||
// Restriction for now, so we don't need to erase multiple pages
|
||||
if (offset + len > SECTOR_SIZE) {
|
||||
printf("mp_spiflash_cached_write_part: len is too large\n");
|
||||
diag_printf("mp_spiflash_cached_write_part: len is too large\n");
|
||||
return -MP_EIO;
|
||||
}
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ enum {
|
|||
};
|
||||
|
||||
struct _mp_spiflash_t;
|
||||
struct _external_flash_device;
|
||||
|
||||
#if MICROPY_HW_SPIFLASH_ENABLE_CACHE
|
||||
// A cache must be provided by the user in the config struct. The same cache
|
||||
|
@ -69,9 +70,10 @@ typedef struct _mp_spiflash_config_t {
|
|||
typedef struct _mp_spiflash_t {
|
||||
const mp_spiflash_config_t *config;
|
||||
volatile uint32_t flags;
|
||||
const struct _external_flash_device *device;
|
||||
} mp_spiflash_t;
|
||||
|
||||
void mp_spiflash_init(mp_spiflash_t *self);
|
||||
int mp_spiflash_init(mp_spiflash_t *self);
|
||||
void mp_spiflash_deepsleep(mp_spiflash_t *self, int value);
|
||||
|
||||
// These functions go direct to the SPI flash device
|
||||
|
|
|
@ -278,6 +278,7 @@ SRC_C += \
|
|||
adc.c \
|
||||
sdio.c \
|
||||
subghz.c \
|
||||
mpu.c \
|
||||
$(wildcard $(BOARD_DIR)/*.c)
|
||||
|
||||
SRC_O += \
|
||||
|
|
|
@ -65,10 +65,10 @@ void board_sleep(int value);
|
|||
#define MICROPY_HW_RTC_USE_CALOUT (1)
|
||||
|
||||
// SPI flash #1, for R/W storage
|
||||
#define MICROPY_HW_SPIFLASH_DEVICES AT25SF161B
|
||||
#define MICROPY_HW_SOFTQSPI_SCK_LOW(self) (GPIOE->BSRR = (0x10000 << 11))
|
||||
#define MICROPY_HW_SOFTQSPI_SCK_HIGH(self) (GPIOE->BSRR = (1 << 11))
|
||||
#define MICROPY_HW_SOFTQSPI_NIBBLE_READ(self) ((GPIOE->IDR >> 7) & 0xf)
|
||||
#define MICROPY_HW_SPIFLASH_SIZE_BITS (16 * 1024 * 1024)
|
||||
#define MICROPY_HW_SPIFLASH_CS (pyb_pin_QSPI1_CS)
|
||||
#define MICROPY_HW_SPIFLASH_SCK (pyb_pin_QSPI1_CLK)
|
||||
#define MICROPY_HW_SPIFLASH_IO0 (pyb_pin_QSPI1_D0)
|
||||
|
@ -84,7 +84,6 @@ extern struct _spi_bdev_t spi_bdev;
|
|||
#endif
|
||||
#define MICROPY_HW_BDEV_SPIFLASH (&spi_bdev)
|
||||
#define MICROPY_HW_BDEV_SPIFLASH_CONFIG (&spiflash_config)
|
||||
#define MICROPY_HW_BDEV_SPIFLASH_SIZE_BYTES (MICROPY_HW_SPIFLASH_SIZE_BITS / 8)
|
||||
#define MICROPY_HW_BDEV_SPIFLASH_EXTENDED (&spi_bdev) // for extended block protocol
|
||||
|
||||
// SPI flash #2, to be memory mapped
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -185,10 +185,14 @@ MP_NOINLINE static bool init_flash_fs(uint reset_mode) {
|
|||
|
||||
if (len != -1) {
|
||||
// Detected a littlefs filesystem so create correct block device for it
|
||||
mp_obj_t args[] = { MP_OBJ_NEW_QSTR(MP_QSTR_len), MP_OBJ_NEW_SMALL_INT(len) };
|
||||
bdev = MP_OBJ_TYPE_GET_SLOT(&pyb_flash_type, make_new)(&pyb_flash_type, 0, 1, args);
|
||||
mp_obj_t lfs_bdev = pyb_flash_new_obj(0, len);
|
||||
if (lfs_bdev == mp_const_none) {
|
||||
// Uncaught exception; len must be an invalid length.
|
||||
mp_printf(&mp_plat_print, "MPY: corrupted filesystem\n");
|
||||
} else {
|
||||
bdev = lfs_bdev;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Try to mount the flash on "/flash" and chdir to it for the boot-up directory.
|
||||
|
|
|
@ -548,11 +548,9 @@
|
|||
// - MICROPY_HW_BDEV_SPIFLASH - pointer to a spi_bdev_t
|
||||
// - MICROPY_HW_BDEV_SPIFLASH_CONFIG - pointer to an mp_spiflash_config_t
|
||||
// - MICROPY_HW_BDEV_SPIFLASH_SIZE_BYTES - size in bytes of the SPI flash
|
||||
#define MICROPY_HW_BDEV_IOCTL(op, arg) ( \
|
||||
(op) == BDEV_IOCTL_NUM_BLOCKS ? (MICROPY_HW_BDEV_SPIFLASH_SIZE_BYTES / FLASH_BLOCK_SIZE) : \
|
||||
(op) == BDEV_IOCTL_INIT ? spi_bdev_ioctl(MICROPY_HW_BDEV_SPIFLASH, (op), (uint32_t)MICROPY_HW_BDEV_SPIFLASH_CONFIG) : \
|
||||
spi_bdev_ioctl(MICROPY_HW_BDEV_SPIFLASH, (op), (arg)) \
|
||||
)
|
||||
// The board can specify the SPI flash chip(s) being used as comma separated list in:
|
||||
// - MICROPY_HW_SPIFLASH_DEVICES
|
||||
#define MICROPY_HW_BDEV_IOCTL(op, arg) (spi_bdev_ioctl(MICROPY_HW_BDEV_SPIFLASH, (op), (arg)))
|
||||
#define MICROPY_HW_BDEV_READBLOCKS(dest, bl, n) spi_bdev_readblocks(MICROPY_HW_BDEV_SPIFLASH, (dest), (bl), (n))
|
||||
#define MICROPY_HW_BDEV_WRITEBLOCKS(src, bl, n) spi_bdev_writeblocks(MICROPY_HW_BDEV_SPIFLASH, (src), (bl), (n))
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "py/mphal.h"
|
||||
#include "mpu.h"
|
||||
|
||||
#if (defined(STM32F4) && defined(MICROPY_HW_ETH_MDC)) || defined(STM32F7) || defined(STM32H7) || defined(STM32WB)
|
||||
|
||||
void mpu_config_region(uint32_t region, uint32_t base_addr, uint32_t attr_size) {
|
||||
MPU->RNR = region;
|
||||
MPU->RBAR = base_addr;
|
||||
MPU->RASR = attr_size;
|
||||
}
|
||||
|
||||
#elif defined(STM32H5)
|
||||
|
||||
void mpu_config_region(uint32_t region, uint32_t base_addr, uint32_t size) {
|
||||
if (region == MPU_REGION_ETH) {
|
||||
// Configure region 1 to make DMA memory non-cacheable.
|
||||
|
||||
__DMB();
|
||||
// Configure attribute 1, inner-outer non-cacheable (=0x44).
|
||||
MPU->MAIR0 = (MPU->MAIR0 & ~MPU_MAIR0_Attr1_Msk)
|
||||
| 0x44 << MPU_MAIR0_Attr1_Pos;
|
||||
__DMB();
|
||||
|
||||
// RBAR
|
||||
// BASE Bits [31:5] of base address
|
||||
// SH[4:3] 00 = Non-shareable
|
||||
// AP[2:1] 01 = Read/write by any privilege level
|
||||
// XN[0]: 1 = No execution
|
||||
|
||||
// RLAR
|
||||
// LIMIT Limit address. Contains bits[31:5] of the upper inclusive limit of the selected MPU memory region
|
||||
// AT[3:1] 001 = Attribute 1
|
||||
// EN[0] 1 = Enabled
|
||||
MPU->RNR = region;
|
||||
MPU->RBAR = (base_addr & MPU_RBAR_BASE_Msk)
|
||||
| MPU_ACCESS_NOT_SHAREABLE << MPU_RBAR_SH_Pos
|
||||
| MPU_REGION_ALL_RW << MPU_RBAR_AP_Pos
|
||||
| MPU_INSTRUCTION_ACCESS_DISABLE << MPU_RBAR_XN_Pos;
|
||||
MPU->RLAR = ((base_addr + size - 1) & MPU_RLAR_LIMIT_Msk)
|
||||
| MPU_ATTRIBUTES_NUMBER1 << MPU_RLAR_AttrIndx_Pos
|
||||
| MPU_REGION_ENABLE << MPU_RLAR_EN_Pos;
|
||||
}
|
||||
__DMB();
|
||||
}
|
||||
|
||||
#endif
|
|
@ -118,11 +118,7 @@ static inline uint32_t mpu_config_start(void) {
|
|||
return disable_irq();
|
||||
}
|
||||
|
||||
static inline void mpu_config_region(uint32_t region, uint32_t base_addr, uint32_t attr_size) {
|
||||
MPU->RNR = region;
|
||||
MPU->RBAR = base_addr;
|
||||
MPU->RASR = attr_size;
|
||||
}
|
||||
MP_NOINLINE void mpu_config_region(uint32_t region, uint32_t base_addr, uint32_t attr_size);
|
||||
|
||||
static inline void mpu_config_end(uint32_t irq_state) {
|
||||
__ISB();
|
||||
|
@ -172,44 +168,15 @@ static inline uint32_t mpu_config_start(void) {
|
|||
return disable_irq();
|
||||
}
|
||||
|
||||
static inline void mpu_config_region(uint32_t region, uint32_t base_addr, uint32_t size) {
|
||||
if (region == MPU_REGION_ETH) {
|
||||
// Configure region 1 to make DMA memory non-cacheable.
|
||||
MP_NOINLINE void mpu_config_region(uint32_t region, uint32_t base_addr, uint32_t attr_size);
|
||||
if (region == MPU_REGION_ETH) {
|
||||
|
||||
static inline void mpu_config_end(uint32_t irq_state) {
|
||||
__ISB();
|
||||
__DSB();
|
||||
__DMB();
|
||||
// Configure attribute 1, inner-outer non-cacheable (=0x44).
|
||||
MPU->MAIR0 = (MPU->MAIR0 & ~MPU_MAIR0_Attr1_Msk)
|
||||
| 0x44 << MPU_MAIR0_Attr1_Pos;
|
||||
__DMB();
|
||||
|
||||
// RBAR
|
||||
// BASE Bits [31:5] of base address
|
||||
// SH[4:3] 00 = Non-shareable
|
||||
// AP[2:1] 01 = Read/write by any privilege level
|
||||
// XN[0]: 1 = No execution
|
||||
|
||||
// RLAR
|
||||
// LIMIT Limit address. Contains bits[31:5] of the upper inclusive limit of the selected MPU memory region
|
||||
// AT[3:1] 001 = Attribute 1
|
||||
// EN[0] 1 = Enabled
|
||||
MPU->RNR = region;
|
||||
MPU->RBAR = (base_addr & MPU_RBAR_BASE_Msk)
|
||||
| MPU_ACCESS_NOT_SHAREABLE << MPU_RBAR_SH_Pos
|
||||
| MPU_REGION_ALL_RW << MPU_RBAR_AP_Pos
|
||||
| MPU_INSTRUCTION_ACCESS_DISABLE << MPU_RBAR_XN_Pos;
|
||||
MPU->RLAR = ((base_addr + size - 1) & MPU_RLAR_LIMIT_Msk)
|
||||
| MPU_ATTRIBUTES_NUMBER1 << MPU_RLAR_AttrIndx_Pos
|
||||
| MPU_REGION_ENABLE << MPU_RLAR_EN_Pos;
|
||||
enable_irq(irq_state);
|
||||
}
|
||||
__DMB();
|
||||
}
|
||||
|
||||
static inline void mpu_config_end(uint32_t irq_state) {
|
||||
__ISB();
|
||||
__DSB();
|
||||
__DMB();
|
||||
enable_irq(irq_state);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
@ -269,37 +269,59 @@ static int octospi_read_cmd(void *self_in, uint8_t cmd, size_t len, uint32_t *de
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int octospi_read_cmd_qaddr_qdata(void *self_in, uint8_t cmd, uint32_t addr, size_t len, uint8_t *dest) {
|
||||
static int octospi_read_cmd_addr_data(void *self_in, uint8_t cmd, uint32_t addr, size_t len, uint8_t *dest, uint8_t mode) {
|
||||
// Note this only support use with 1 or 4 line commands.
|
||||
// Full 8-line mode support is not included.
|
||||
// Some commands will auto-downgrade to support 2-line mode if needed by hardware.
|
||||
(void)self_in;
|
||||
|
||||
#if defined(MICROPY_HW_OSPIFLASH_IO1) && !defined(MICROPY_HW_OSPIFLASH_IO2) && !defined(MICROPY_HW_OSPIFLASH_IO4)
|
||||
uint32_t adsize = MICROPY_HW_SPI_ADDR_IS_32BIT(addr) ? 3 : 2;
|
||||
|
||||
uint32_t dmode = 0;
|
||||
uint32_t admode = 0;
|
||||
uint32_t dcyc = 0;
|
||||
uint32_t abmode = 0;
|
||||
|
||||
if (mode == MP_QSPI_TRANSFER_CMD_QADDR_QDATA) {
|
||||
dmode = 3; // 4 data lines used
|
||||
admode = 3; // 4 address lines used
|
||||
dcyc = 4; // 4 dummy cycles (2 bytes)
|
||||
abmode = 3; // alternate-byte bytes sent on 4 lines
|
||||
} else if (mode == MP_QSPI_TRANSFER_CMD_ADDR_DATA) {
|
||||
dmode = 1; // 1 data lines used
|
||||
admode = 1; // 1 address lines used
|
||||
dcyc = 8; // 8 dummy cycles (1 byte)
|
||||
abmode = 0; // No alternate-byte bytes sent
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if !defined(MICROPY_HW_OSPIFLASH_IO2) && !defined(MICROPY_HW_OSPIFLASH_IO4)
|
||||
|
||||
// Use 2-line address, 2-line data.
|
||||
|
||||
uint32_t adsize = MICROPY_HW_SPI_ADDR_IS_32BIT(addr) ? 3 : 2;
|
||||
uint32_t dmode = 2; // data on 2-lines
|
||||
uint32_t admode = 2; // address on 2-lines
|
||||
uint32_t dcyc = 4; // 4 dummy cycles
|
||||
dmode = 2; // data on 2-lines
|
||||
admode = 2; // address on 2-lines
|
||||
dcyc = 4; // 4 dummy cycles
|
||||
|
||||
if (cmd == 0xeb || cmd == 0xec) {
|
||||
// Convert to 2-line command.
|
||||
cmd = MICROPY_HW_SPI_ADDR_IS_32BIT(addr) ? 0xbc : 0xbb;
|
||||
}
|
||||
#endif
|
||||
|
||||
#else
|
||||
#if !defined(MICROPY_HW_OSPIFLASH_IO1)
|
||||
|
||||
// Fallback to use 1-line address, 1-line data.
|
||||
|
||||
uint32_t adsize = MICROPY_HW_SPI_ADDR_IS_32BIT(addr) ? 3 : 2;
|
||||
uint32_t dmode = 1; // data on 1-line
|
||||
uint32_t admode = 1; // address on 1-line
|
||||
uint32_t dcyc = 0; // 0 dummy cycles
|
||||
dmode = 1; // data on 1-line
|
||||
admode = 1; // address on 1-line
|
||||
dcyc = 0; // 0 dummy cycles
|
||||
|
||||
if (cmd == 0xeb || cmd == 0xec) {
|
||||
// Convert to 1-line command.
|
||||
cmd = MICROPY_HW_SPI_ADDR_IS_32BIT(addr) ? 0x13 : 0x03;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
OCTOSPI1->FCR = OCTOSPI_FCR_CTCF; // clear TC flag
|
||||
|
@ -311,7 +333,7 @@ static int octospi_read_cmd_qaddr_qdata(void *self_in, uint8_t cmd, uint32_t add
|
|||
| 0 << OCTOSPI_CCR_SIOO_Pos // send instruction every transaction
|
||||
| dmode << OCTOSPI_CCR_DMODE_Pos // data on n lines
|
||||
| 0 << OCTOSPI_CCR_ABSIZE_Pos // 8-bit alternate byte
|
||||
| 0 << OCTOSPI_CCR_ABMODE_Pos // no alternate byte
|
||||
| abmode << OCTOSPI_CCR_ABMODE_Pos // alternate byte
|
||||
| adsize << OCTOSPI_CCR_ADSIZE_Pos // 32 or 24-bit address size
|
||||
| admode << OCTOSPI_CCR_ADMODE_Pos // address on n lines
|
||||
| 1 << OCTOSPI_CCR_IMODE_Pos // instruction on 1 line
|
||||
|
@ -357,7 +379,7 @@ const mp_qspi_proto_t octospi_proto = {
|
|||
.write_cmd_data = octospi_write_cmd_data,
|
||||
.write_cmd_addr_data = octospi_write_cmd_addr_data,
|
||||
.read_cmd = octospi_read_cmd,
|
||||
.read_cmd_qaddr_qdata = octospi_read_cmd_qaddr_qdata,
|
||||
.read_cmd_addr_data = octospi_read_cmd_addr_data,
|
||||
};
|
||||
|
||||
#endif // defined(MICROPY_HW_OSPIFLASH_SIZE_BITS_LOG2)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
@ -350,11 +433,29 @@ static int qspi_read_cmd(void *self_in, uint8_t cmd, size_t len, uint32_t *dest)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int qspi_read_cmd_qaddr_qdata(void *self_in, uint8_t cmd, uint32_t addr, size_t len, uint8_t *dest) {
|
||||
static int qspi_read_cmd_addr_data(void *self_in, uint8_t cmd, uint32_t addr, size_t len, uint8_t *dest, uint8_t mode) {
|
||||
(void)self_in;
|
||||
|
||||
uint8_t adsize = MICROPY_HW_SPI_ADDR_IS_32BIT(addr) ? 3 : 2;
|
||||
|
||||
uint32_t dmode = 0;
|
||||
uint32_t admode = 0;
|
||||
uint32_t dcyc = 0;
|
||||
uint32_t abmode = 0;
|
||||
|
||||
if (mode == MP_QSPI_TRANSFER_CMD_QADDR_QDATA) {
|
||||
dmode = 3; // 4 data lines used
|
||||
admode = 3; // 4 address lines used
|
||||
dcyc = 4; // 4 dummy cycles (2 bytes)
|
||||
abmode = 3; // alternate-byte bytes sent on 4 lines
|
||||
} else if (mode == MP_QSPI_TRANSFER_CMD_ADDR_DATA) {
|
||||
dmode = 1; // 1 data lines used
|
||||
admode = 1; // 1 address lines used
|
||||
dcyc = 8; // 8 dummy cycles (1 byte)
|
||||
abmode = 0; // No alternate-byte bytes sent
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
QUADSPI->FCR = QUADSPI_FCR_CTCF; // clear TC flag
|
||||
|
||||
QUADSPI->DLR = len - 1; // number of bytes to read
|
||||
|
@ -363,12 +464,12 @@ static int qspi_read_cmd_qaddr_qdata(void *self_in, uint8_t cmd, uint32_t addr,
|
|||
0 << QUADSPI_CCR_DDRM_Pos // DDR mode disabled
|
||||
| 0 << QUADSPI_CCR_SIOO_Pos // send instruction every transaction
|
||||
| 1 << QUADSPI_CCR_FMODE_Pos // indirect read mode
|
||||
| 3 << QUADSPI_CCR_DMODE_Pos // data on 4 lines
|
||||
| 4 << QUADSPI_CCR_DCYC_Pos // 4 dummy cycles
|
||||
| dmode << QUADSPI_CCR_DMODE_Pos // data lines
|
||||
| dcyc << QUADSPI_CCR_DCYC_Pos // dummy cycles
|
||||
| 0 << QUADSPI_CCR_ABSIZE_Pos // 8-bit alternate byte
|
||||
| 3 << QUADSPI_CCR_ABMODE_Pos // alternate byte on 4 lines
|
||||
| abmode << QUADSPI_CCR_ABMODE_Pos // alternate byte count / lines
|
||||
| adsize << QUADSPI_CCR_ADSIZE_Pos // 32 or 24-bit address size
|
||||
| 3 << QUADSPI_CCR_ADMODE_Pos // address on 4 lines
|
||||
| admode << QUADSPI_CCR_ADMODE_Pos // address lines
|
||||
| 1 << QUADSPI_CCR_IMODE_Pos // instruction on 1 line
|
||||
| cmd << QUADSPI_CCR_INSTRUCTION_Pos // quad read opcode
|
||||
;
|
||||
|
@ -419,7 +520,7 @@ const mp_qspi_proto_t qspi_proto = {
|
|||
.write_cmd_data = qspi_write_cmd_data,
|
||||
.write_cmd_addr_data = qspi_write_cmd_addr_data,
|
||||
.read_cmd = qspi_read_cmd,
|
||||
.read_cmd_qaddr_qdata = qspi_read_cmd_qaddr_qdata,
|
||||
.read_cmd_addr_data = qspi_read_cmd_addr_data,
|
||||
};
|
||||
|
||||
#endif // defined(MICROPY_HW_QSPIFLASH_SIZE_BITS_LOG2)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -29,16 +29,33 @@
|
|||
#include "irq.h"
|
||||
#include "led.h"
|
||||
#include "storage.h"
|
||||
#include "drivers/memory/external_flash_device.h"
|
||||
|
||||
#if MICROPY_HW_ENABLE_STORAGE
|
||||
|
||||
int32_t spi_bdev_ioctl(spi_bdev_t *bdev, uint32_t op, uint32_t arg) {
|
||||
switch (op) {
|
||||
case BDEV_IOCTL_INIT:
|
||||
#ifdef MICROPY_HW_BDEV_SPIFLASH_CONFIG
|
||||
if (!arg) {
|
||||
arg = (uint32_t)(MICROPY_HW_BDEV_SPIFLASH_CONFIG);
|
||||
}
|
||||
#endif
|
||||
bdev->spiflash.config = (const mp_spiflash_config_t *)arg;
|
||||
mp_spiflash_init(&bdev->spiflash);
|
||||
int ret = mp_spiflash_init(&bdev->spiflash);
|
||||
bdev->flash_tick_counter_last_write = 0;
|
||||
return 0;
|
||||
return ret;
|
||||
|
||||
case BDEV_IOCTL_NUM_BLOCKS:
|
||||
#if MICROPY_HW_BDEV_SPIFLASH_SIZE_BYTES
|
||||
return MICROPY_HW_BDEV_SPIFLASH_SIZE_BYTES / FLASH_BLOCK_SIZE;
|
||||
#else
|
||||
if (bdev->spiflash.device != NULL) {
|
||||
external_flash_device *flash = (external_flash_device *)bdev->spiflash.device;
|
||||
return flash->total_size / FLASH_BLOCK_SIZE;
|
||||
}
|
||||
return -1;
|
||||
#endif
|
||||
|
||||
case BDEV_IOCTL_IRQ_HANDLER: {
|
||||
int ret = 0;
|
||||
|
|
|
@ -48,11 +48,11 @@ static void storage_systick_callback(uint32_t ticks_ms);
|
|||
|
||||
void storage_init(void) {
|
||||
if (!storage_is_initialised) {
|
||||
storage_is_initialised = true;
|
||||
|
||||
systick_enable_dispatch(SYSTICK_DISPATCH_STORAGE, storage_systick_callback);
|
||||
|
||||
MICROPY_HW_BDEV_IOCTL(BDEV_IOCTL_INIT, 0);
|
||||
if (MICROPY_HW_BDEV_IOCTL(BDEV_IOCTL_INIT, 0) == 0) {
|
||||
storage_is_initialised = true;
|
||||
}
|
||||
|
||||
#if defined(MICROPY_HW_BDEV2_IOCTL)
|
||||
MICROPY_HW_BDEV2_IOCTL(BDEV_IOCTL_INIT, 0);
|
||||
|
@ -273,6 +273,30 @@ const pyb_flash_obj_t pyb_flash_obj = {
|
|||
0, // actual size handled in ioctl, MP_BLOCKDEV_IOCTL_BLOCK_COUNT case
|
||||
};
|
||||
|
||||
mp_obj_t pyb_flash_new_obj(mp_int_t start, mp_int_t len) {
|
||||
|
||||
uint32_t bl_len = (storage_get_block_count() - FLASH_PART1_START_BLOCK) * FLASH_BLOCK_SIZE;
|
||||
|
||||
if (start == -1) {
|
||||
start = 0;
|
||||
} else if (!(0 <= start && start < bl_len && start % MICROPY_HW_BDEV_BLOCKSIZE_EXT == 0)) {
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
if (len == -1) {
|
||||
len = bl_len - start;
|
||||
} else if (!(0 < len && start + len <= bl_len && len % MICROPY_HW_BDEV_BLOCKSIZE_EXT == 0)) {
|
||||
return mp_const_none;
|
||||
}
|
||||
|
||||
pyb_flash_obj_t *self = mp_obj_malloc(pyb_flash_obj_t, &pyb_flash_type);
|
||||
self->use_native_block_size = false;
|
||||
self->start = start;
|
||||
self->len = len;
|
||||
|
||||
return MP_OBJ_FROM_PTR(self);
|
||||
}
|
||||
|
||||
static void pyb_flash_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
|
||||
pyb_flash_obj_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
if (self == &pyb_flash_obj) {
|
||||
|
@ -296,30 +320,15 @@ static mp_obj_t pyb_flash_make_new(const mp_obj_type_t *type, size_t n_args, siz
|
|||
// Default singleton object that accesses entire flash, including virtual partition table
|
||||
return MP_OBJ_FROM_PTR(&pyb_flash_obj);
|
||||
}
|
||||
|
||||
pyb_flash_obj_t *self = mp_obj_malloc(pyb_flash_obj_t, &pyb_flash_type);
|
||||
self->use_native_block_size = false;
|
||||
|
||||
uint32_t bl_len = (storage_get_block_count() - FLASH_PART1_START_BLOCK) * FLASH_BLOCK_SIZE;
|
||||
|
||||
mp_int_t start = args[ARG_start].u_int;
|
||||
if (start == -1) {
|
||||
start = 0;
|
||||
} else if (!(0 <= start && start < bl_len && start % MICROPY_HW_BDEV_BLOCKSIZE_EXT == 0)) {
|
||||
mp_raise_ValueError(NULL);
|
||||
}
|
||||
|
||||
mp_int_t len = args[ARG_len].u_int;
|
||||
if (len == -1) {
|
||||
len = bl_len - start;
|
||||
} else if (!(0 < len && start + len <= bl_len && len % MICROPY_HW_BDEV_BLOCKSIZE_EXT == 0)) {
|
||||
|
||||
mp_obj_t self = pyb_flash_new_obj(start, len);
|
||||
if (self == mp_const_none) {
|
||||
// Invalid start or end arg
|
||||
mp_raise_ValueError(NULL);
|
||||
}
|
||||
|
||||
self->start = start;
|
||||
self->len = len;
|
||||
|
||||
return MP_OBJ_FROM_PTR(self);
|
||||
return self;
|
||||
}
|
||||
|
||||
static mp_obj_t pyb_flash_readblocks(size_t n_args, const mp_obj_t *args) {
|
||||
|
|
|
@ -77,4 +77,8 @@ extern const struct _pyb_flash_obj_t pyb_flash_obj;
|
|||
struct _fs_user_mount_t;
|
||||
void pyb_flash_init_vfs(struct _fs_user_mount_t *vfs);
|
||||
|
||||
#if !BUILDING_MBOOT
|
||||
mp_obj_t pyb_flash_new_obj(mp_int_t start, mp_int_t len);
|
||||
#endif
|
||||
|
||||
#endif // MICROPY_INCLUDED_STM32_STORAGE_H
|
||||
|
|
Ładowanie…
Reference in New Issue