alif/ospi_flash: Generalise flash driver to support MX chips.
Signed-off-by: Damien George <damien@micropython.org>
This commit is contained in:
@@ -122,6 +122,7 @@ SRC_C = \
|
||||
mpu.c \
|
||||
mpuart.c \
|
||||
msc_disk.c \
|
||||
ospi_ext.c \
|
||||
ospi_flash.c \
|
||||
pendsv.c \
|
||||
system_tick.c \
|
||||
|
||||
292
ports/alif/ospi_ext.c
Normal file
292
ports/alif/ospi_ext.c
Normal file
@@ -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);
|
||||
}
|
||||
57
ports/alif/ospi_ext.h
Normal file
57
ports/alif/ospi_ext.h
Normal file
@@ -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
|
||||
@@ -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)
|
||||
// Generic SPI flash commands.
|
||||
#define CMD_SPI_WREN (0x06)
|
||||
|
||||
// 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)
|
||||
// This is the maximum number of bytes that can be written to SPI flash at once.
|
||||
#define PAGE_SIZE (256)
|
||||
|
||||
#define ISSI_MODE_OCTAL_DDR_DQS (0xe7)
|
||||
// All OSP0/OSPI1 pins use the same alternate function.
|
||||
#define OSPI_PIN_FUNCTION PINMUX_ALTERNATE_FUNCTION_1
|
||||
|
||||
// All OSPI1 pins use the same alternate function.
|
||||
#define OSPI1_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);
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
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);
|
||||
ospi_push(&self->cfg, addr);
|
||||
ospi_send_blocking(&self->cfg, value);
|
||||
/******************************************************************************/
|
||||
// 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_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;
|
||||
}
|
||||
|
||||
int ospi_flash_erase_sector(uint32_t addr) {
|
||||
mp_spiflash_t *self = &global_flash;
|
||||
/******************************************************************************/
|
||||
// Top-level read/erase/write functions.
|
||||
|
||||
mp_spiflash_write_cmd(self, CMD_WREN);
|
||||
int ret = mp_spiflash_wait_wel1(self);
|
||||
int ospi_flash_erase_sector(uint32_t addr) {
|
||||
ospi_flash_t *self = &global_flash;
|
||||
|
||||
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++);
|
||||
}
|
||||
ospi_send_blocking(&self->cfg, *src);
|
||||
|
||||
return mp_spiflash_wait_wip0(self);
|
||||
const uint32_t *src32 = (const uint32_t *)src;
|
||||
for (; len; len -= 4) {
|
||||
ospi_push(&self->cfg, __ROR(*src32++, 16));
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
Reference in New Issue
Block a user