diff --git a/Documentation/platforms/arm/rp23xx/index.rst b/Documentation/platforms/arm/rp23xx/index.rst index 4de30f14006..9d650248181 100644 --- a/Documentation/platforms/arm/rp23xx/index.rst +++ b/Documentation/platforms/arm/rp23xx/index.rst @@ -322,7 +322,9 @@ The region is described by ``RP23XX_FLASH_MTD_OFFSET`` (byte offset from ``0x10000000``) and ``RP23XX_FLASH_MTD_SIZE``, both of which must be multiples of the 4096 byte erase sector. The driver refuses to initialize if the region would overlap the NuttX image (it checks ``__flash_binary_end``), so a bad -offset fails at boot instead of corrupting the running firmware. +offset fails at boot instead of corrupting the running firmware. It also +reads the flash size from the JEDEC ID and refuses a region that ends past the +end of the flash, because such an address wraps around to the image. Erase and program go through the bootrom flash routines. Because those operations stall instruction fetch from the same flash, the driver runs them diff --git a/arch/arm/src/rp23xx/Kconfig b/arch/arm/src/rp23xx/Kconfig index 1f166d81cd0..4cc45cd0c6b 100644 --- a/arch/arm/src/rp23xx/Kconfig +++ b/arch/arm/src/rp23xx/Kconfig @@ -1241,7 +1241,8 @@ config RP23XX_FLASH_MTD_SIZE ---help--- Size of the MTD region in bytes. Must be a multiple of the 4096 byte erase sector, and must fit within the flash fitted to the - board. + board. The driver reads the flash size from its JEDEC ID, and + refuses to initialize if the region does not fit. config RP23XX_FLASH_MTD_SAFE_XIP bool "Always restore XIP with the bootrom command mode" diff --git a/arch/arm/src/rp23xx/rp23xx_flash_mtd.c b/arch/arm/src/rp23xx/rp23xx_flash_mtd.c index a3e9d3ae655..826c27e33cb 100644 --- a/arch/arm/src/rp23xx/rp23xx_flash_mtd.c +++ b/arch/arm/src/rp23xx/rp23xx_flash_mtd.c @@ -142,6 +142,11 @@ #define FLASH_BLOCK_SIZE 65536 #define FLASH_BLOCK_ERASE_CMD 0xd8 +/* JEDEC ID: command, manufacturer, memory type, capacity (log2 bytes) */ + +#define FLASH_READ_ID_CMD 0x9f +#define FLASH_READ_ID_SIZE 4 + #define FS_OFFSET CONFIG_RP23XX_FLASH_MTD_OFFSET #define FS_SIZE CONFIG_RP23XX_FLASH_MTD_SIZE @@ -171,7 +176,7 @@ struct rp23xx_flash_op_s { CODE void (*func)(FAR struct rp23xx_flash_op_s *op); uint32_t addr; - FAR const uint8_t *data; + FAR uint8_t *data; size_t count; }; @@ -545,6 +550,63 @@ static void RAM_CODE(do_write)(FAR struct rp23xx_flash_op_s *op) rp23xx_flash_end(&state); } +/**************************************************************************** + * Name: do_read_id + * + * Description: + * Read the JEDEC ID in QMI direct mode, as the Pico SDK flash_do_cmd() + * does. + * + ****************************************************************************/ + +static void RAM_CODE(do_read_id)(FAR struct rp23xx_flash_op_s *op) +{ + struct rp23xx_qspi_state_s state; + size_t tx = 0; + size_t rx = 0; + uint32_t csr; + + rp23xx_flash_begin(&state); + + putreg32(getreg32(RP23XX_QMI_DIRECT_CSR) | + RP23XX_QMI_DIRECT_CSR_ASSERT_CS0N, RP23XX_QMI_DIRECT_CSR); + putreg32(getreg32(RP23XX_QMI_DIRECT_CSR) | RP23XX_QMI_DIRECT_CSR_EN, + RP23XX_QMI_DIRECT_CSR); + + while ((getreg32(RP23XX_QMI_DIRECT_CSR) & RP23XX_QMI_DIRECT_CSR_BUSY) != 0) + { + } + + while (tx < op->count || rx < op->count) + { + csr = getreg32(RP23XX_QMI_DIRECT_CSR); + + if ((csr & RP23XX_QMI_DIRECT_CSR_TXFULL) == 0 && tx < op->count) + { + putreg32(tx == 0 ? FLASH_READ_ID_CMD : 0, RP23XX_QMI_DIRECT_TX); + tx++; + } + + if ((csr & RP23XX_QMI_DIRECT_CSR_RXEMPTY) == 0 && rx < op->count) + { + op->data[rx++] = (uint8_t)getreg32(RP23XX_QMI_DIRECT_RX); + } + } + + /* BUSY stays high for half an SCK after the last bit, for CS timing */ + + while ((getreg32(RP23XX_QMI_DIRECT_CSR) & RP23XX_QMI_DIRECT_CSR_BUSY) != 0) + { + } + + putreg32(getreg32(RP23XX_QMI_DIRECT_CSR) & ~RP23XX_QMI_DIRECT_CSR_EN, + RP23XX_QMI_DIRECT_CSR); + putreg32(getreg32(RP23XX_QMI_DIRECT_CSR) & + ~RP23XX_QMI_DIRECT_CSR_ASSERT_CS0N, RP23XX_QMI_DIRECT_CSR); + + rp23xx_flash_end(&state); +} + /**************************************************************************** * Name: rp23xx_flash_call * @@ -607,6 +669,42 @@ static void rp23xx_flash_run(void) #endif } +/**************************************************************************** + * Name: rp23xx_flash_size + * + * Description: + * Return the flash size from its JEDEC ID, or 0 if the ID is not valid. + * + ****************************************************************************/ + +static size_t rp23xx_flash_size(void) +{ + FAR uint8_t *id = g_flash_page; + + if (nxmutex_lock(&g_flash_dev.lock) < 0) + { + return 0; + } + + g_flash_op.func = do_read_id; + g_flash_op.data = id; + g_flash_op.count = FLASH_READ_ID_SIZE; + + rp23xx_flash_run(); + nxmutex_unlock(&g_flash_dev.lock); + + finfo("rp23xx_flash: JEDEC ID %02x %02x %02x\n", id[1], id[2], id[3]); + + /* Accept a capacity from 64K to 64M */ + + if (id[1] == 0x00 || id[1] == 0xff || id[3] < 16 || id[3] > 26) + { + return 0; + } + + return (size_t)1 << id[3]; +} + /**************************************************************************** * Name: rp23xx_flash_erase * @@ -725,7 +823,7 @@ static ssize_t rp23xx_flash_bwrite(struct mtd_dev_s *dev, off_t startblock, { g_flash_op.func = do_write; g_flash_op.addr = FS_OFFSET + (startblock + i) * FLASH_PAGE_SIZE; - g_flash_op.data = buffer + i * FLASH_PAGE_SIZE; + g_flash_op.data = (FAR uint8_t *)buffer + i * FLASH_PAGE_SIZE; g_flash_op.count = FLASH_PAGE_SIZE; if (IS_XIP_ADDR(g_flash_op.data)) @@ -858,6 +956,7 @@ static int rp23xx_flash_ioctl(struct mtd_dev_s *dev, int cmd, struct mtd_dev_s *rp23xx_flash_mtd_initialize(void) { + size_t size; #ifndef CONFIG_RP23XX_FLASH_MTD_SAFE_XIP int i; #endif @@ -937,6 +1036,23 @@ struct mtd_dev_s *rp23xx_flash_mtd_initialize(void) } #endif + /* An address past the end of the flash wraps around to the start, where + * the NuttX image is. Refuse a region that does not fit. + */ + + size = rp23xx_flash_size(); + if (size == 0) + { + fwarn("rp23xx_flash: unknown flash size; not checked\n"); + } + else if (FS_OFFSET + FS_SIZE > size) + { + merr("ERROR: flash MTD region ends at 0x%08x, past the end of the " + "%zu byte flash\n", (unsigned)(FS_OFFSET + FS_SIZE), size); + set_errno(EINVAL); + return NULL; + } + g_initialized = true; finfo("rp23xx_flash: %u sectors of %u bytes at 0x%08x\n",