diff options
| author | Damien George <damien@micropython.org> | 2024-10-07 16:06:50 +1100 |
|---|---|---|
| committer | Damien George <damien@micropython.org> | 2025-04-09 00:22:32 +1000 |
| commit | 84effb386ac23257e9a399bf52dc23f8f50387fe (patch) | |
| tree | 86a7eaae3cd80a0100d60a8f67880e3c733b0fb8 | |
| parent | 4c4b4844df7746aa2ed4b124663e2472cb908b1a (diff) | |
alif/ospi_flash: Generalise flash driver to support MX chips.
Signed-off-by: Damien George <damien@micropython.org>
| -rw-r--r-- | ports/alif/alif.mk | 1 | ||||
| -rw-r--r-- | ports/alif/ospi_ext.c | 292 | ||||
| -rw-r--r-- | ports/alif/ospi_ext.h | 57 | ||||
| -rw-r--r-- | ports/alif/ospi_flash.c | 362 | ||||
| -rw-r--r-- | ports/alif/ospi_flash.h | 61 | ||||
| -rw-r--r-- | ports/alif/ospi_xip_user.h | 5 |
6 files changed, 643 insertions, 135 deletions
diff --git a/ports/alif/alif.mk b/ports/alif/alif.mk index 210b06c65..158145219 100644 --- a/ports/alif/alif.mk +++ b/ports/alif/alif.mk @@ -122,6 +122,7 @@ SRC_C = \ mpu.c \ mpuart.c \ msc_disk.c \ + ospi_ext.c \ ospi_flash.c \ pendsv.c \ system_tick.c \ diff --git a/ports/alif/ospi_ext.c b/ports/alif/ospi_ext.c new file mode 100644 index 000000000..48446d062 --- /dev/null +++ b/ports/alif/ospi_ext.c @@ -0,0 +1,292 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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 ALIF_CMSIS_H +#include "ospi_ext.h" +#include "ospi_xip_user.h" + +#define INST_L16bit (3) + +static void ospi_xip_disable(ospi_flash_cfg_t *ospi_cfg) { + ospi_cfg->aes_regs->aes_control &= ~AES_CONTROL_XIP_EN; +} + +static void ospi_xip_enable(ospi_flash_cfg_t *ospi_cfg) { + ospi_cfg->aes_regs->aes_control |= AES_CONTROL_XIP_EN; + #if OSPI_XIP_ENABLE_AES_DECRYPTION + ospi_cfg->aes_regs->aes_control |= (AES_CONTROL_LD_KEY | AES_CONTROL_DECRYPT_EN); + #endif +} + +// Standard SPI transfer. +void ospi_spi_transfer(ospi_flash_cfg_t *ospi_cfg, size_t len, const uint8_t *buf_out, uint8_t *buf_in) { + ospi_writel(ospi_cfg, ser, 0); + spi_disable(ospi_cfg); + + uint32_t ctrlr0 = CTRLR0_IS_MST + | (SINGLE << CTRLR0_SPI_FRF_OFFSET) + | (SPI_TMOD_TR << CTRLR0_TMOD_OFFSET) + | (CTRLR0_DFS_8bit << CTRLR0_DFS_OFFSET) + ; + + uint32_t spi_ctrlr0 = TRANS_TYPE_STANDARD; + + ospi_writel(ospi_cfg, ctrlr0, ctrlr0); + ospi_writel(ospi_cfg, ctrlr1, len - 1); + ospi_writel(ospi_cfg, spi_ctrlr0, spi_ctrlr0); + spi_enable(ospi_cfg); + + // Buffer output data in SPI FIFO. + for (int i = 0; i < len; ++i) { + ospi_writel(ospi_cfg, data_reg, buf_out[i]); + } + + // Enable the SPI peripheral. + ospi_writel(ospi_cfg, ser, ospi_cfg->ser); + + // Read in data. + for (int i = 0; i < len; ++i) { + unsigned int timeout = 100000; + while (ospi_readl(ospi_cfg, rxflr) == 0) { + if (--timeout == 0) { + return; + } + } + buf_in[i] = ospi_readl(ospi_cfg, data_reg); + } +} + +void ospi_setup_read_ext(ospi_flash_cfg_t *ospi_cfg, bool rxds, uint32_t inst_len, uint32_t addr_len, uint32_t data_len, uint32_t read_len, uint32_t wait_cycles) { + ospi_writel(ospi_cfg, ser, 0); + spi_disable(ospi_cfg); + + uint32_t val = CTRLR0_IS_MST + | (OCTAL << CTRLR0_SPI_FRF_OFFSET) + | (TMOD_RO << CTRLR0_TMOD_OFFSET) + | (data_len << CTRLR0_DFS_OFFSET); + + ospi_writel(ospi_cfg, ctrlr0, val); + ospi_writel(ospi_cfg, ctrlr1, read_len - 1); + + if (ospi_cfg->ddr_en) { + val = TRANS_TYPE_FRF_DEFINED + | (rxds << CTRLR0_SPI_RXDS_EN_OFFSET) + | (1 << CTRLR0_SPI_DDR_EN_OFFSET) + | (inst_len << CTRLR0_INST_L_OFFSET) + | (addr_len << CTRLR0_ADDR_L_OFFSET) + | (wait_cycles << CTRLR0_WAIT_CYCLES_OFFSET); + if (inst_len == OSPI_INST_L_16bit) { + val |= 1 << CTRLR0_INST_DDR_EN_OFFSET; + } + } else { + val = TRANS_TYPE_FRF_DEFINED + | (rxds << CTRLR0_SPI_RXDS_EN_OFFSET) + | (inst_len << CTRLR0_INST_L_OFFSET) + | (addr_len << CTRLR0_ADDR_L_OFFSET) + | (wait_cycles << CTRLR0_WAIT_CYCLES_OFFSET); + } + + ospi_writel(ospi_cfg, spi_ctrlr0, val); + ospi_cfg->rx_req = read_len; + spi_enable(ospi_cfg); +} + +int ospi_recv_blocking_8bit_data(ospi_flash_cfg_t *ospi_cfg, uint32_t command, uint8_t *buffer) { + ospi_writel(ospi_cfg, data_reg, command); + ospi_writel(ospi_cfg, ser, ospi_cfg->ser); + + ospi_cfg->rx_cnt = 0; + + while (ospi_cfg->rx_cnt < ospi_cfg->rx_req) { + unsigned int timeout = 100000; + while (ospi_readl(ospi_cfg, rxflr) == 0) { + if (--timeout == 0) { + return -OSPI_ETIMEDOUT; + } + } + *buffer++ = ospi_readl(ospi_cfg, data_reg); + ospi_cfg->rx_cnt++; + } + + return 0; +} + +int ospi_recv_blocking_16bit_data(ospi_flash_cfg_t *ospi_cfg, uint32_t command, uint16_t *buffer) { + ospi_writel(ospi_cfg, data_reg, command); + ospi_writel(ospi_cfg, ser, ospi_cfg->ser); + + ospi_cfg->rx_cnt = 0; + + while (ospi_cfg->rx_cnt < ospi_cfg->rx_req) { + unsigned int timeout = 100000; + while (ospi_readl(ospi_cfg, rxflr) == 0) { + if (--timeout == 0) { + return -OSPI_ETIMEDOUT; + } + } + *buffer++ = ospi_readl(ospi_cfg, data_reg); + ospi_cfg->rx_cnt++; + } + + return 0; +} + +int ospi_recv_blocking_32bit_data(ospi_flash_cfg_t *ospi_cfg, uint32_t command, uint32_t *buffer) { + ospi_writel(ospi_cfg, data_reg, command); + ospi_writel(ospi_cfg, ser, ospi_cfg->ser); + + ospi_cfg->rx_cnt = 0; + + while (ospi_cfg->rx_cnt < ospi_cfg->rx_req) { + unsigned int timeout = 100000; + while (ospi_readl(ospi_cfg, rxflr) == 0) { + if (--timeout == 0) { + return -OSPI_ETIMEDOUT; + } + } + *buffer++ = __ROR(ospi_readl(ospi_cfg, data_reg), 16); + ospi_cfg->rx_cnt++; + } + + return 0; +} + +void ospi_setup_write_ext(ospi_flash_cfg_t *ospi_cfg, bool rxds, uint32_t inst_len, uint32_t addr_len, uint32_t data_len) { + ospi_writel(ospi_cfg, ser, 0); + spi_disable(ospi_cfg); + + uint32_t val = CTRLR0_IS_MST + | (OCTAL << CTRLR0_SPI_FRF_OFFSET) + | (TMOD_TO << CTRLR0_TMOD_OFFSET) + | (data_len << CTRLR0_DFS_OFFSET); + + ospi_writel(ospi_cfg, ctrlr0, val); + ospi_writel(ospi_cfg, ctrlr1, 0); + + if (ospi_cfg->ddr_en) { + val = TRANS_TYPE_FRF_DEFINED + | (rxds << CTRLR0_SPI_RXDS_EN_OFFSET) + | (1 << CTRLR0_SPI_DDR_EN_OFFSET) + | (inst_len << CTRLR0_INST_L_OFFSET) + | (addr_len << CTRLR0_ADDR_L_OFFSET) + | (0 << CTRLR0_WAIT_CYCLES_OFFSET); + if (inst_len == OSPI_INST_L_16bit) { + val |= 1 << CTRLR0_INST_DDR_EN_OFFSET; + } + } else { + val = TRANS_TYPE_FRF_DEFINED + | (rxds << CTRLR0_SPI_RXDS_EN_OFFSET) + | (inst_len << CTRLR0_INST_L_OFFSET) + | (addr_len << CTRLR0_ADDR_L_OFFSET) + | (0 << CTRLR0_WAIT_CYCLES_OFFSET); + } + + ospi_writel(ospi_cfg, spi_ctrlr0, val); + spi_enable(ospi_cfg); +} + +void ospi_xip_enter_16bit_cmd(ospi_flash_cfg_t *ospi_cfg, uint32_t data_len, uint16_t incr_command, uint16_t wrap_command, uint16_t read_dummy_cycles) { + spi_disable(ospi_cfg); + + uint32_t val = CTRLR0_IS_MST + | (OCTAL << CTRLR0_SPI_FRF_OFFSET) + | (0 << CTRLR0_SCPOL_OFFSET) + | (0 << CTRLR0_SCPH_OFFSET) + | (0 << CTRLR0_SSTE_OFFSET) + | (TMOD_RO << CTRLR0_TMOD_OFFSET) + | (data_len << CTRLR0_DFS_OFFSET); + + ospi_writel(ospi_cfg, ctrlr0, val); + + val = (OCTAL << XIP_CTRL_FRF_OFFSET) + | (0x2 << XIP_CTRL_TRANS_TYPE_OFFSET) + | (ADDR_L32bit << XIP_CTRL_ADDR_L_OFFSET) + | (INST_L16bit << XIP_CTRL_INST_L_OFFSET) + | (0x0 << XIP_CTRL_MD_BITS_EN_OFFSET) + | (read_dummy_cycles << XIP_CTRL_WAIT_CYCLES_OFFSET) + | (0x1 << XIP_CTRL_DFC_HC_OFFSET) + | (ospi_cfg->ddr_en << XIP_CTRL_DDR_EN_OFFSET) + | (ospi_cfg->ddr_en << XIP_CTRL_INST_DDR_EN_OFFSET) + | (0x1 << XIP_CTRL_RXDS_EN_OFFSET) + | (0x1 << XIP_CTRL_INST_EN_OFFSET) + | (0x0 << XIP_CTRL_CONT_XFER_EN_OFFSET) + | (0x0 << XIP_CTRL_HYPERBUS_EN_OFFSET) + | (0x1 << XIP_CTRL_RXDS_SIG_EN) + | (0x0 << XIP_CTRL_XIP_MBL_OFFSET) + | (0x0 << XIP_PREFETCH_EN_OFFSET) + | (0x0 << XIP_CTRL_RXDS_VL_EN_OFFSET); + + ospi_writel(ospi_cfg, xip_ctrl, val); + + ospi_writel(ospi_cfg, rx_sample_dly, 4); + ospi_writel(ospi_cfg, txd_drive_edge, 1); + ospi_cfg->aes_regs->aes_rxds_delay = OSPI_XIP_RXDS_DELAY; + + ospi_writel(ospi_cfg, xip_mode_bits, 0x0); + ospi_writel(ospi_cfg, xip_incr_inst, incr_command); + ospi_writel(ospi_cfg, xip_wrap_inst, wrap_command); + ospi_writel(ospi_cfg, xip_ser, ospi_cfg->ser); + + spi_enable(ospi_cfg); + ospi_xip_enable(ospi_cfg); +} + +void ospi_xip_exit_16bit_cmd(ospi_flash_cfg_t *ospi_cfg, uint16_t incr_command, uint16_t wrap_command) { + spi_disable(ospi_cfg); + + uint32_t val = CTRLR0_IS_MST + | (OCTAL << CTRLR0_SPI_FRF_OFFSET) + | (0 << CTRLR0_SCPOL_OFFSET) + | (0 << CTRLR0_SCPH_OFFSET) + | (0 << CTRLR0_SSTE_OFFSET) + | (TMOD_RO << CTRLR0_TMOD_OFFSET) + | (CTRLR0_DFS_32bit << CTRLR0_DFS_OFFSET); + + ospi_writel(ospi_cfg, ctrlr0, val); + + val = TRANS_TYPE_FRF_DEFINED + | ((ospi_cfg->ddr_en) << CTRLR0_SPI_DDR_EN_OFFSET) + | (2 << CTRLR0_XIP_MBL_OFFSET) + | (1 << CTRLR0_XIP_DFS_HC_OFFSET) + | (1 << CTRLR0_XIP_INST_EN_OFFSET) + | (CTRLR0_INST_L_16bit << CTRLR0_INST_L_OFFSET) + | (ospi_cfg->addrlen) << (CTRLR0_ADDR_L_OFFSET) + | (ospi_cfg->wait_cycles << CTRLR0_WAIT_CYCLES_OFFSET); + + ospi_writel(ospi_cfg, spi_ctrlr0, val); + + ospi_writel(ospi_cfg, xip_mode_bits, 0x1); + ospi_writel(ospi_cfg, xip_incr_inst, incr_command); + ospi_writel(ospi_cfg, xip_wrap_inst, wrap_command); + ospi_writel(ospi_cfg, xip_ser, ospi_cfg->ser); + ospi_writel(ospi_cfg, ser, ospi_cfg->ser); + ospi_writel(ospi_cfg, xip_cnt_time_out, 100); + + spi_enable(ospi_cfg); + + ospi_xip_enable(ospi_cfg); + ospi_xip_disable(ospi_cfg); +} diff --git a/ports/alif/ospi_ext.h b/ports/alif/ospi_ext.h new file mode 100644 index 000000000..e467772d1 --- /dev/null +++ b/ports/alif/ospi_ext.h @@ -0,0 +1,57 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 OpenMV LLC. + * + * 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. + */ +#ifndef MICROPY_INCLUDED_ALIF_OSPI_EXT_H +#define MICROPY_INCLUDED_ALIF_OSPI_EXT_H + +#include <stddef.h> +#include "ospi_drv.h" + +#define OSPI_ETIMEDOUT (110) + +#define OSPI_INST_L_8bit (CTRLR0_INST_L_8bit) +#define OSPI_INST_L_16bit (CTRLR0_INST_L_16bit) + +#define OSPI_ADDR_L_0bit UINT32_C(0x0) +#define OSPI_ADDR_L_24bit UINT32_C(0x6) +#define OSPI_ADDR_L_32bit UINT32_C(0x8) + +#define OSPI_DATA_L_8bit (CTRLR0_DFS_8bit) +#define OSPI_DATA_L_16bit (CTRLR0_DFS_16bit) +#define OSPI_DATA_L_32bit (CTRLR0_DFS_32bit) + +void ospi_spi_transfer(ospi_flash_cfg_t *ospi_cfg, size_t len, const uint8_t *buf_out, uint8_t *buf_in); + +void ospi_setup_read_ext(ospi_flash_cfg_t *ospi_cfg, bool rxds, uint32_t inst_len, uint32_t addr_len, uint32_t data_len, uint32_t read_len, uint32_t wait_cycles); +int ospi_recv_blocking_8bit_data(ospi_flash_cfg_t *ospi_cfg, uint32_t command, uint8_t *buffer); +int ospi_recv_blocking_16bit_data(ospi_flash_cfg_t *ospi_cfg, uint32_t command, uint16_t *buffer); +int ospi_recv_blocking_32bit_data(ospi_flash_cfg_t *ospi_cfg, uint32_t command, uint32_t *buffer); + +void ospi_setup_write_ext(ospi_flash_cfg_t *ospi_cfg, bool rxds, uint32_t inst_len, uint32_t addr_len, uint32_t data_len); + +void ospi_xip_enter_16bit_cmd(ospi_flash_cfg_t *ospi_cfg, uint32_t data_len, uint16_t incr_command, uint16_t wrap_command, uint16_t read_dummy_cycles); +void ospi_xip_exit_16bit_cmd(ospi_flash_cfg_t *ospi_cfg, uint16_t incr_command, uint16_t wrap_command); + +#endif // MICROPY_INCLUDED_ALIF_OSPI_EXT_H diff --git a/ports/alif/ospi_flash.c b/ports/alif/ospi_flash.c index f84761366..6f083de63 100644 --- a/ports/alif/ospi_flash.c +++ b/ports/alif/ospi_flash.c @@ -28,72 +28,84 @@ #include "py/mphal.h" #if MICROPY_HW_ENABLE_OSPI + +#include "ospi_ext.h" #include "ospi_flash.h" #include "ospi_drv.h" +#include "ospi_xip_user.h" #include "pinconf.h" -#define CMD_RDSR (0x05) -#define CMD_WREN (0x06) -#define CMD_SEC_ERASE_32ADDR (0x21) // 4kiB sector erase with 32-bit address -#define CMD_WRVOL (0x81) -#define CMD_RD_DEVID (0x9f) - -#define WAIT_SR_TIMEOUT (1000000) +#define WAIT_SR_TIMEOUT (1000000) -// maximum bytes we can write in one SPI transfer -// limited by 256 byte FIFO buffer (can't go up to 256) -// need to use DMA to make this 256 -#define PAGE_SIZE (128) +// Generic SPI flash commands. +#define CMD_SPI_WREN (0x06) -#define ISSI_MODE_OCTAL_DDR_DQS (0xe7) +// This is the maximum number of bytes that can be written to SPI flash at once. +#define PAGE_SIZE (256) -// All OSPI1 pins use the same alternate function. -#define OSPI1_PIN_FUNCTION PINMUX_ALTERNATE_FUNCTION_1 +// All OSP0/OSPI1 pins use the same alternate function. +#define OSPI_PIN_FUNCTION PINMUX_ALTERNATE_FUNCTION_1 -typedef struct _mp_spiflash_t { +typedef struct _ospi_flash_t { + const ospi_pin_settings_t *pin; + const ospi_flash_settings_t *set; ospi_flash_cfg_t cfg; -} mp_spiflash_t; +} ospi_flash_t; -static mp_spiflash_t global_flash; +static ospi_flash_t global_flash; -// Alif version of this function can overwrite the destination buffer. -static void ospi_recv_blocking2(ospi_flash_cfg_t *ospi_cfg, uint32_t command, uint8_t *buffer) { - uint32_t val; +/******************************************************************************/ +// Generic SPI-flash helper functions. - ospi_writel(ospi_cfg, data_reg, command); - ospi_writel(ospi_cfg, ser, ospi_cfg->ser); - - ospi_cfg->rx_cnt = 0; +static void ospi_flash_wren_spi(ospi_flash_t *self) { + uint8_t buf[1] = {CMD_SPI_WREN}; + ospi_spi_transfer(&self->cfg, 1, buf, buf); +} - while (ospi_cfg->rx_cnt < ospi_cfg->rx_req) { - unsigned int timeout = 100000; - while (ospi_readl(ospi_cfg, rxflr) == 0) { - if (--timeout == 0) { - return; - } - } - val = ospi_readl(ospi_cfg, data_reg); - *buffer++ = (uint8_t)val; - ospi_cfg->rx_cnt++; +static int ospi_flash_read_cmd(ospi_flash_t *self, uint32_t cmd, uint8_t cmd_dummy_cycles, size_t len, uint8_t *dest) { + int ret = 0; + if (self->set->inst_len == OSPI_INST_L_8bit) { + ospi_setup_read_ext(&self->cfg, self->set->rxds, self->set->inst_len, OSPI_ADDR_L_0bit, OSPI_DATA_L_8bit, len, cmd_dummy_cycles); + ret = ospi_recv_blocking_8bit_data(&self->cfg, cmd, dest); + } else { + uint16_t dest16 = 0; + ospi_setup_read_ext(&self->cfg, self->set->rxds, self->set->inst_len, OSPI_ADDR_L_32bit, OSPI_DATA_L_16bit, len, cmd_dummy_cycles); + ospi_push(&self->cfg, cmd); + ret = ospi_recv_blocking_16bit_data(&self->cfg, 0 /* addr */, &dest16); + *dest = dest16; } + return ret; } -static int mp_spiflash_read_cmd(mp_spiflash_t *self, uint8_t cmd, size_t len, uint8_t *dest) { - ospi_setup_read(&self->cfg, 0, len, 8); - ospi_recv_blocking2(&self->cfg, cmd, dest); +static int ospi_flash_write_cmd_addr(ospi_flash_t *self, uint32_t cmd, uint32_t addr_len, uint32_t addr) { + if (self->set->inst_len == OSPI_INST_L_8bit) { + ospi_setup_write_ext(&self->cfg, false, self->set->inst_len, addr_len, OSPI_DATA_L_8bit); + } else { + ospi_setup_write_ext(&self->cfg, self->set->rxds, self->set->inst_len, addr_len, OSPI_DATA_L_16bit); + } + if (addr_len == OSPI_ADDR_L_0bit) { + ospi_send_blocking(&self->cfg, cmd); + } else { + ospi_push(&self->cfg, cmd); + ospi_send_blocking(&self->cfg, addr); + } return 0; } -static int mp_spiflash_write_cmd(mp_spiflash_t *self, uint8_t cmd) { - ospi_setup_write(&self->cfg, 0); - ospi_send_blocking(&self->cfg, cmd); - return 0; +static int ospi_flash_write_cmd(ospi_flash_t *self, uint32_t cmd) { + return ospi_flash_write_cmd_addr(self, cmd, OSPI_ADDR_L_0bit, 0); } -static int mp_spiflash_wait_sr(mp_spiflash_t *self, uint8_t mask, uint8_t val, uint32_t timeout) { +static uint32_t ospi_flash_read_id_spi(ospi_flash_t *self) { + uint8_t buf[4] = {0x9f, 0, 0, 0}; + ospi_spi_transfer(&self->cfg, 4, buf, buf); + return buf[1] | buf[2] << 8 | buf[3] << 16; +} + +static int ospi_flash_wait_sr(ospi_flash_t *self, uint8_t mask, uint8_t val, uint32_t timeout) { do { - uint8_t sr; - int ret = mp_spiflash_read_cmd(self, CMD_RDSR, 1, &sr); + uint8_t sr = 0; + int ret = ospi_flash_read_cmd(self, self->set->read_sr, self->set->read_sr_dummy_cycles, 1, &sr); if (ret != 0) { return ret; } @@ -105,144 +117,251 @@ static int mp_spiflash_wait_sr(mp_spiflash_t *self, uint8_t mask, uint8_t val, u return -MP_ETIMEDOUT; } -static int mp_spiflash_wait_wel1(mp_spiflash_t *self) { - return mp_spiflash_wait_sr(self, 2, 2, WAIT_SR_TIMEOUT); +static int ospi_flash_wait_wel1(ospi_flash_t *self) { + return ospi_flash_wait_sr(self, 2, 2, WAIT_SR_TIMEOUT); } -static int mp_spiflash_wait_wip0(mp_spiflash_t *self) { - return mp_spiflash_wait_sr(self, 1, 0, WAIT_SR_TIMEOUT); +static int ospi_flash_wait_wip0(ospi_flash_t *self) { + return ospi_flash_wait_sr(self, 1, 0, WAIT_SR_TIMEOUT); } -static uint32_t ospi_flash_read_id(mp_spiflash_t *self) { - uint8_t buf[8]; - ospi_setup_read(&self->cfg, 0, 3, ospi_flash_settings.read_id_dummy_cycles); - ospi_recv_blocking2(&self->cfg, CMD_RD_DEVID, buf); +static uint32_t ospi_flash_read_id(ospi_flash_t *self) { + uint8_t buf[4] = {0}; + if (self->set->inst_len == OSPI_INST_L_8bit) { + ospi_setup_read_ext(&self->cfg, self->set->rxds, self->set->inst_len, OSPI_ADDR_L_0bit, OSPI_DATA_L_8bit, 3, self->set->read_id_dummy_cycles); + ospi_recv_blocking_8bit_data(&self->cfg, self->set->read_id, buf); + } else { + ospi_setup_read_ext(&self->cfg, self->set->rxds, self->set->inst_len, OSPI_ADDR_L_32bit, OSPI_DATA_L_16bit, 4, self->set->read_id_dummy_cycles); + ospi_push(&self->cfg, self->set->read_id); + // Read 8-bit values because data is in SDR mode for read id. + ospi_recv_blocking_8bit_data(&self->cfg, 0, buf); + } return buf[0] | buf[1] << 8 | buf[2] << 16; } -static void ospi_flash_write_reg_sdr(mp_spiflash_t *self, uint8_t cmd, uint8_t addr, uint8_t value) { - mp_spiflash_write_cmd(self, CMD_WREN); - ospi_setup_write_sdr(&self->cfg, 6); - ospi_push(&self->cfg, cmd); - ospi_push(&self->cfg, 0x00); - ospi_push(&self->cfg, 0x00); +/******************************************************************************/ +// Functions specific to ISSI flash chips. + +int ospi_flash_issi_octal_switch(ospi_flash_t *self) { + // Switch SPI flash to Octal DDR mode. + const uint8_t cmd_wrvol = 0x81; + const uint8_t issi_mode_octal_ddr_dqs = 0xe7; + ospi_flash_wren_spi(self); + uint8_t buf[5] = {cmd_wrvol, 0, 0, 0, issi_mode_octal_ddr_dqs}; + ospi_spi_transfer(&self->cfg, sizeof(buf), buf, buf); + self->cfg.ddr_en = 1; + return 0; +} + +/******************************************************************************/ +// Functions specific to MX flash chips. + +int ospi_flash_mx_octal_switch(ospi_flash_t *self) { + // Switch SPI flash to Octal SDR or DDR mode (SOPI or DOPI) by writing to CR2. + const uint8_t cmd_wrcr2 = 0x72; + const uint8_t mx_mode_enable_sopi = 0x01; + const uint8_t mx_mode_enable_dopi = 0x02; + uint8_t mx_mode; + if (self->set->octal_mode == OSPI_FLASH_OCTAL_MODE_DDD) { + mx_mode = mx_mode_enable_dopi; + } else { + mx_mode = mx_mode_enable_sopi; + } + ospi_flash_wren_spi(self); + uint8_t buf[6] = {cmd_wrcr2, 0, 0, 0, 0, mx_mode}; + ospi_spi_transfer(&self->cfg, sizeof(buf), buf, buf); + if (self->set->octal_mode == OSPI_FLASH_OCTAL_MODE_DDD) { + self->cfg.ddr_en = 1; + } else { + self->cfg.ddr_en = 0; + } + return 0; +} + +static uint8_t ospi_flash_mx_read_cr_helper(ospi_flash_t *self, uint16_t command, uint32_t addr) { + // TODO: currently only works in DDR mode + + uint16_t buf[1] = {0}; + ospi_setup_read_ext(&self->cfg, self->set->rxds, OSPI_INST_L_16bit, OSPI_ADDR_L_32bit, OSPI_DATA_L_16bit, 1, 4); + ospi_push(&self->cfg, command); + ospi_recv_blocking_16bit_data(&self->cfg, addr, buf); + return buf[0] & 0xff; +} + +uint8_t ospi_flash_mx_read_cr(ospi_flash_t *self) { + return ospi_flash_mx_read_cr_helper(self, 0x15ea, 0); +} + +uint8_t ospi_flash_mx_read_cr2(ospi_flash_t *self, uint32_t addr) { + return ospi_flash_mx_read_cr_helper(self, 0x718e, addr); +} + +static int ospi_flash_mx_write_cr_helper(ospi_flash_t *self, uint16_t command, uint32_t addr, uint8_t value) { + // TODO: currently only works in DDR mode + + // Enable writes so that the register can be modified. + ospi_flash_write_cmd(self, self->set->write_en); + int ret = ospi_flash_wait_wel1(self); + if (ret < 0) { + return ret; + } + + // Do the write. + ospi_setup_write_ext(&self->cfg, self->set->rxds, OSPI_INST_L_16bit, OSPI_ADDR_L_32bit, OSPI_DATA_L_16bit); + ospi_push(&self->cfg, command); ospi_push(&self->cfg, addr); - ospi_send_blocking(&self->cfg, value); + ospi_push(&self->cfg, value << 8); // in DDR mode, MSByte contains the register value to write + ospi_writel((&self->cfg), ser, self->cfg.ser); + while ((ospi_readl((&self->cfg), sr) & (SR_TF_EMPTY | SR_BUSY)) != SR_TF_EMPTY) { + } + + // Wait for the write to finish. + return ospi_flash_wait_wip0(self); +} + +int ospi_flash_mx_write_cr(ospi_flash_t *self, uint8_t value) { + return ospi_flash_mx_write_cr_helper(self, 0x01fe, 1, value); } +int ospi_flash_mx_write_cr2(ospi_flash_t *self, uint32_t addr, uint8_t value) { + return ospi_flash_mx_write_cr_helper(self, 0x728d, addr, value); +} + +/******************************************************************************/ +// SPI flash initialisation. + int ospi_flash_init(void) { - mp_spiflash_t *self = &global_flash; + ospi_flash_t *self = &global_flash; + + const ospi_pin_settings_t *pin = &ospi_pin_settings; + const ospi_flash_settings_t *set = &ospi_flash_settings; + self->pin = pin; + self->set = set; uint32_t pad_ctrl = PADCTRL_OUTPUT_DRIVE_STRENGTH_12MA | PADCTRL_SLEW_RATE_FAST | PADCTRL_READ_ENABLE; - pinconf_set(pin_OSPI1_CS->port, pin_OSPI1_CS->pin, OSPI1_PIN_FUNCTION, PADCTRL_OUTPUT_DRIVE_STRENGTH_12MA); - pinconf_set(pin_OSPI1_SCLK->port, pin_OSPI1_SCLK->pin, OSPI1_PIN_FUNCTION, PADCTRL_OUTPUT_DRIVE_STRENGTH_12MA | PADCTRL_SLEW_RATE_FAST); - pinconf_set(pin_OSPI1_D0->port, pin_OSPI1_D0->pin, OSPI1_PIN_FUNCTION, pad_ctrl); - pinconf_set(pin_OSPI1_D1->port, pin_OSPI1_D1->pin, OSPI1_PIN_FUNCTION, pad_ctrl); - pinconf_set(pin_OSPI1_D2->port, pin_OSPI1_D2->pin, OSPI1_PIN_FUNCTION, pad_ctrl); - pinconf_set(pin_OSPI1_D3->port, pin_OSPI1_D3->pin, OSPI1_PIN_FUNCTION, pad_ctrl); - #if defined(pin_OSPI1_D4) - pinconf_set(pin_OSPI1_D4->port, pin_OSPI1_D4->pin, OSPI1_PIN_FUNCTION, pad_ctrl); - pinconf_set(pin_OSPI1_D5->port, pin_OSPI1_D5->pin, OSPI1_PIN_FUNCTION, pad_ctrl); - pinconf_set(pin_OSPI1_D6->port, pin_OSPI1_D6->pin, OSPI1_PIN_FUNCTION, pad_ctrl); - pinconf_set(pin_OSPI1_D7->port, pin_OSPI1_D7->pin, OSPI1_PIN_FUNCTION, pad_ctrl); - #endif - #if defined(pin_OSPI1_RXDS) - pinconf_set(pin_OSPI1_RXDS->port, pin_OSPI1_RXDS->pin, OSPI1_PIN_FUNCTION, pad_ctrl); - #endif - - #if defined(pin_OSPI1_RXDS) - if (pin_OSPI1_RXDS->port == PORT_10 && pin_OSPI1_RXDS->pin == PIN_7) { - // Alif: P5_6 is needed to support proper alt function selection of P10_7. - pinconf_set(PORT_5, PIN_6, OSPI1_PIN_FUNCTION, pad_ctrl); + pinconf_set(pin->pin_cs->port, pin->pin_cs->pin, OSPI_PIN_FUNCTION, PADCTRL_OUTPUT_DRIVE_STRENGTH_12MA); + pinconf_set(pin->pin_clk->port, pin->pin_clk->pin, OSPI_PIN_FUNCTION, PADCTRL_OUTPUT_DRIVE_STRENGTH_12MA | PADCTRL_SLEW_RATE_FAST); + if (pin->pin_rwds != NULL) { + pinconf_set(pin->pin_rwds->port, pin->pin_rwds->pin, OSPI_PIN_FUNCTION, pad_ctrl); + if (pin->pin_rwds->port == PORT_10 && pin->pin_rwds->pin == PIN_7) { + // Alif: P5_6 is needed to support proper alt function selection of P10_7. + pinconf_set(PORT_5, PIN_6, OSPI_PIN_FUNCTION, pad_ctrl); + } + } + pinconf_set(pin->pin_d0->port, pin->pin_d0->pin, OSPI_PIN_FUNCTION, pad_ctrl); + pinconf_set(pin->pin_d1->port, pin->pin_d1->pin, OSPI_PIN_FUNCTION, pad_ctrl); + pinconf_set(pin->pin_d2->port, pin->pin_d2->pin, OSPI_PIN_FUNCTION, pad_ctrl); + pinconf_set(pin->pin_d3->port, pin->pin_d3->pin, OSPI_PIN_FUNCTION, pad_ctrl); + if (pin->pin_d4 != NULL) { + pinconf_set(pin->pin_d4->port, pin->pin_d4->pin, OSPI_PIN_FUNCTION, pad_ctrl); + pinconf_set(pin->pin_d5->port, pin->pin_d5->pin, OSPI_PIN_FUNCTION, pad_ctrl); + pinconf_set(pin->pin_d6->port, pin->pin_d6->pin, OSPI_PIN_FUNCTION, pad_ctrl); + pinconf_set(pin->pin_d7->port, pin->pin_d7->pin, OSPI_PIN_FUNCTION, pad_ctrl); } - #endif // Reset the SPI flash. - mp_hal_pin_output(pin_OSPI1_RESET); - mp_hal_pin_low(pin_OSPI1_RESET); - mp_hal_delay_us(30); - mp_hal_pin_high(pin_OSPI1_RESET); + mp_hal_pin_output(pin->pin_reset); + mp_hal_pin_low(pin->pin_reset); + mp_hal_delay_us(100); + mp_hal_pin_high(pin->pin_reset); + mp_hal_delay_us(1000); // Configure the OSPI peripheral. - self->cfg.regs = (ssi_regs_t *)OSPI1_BASE; - self->cfg.aes_regs = (aes_regs_t *)AES1_BASE; - self->cfg.xip_base = (volatile void *)OSPI1_XIP_BASE; - self->cfg.ser = 1; + if (pin->peripheral_number == 0) { + self->cfg.regs = (ssi_regs_t *)OSPI0_BASE; + self->cfg.aes_regs = (aes_regs_t *)AES0_BASE; + self->cfg.xip_base = (volatile void *)OSPI0_XIP_BASE; + } else { + self->cfg.regs = (ssi_regs_t *)OSPI1_BASE; + self->cfg.aes_regs = (aes_regs_t *)AES1_BASE; + self->cfg.xip_base = (volatile void *)OSPI1_XIP_BASE; + } + self->cfg.ser = 1; // enable slave select self->cfg.addrlen = 8; // 32-bit address length - self->cfg.ospi_clock = ospi_flash_settings.freq_mhz; + self->cfg.ospi_clock = set->freq_hz; self->cfg.ddr_en = 0; self->cfg.wait_cycles = 0; // used only for ospi_xip_exit ospi_init(&self->cfg); - if (ospi_flash_settings.is_oct && ospi_flash_settings.is_ddr) { - // Switch SPI flash to Octal DDR mode. - ospi_flash_write_reg_sdr(self, CMD_WRVOL, 0x00, ISSI_MODE_OCTAL_DDR_DQS); - self->cfg.ddr_en = 1; + // Check the device ID before attempting to switch to octal mode (if needed). + if (ospi_flash_read_id_spi(self) != set->jedec_id) { + return -1; } - // Check the device ID. - if (ospi_flash_read_id(self) != ospi_flash_settings.jedec_id) { - return -1; + // Switch to octal mode if needed. + if (set->octal_switch != NULL) { + set->octal_switch(self); + + // Check the device ID after switching mode. + if (ospi_flash_read_id(self) != set->jedec_id) { + return -1; + } } return 0; } +/******************************************************************************/ +// Top-level read/erase/write functions. + int ospi_flash_erase_sector(uint32_t addr) { - mp_spiflash_t *self = &global_flash; + ospi_flash_t *self = &global_flash; - mp_spiflash_write_cmd(self, CMD_WREN); - int ret = mp_spiflash_wait_wel1(self); + ospi_flash_write_cmd(self, self->set->write_en); + int ret = ospi_flash_wait_wel1(self); if (ret < 0) { return ret; } - ospi_setup_write(&self->cfg, 8 /* 32-bit addr len*/); - ospi_push(&self->cfg, CMD_SEC_ERASE_32ADDR); - ospi_send_blocking(&self->cfg, addr); + ospi_flash_write_cmd_addr(self, self->set->erase_command, OSPI_ADDR_L_32bit, addr); - return mp_spiflash_wait_wip0(self); + return ospi_flash_wait_wip0(self); } int ospi_flash_read(uint32_t addr, uint32_t len, uint8_t *dest) { // OSPI FIFO is limited to 256 bytes. Need DMA to get a longer read. - mp_spiflash_t *self = &global_flash; + ospi_flash_t *self = &global_flash; while (len) { - uint32_t l = len; + uint32_t l = len / 4; if (l > 256) { l = 256; } - ospi_setup_read(&self->cfg, 8 /* 32-bit addr len*/, l, ospi_flash_settings.read_dummy_cycles); - ospi_push(&self->cfg, ospi_flash_settings.read_command); - ospi_recv_blocking2(&self->cfg, addr, dest); - addr += l; - len -= l; - dest += l; + ospi_setup_read_ext(&self->cfg, self->set->rxds, self->set->inst_len, OSPI_ADDR_L_32bit, OSPI_DATA_L_32bit, l, self->set->read_dummy_cycles); + ospi_push(&self->cfg, self->set->read_command); + ospi_recv_blocking_32bit_data(&self->cfg, addr, (uint32_t *)dest); + addr += l * 4; + len -= l * 4; + dest += l * 4; } return 0; } static int ospi_flash_write_page(uint32_t addr, uint32_t len, const uint8_t *src) { - mp_spiflash_t *self = &global_flash; + ospi_flash_t *self = &global_flash; - mp_spiflash_write_cmd(self, CMD_WREN); - int ret = mp_spiflash_wait_wel1(self); + ospi_flash_write_cmd(self, self->set->write_en); + int ret = ospi_flash_wait_wel1(self); if (ret < 0) { return ret; } - ospi_setup_write(&self->cfg, 8 /* 32-bit addr len*/); - ospi_push(&self->cfg, ospi_flash_settings.write_command); + ospi_setup_write_ext(&self->cfg, self->set->rxds, self->set->inst_len, OSPI_ADDR_L_32bit, OSPI_DATA_L_32bit); + ospi_push(&self->cfg, self->set->write_command); ospi_push(&self->cfg, addr); - while (--len) { - ospi_push(&self->cfg, *src++); + + const uint32_t *src32 = (const uint32_t *)src; + for (; len; len -= 4) { + ospi_push(&self->cfg, __ROR(*src32++, 16)); } - ospi_send_blocking(&self->cfg, *src); - return mp_spiflash_wait_wip0(self); + ospi_writel((&self->cfg), ser, self->cfg.ser); + while ((ospi_readl((&self->cfg), sr) & (SR_TF_EMPTY | SR_BUSY)) != SR_TF_EMPTY) { + } + + return ospi_flash_wait_wip0(self); } int ospi_flash_write(uint32_t addr, uint32_t len, const uint8_t *src) { @@ -264,4 +383,5 @@ int ospi_flash_write(uint32_t addr, uint32_t len, const uint8_t *src) { } return ret; } + #endif // MICROPY_HW_ENABLE_OSPI diff --git a/ports/alif/ospi_flash.h b/ports/alif/ospi_flash.h index 945dc4bb0..6f5d8327b 100644 --- a/ports/alif/ospi_flash.h +++ b/ports/alif/ospi_flash.h @@ -26,24 +26,67 @@ #ifndef MICROPY_INCLUDED_ALIF_OSPI_FLASH_H #define MICROPY_INCLUDED_ALIF_OSPI_FLASH_H -#include <stdbool.h> -#include <stdint.h> +#include "py/mphal.h" + +// Format of command, address and data phases. +enum { + OSPI_FLASH_OCTAL_MODE_SSS, + OSPI_FLASH_OCTAL_MODE_SDD, + OSPI_FLASH_OCTAL_MODE_DDD, +}; + +struct _ospi_flash_t; + +typedef struct _ospi_pin_settings_t { + uint32_t peripheral_number; + const mp_hal_pin_obj_t pin_reset; + const mp_hal_pin_obj_t pin_cs; + const mp_hal_pin_obj_t pin_clk; + const mp_hal_pin_obj_t pin_rwds; + const mp_hal_pin_obj_t pin_d0; + const mp_hal_pin_obj_t pin_d1; + const mp_hal_pin_obj_t pin_d2; + const mp_hal_pin_obj_t pin_d3; + const mp_hal_pin_obj_t pin_d4; + const mp_hal_pin_obj_t pin_d5; + const mp_hal_pin_obj_t pin_d6; + const mp_hal_pin_obj_t pin_d7; +} ospi_pin_settings_t; typedef struct _ospi_flash_settings_t { uint32_t jedec_id; - uint32_t freq_mhz; - bool is_quad : 1; - bool is_oct : 1; - bool is_ddr : 1; + uint32_t freq_hz; + int (*octal_switch)(struct _ospi_flash_t *); + uint8_t octal_mode; + bool rxds; + uint8_t inst_len; + uint8_t xip_data_len; + uint16_t read_sr; + uint8_t read_sr_dummy_cycles; + uint16_t read_id; uint8_t read_id_dummy_cycles; + uint16_t write_en; + uint16_t read_command; uint8_t read_dummy_cycles; - uint8_t read_command; - uint8_t write_command; + uint16_t write_command; + uint16_t erase_command; } ospi_flash_settings_t; -// Provided by the board when it enables OSPI1. +// Provided by the board when it enables OSPI. +extern const ospi_pin_settings_t ospi_pin_settings; extern const ospi_flash_settings_t ospi_flash_settings; +// Functions specific to ISSI flash chips. +int ospi_flash_issi_octal_switch(struct _ospi_flash_t *self); + +// Functions specific to MX flash chips. +int ospi_flash_mx_octal_switch(struct _ospi_flash_t *self); +uint8_t ospi_flash_mx_read_cr(struct _ospi_flash_t *self); +uint8_t ospi_flash_mx_read_cr2(struct _ospi_flash_t *self, uint32_t addr); +int ospi_flash_mx_write_cr(struct _ospi_flash_t *self, uint8_t value); +int ospi_flash_mx_write_cr2(struct _ospi_flash_t *self, uint32_t addr, uint8_t value); + +// SPI flash interface. int ospi_flash_init(void); int ospi_flash_erase_sector(uint32_t addr); int ospi_flash_read(uint32_t addr, uint32_t len, uint8_t *dest); diff --git a/ports/alif/ospi_xip_user.h b/ports/alif/ospi_xip_user.h deleted file mode 100644 index a15baff2a..000000000 --- a/ports/alif/ospi_xip_user.h +++ /dev/null @@ -1,5 +0,0 @@ -// This file is needed by ospi_xip/source/ospi/ospi_drv.c. -#define OSPI_XIP_ENABLE_AES_DECRYPTION (0) -#define OSPI_XIP_RX_SAMPLE_DELAY (4) -#define OSPI_XIP_DDR_DRIVE_EDGE (1) -#define OSPI_XIP_RXDS_DELAY (12) |
