Merge pull request #14221 from macronix/macronix_rww

Enable the RWW function of Macronix Flash MX25LW51245G in OSPI block device driver
pull/14733/head
Martin Kojtal 2021-06-02 16:53:59 +02:00 committed by GitHub
commit 5fe4dafc34
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 316 additions and 38 deletions

View File

@ -17,7 +17,6 @@
#ifndef MBED_OSPI_FLASH_MX25LM51245G_H
#define MBED_OSPI_FLASH_MX25LM51245G_H
#define OSPI_FLASH_CHIP_STRING "macronix MX25LM51245G"
// This is a workaround,
@ -26,20 +25,13 @@
// The code below can be removed when users test with the new flash.
#define NEED_DEFINE_SFDP_PARA
#ifdef NEED_DEFINE_SFDP_PARA
uint8_t _sfdp_head_table[32] = {0x53, 0x46, 0x44, 0x50, 0x06, 0x01, 0x02, 0xFF, 0x00, 0x06, 0x01,
0x10, 0x30, 0x00, 0x00, 0xFF, 0xC2, 0x00, 0x01, 0x04, 0x10, 0x01,
0x00, 0xFF, 0x84, 0x00, 0x01, 0x02, 0xC0, 0x00, 0x00, 0xFF
};
uint8_t _sfdp_basic_param_table[64] = {0x30, 0xFF, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x14, 0xEC,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0C, 0x20,
0x10, 0xDC, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0x50, 0xF9, 0x80
};
uint8_t _sfdp_4_byte_inst_table[8] = {0x7F, 0xEF, 0xFF, 0xFF, 0x21, 0x5C, 0xDC, 0x14};
#endif
#define OSPIF_CR2_OPI_EN_ADDR 0x00000000
#define MX_FLASH_BLOCK_SIZE 0x10000 /* 1024 blocks of 64 KBytes */
#define MX_FLASH_SECTOR_SIZE 0x1000 /* 16384 sectors of 4 kBytes */
#define MX_FLASH_PAGE_SIZE 0x100 /* 262144 pages of 256 bytes */
#define MX_FLASH_CHUNK_SIZE 0x10 /* 16 bytes */
#define MX_FLASH_BANK_SIZE 0x01000000 /* 16 MBytes */
#define MX_FLASH_BANK_SIZE_MASK ~(MX_FLASH_BANK_SIZE - 1) /* 0xFF000000 */
#endif // MBED_OSPI_FLASH_MX25LM51245G_H

View File

@ -0,0 +1,44 @@
/* mbed Microcontroller Library
* Copyright (c) 2020 ARM Limited
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef MBED_OSPI_FLASH_MX25LW51245G_H
#define MBED_OSPI_FLASH_MX25LW51245G_H
#define OSPI_FLASH_CHIP_STRING "macronix MX25LW51245G"
// This is a workaround,
// The sfdp parameter values in Macronix old octaflash(include the MX25LW51245G on L4R9I_DISCO) are all 0xFF,
// so we need to define the parameter values by software to support SFDP parsing.
// The code below can be removed when users test with the new flash.
#define NEED_DEFINE_SFDP_PARA
#define MX_FLASH_SUPPORT_RWW 1
// Configuration Register2 address
#define OSPIF_CR2_OPI_EN_ADDR 0x00000000
#define OSPIF_CR2_BANK_STATUS_ADDR 0xc0000000
#define OSPIF_CR2_RWWDI ((uint8_t)0x00) /*!< No active program or erase operation */
#define OSPIF_CR2_RWWDS ((uint8_t)0x01) /*!< Program/erase in other bank */
#define OSPIF_CR2_RWWBS ((uint8_t)0x03) /*!< program/erase operation in addressed bank */
#define MX_FLASH_BLOCK_SIZE 0x10000 /* 1024 blocks of 64 KBytes */
#define MX_FLASH_SECTOR_SIZE 0x1000 /* 16384 sectors of 4 kBytes */
#define MX_FLASH_PAGE_SIZE 0x100 /* 262144 pages of 256 bytes */
#define MX_FLASH_CHUNK_SIZE 0x10 /* 16 bytes */
#define MX_FLASH_BANK_SIZE 0x01000000 /* 16 MBytes */
#define MX_FLASH_BANK_SIZE_MASK ~(MX_FLASH_BANK_SIZE - 1) /* 0xFF000000 */
#endif // MBED_OSPI_FLASH_MX25LW51245G_H

View File

@ -22,6 +22,14 @@
#include "blockdevice/BlockDevice.h"
#include "platform/Callback.h"
#if defined(TARGET_MX25LM51245G)
#include "MX25LM51245G_config.h"
#endif
#if defined(TARGET_MX25LW51245G)
#include "MX25LW51245G_config.h"
#endif
#ifndef MBED_CONF_OSPIF_OSPI_IO0
#define MBED_CONF_OSPIF_OSPI_IO0 NC
#endif
@ -381,6 +389,10 @@ private:
// Detect 4-byte addressing mode and enable it if supported
int _sfdp_detect_and_enable_4byte_addressing(uint8_t *basic_param_table_ptr, int basic_param_table_size);
#ifdef MX_FLASH_SUPPORT_RWW
bool _is_mem_ready_rww(bd_addr_t addr, uint8_t rw);
#endif
private:
enum ospif_clear_protection_method_t {
OSPIF_BP_ULBPR, // Issue global protection unlock instruction
@ -449,6 +461,16 @@ private:
uint32_t _init_ref_count;
bool _is_initialized;
#ifdef MX_FLASH_SUPPORT_RWW
enum wait_flag {
NOT_STARTED, // no wait is started
WRITE_WAIT_STARTED, // write wait is started
ERASE_WAIT_STARTED, // erase wait is started
};
uint32_t _busy_bank; // Current busy bank
wait_flag _wait_flag; // wait flag
PlatformMutex _busy_mutex;
#endif
};
#endif

View File

@ -20,9 +20,6 @@
#include "OSPIFBlockDevice.h"
#include <string.h>
#include "rtos/ThisThread.h"
#if defined(TARGET_MX25LM51245G)
#include "MX25LM51245G_config.h"
#endif
#ifndef MBED_CONF_MBED_TRACE_ENABLE
#define MBED_CONF_MBED_TRACE_ENABLE 0
@ -51,9 +48,6 @@ using namespace mbed;
#define OSPIF_STATUS_BIT_WEL 0x2 // Write Enable Latch
#define OSPIF_NO_QUAD_ENABLE (-1)
// Configuration Register2 address
#define OSPIF_CR2_OPI_EN_ADDR 0x00000000
/* SFDP Header Parsing */
/***********************/
#define OSPIF_RSFDP_DUMMY_CYCLES 8
@ -168,6 +162,21 @@ using namespace mbed;
// Length of data returned from RDID instruction
#define OSPI_RDID_DATA_LENGTH 3
#ifdef NEED_DEFINE_SFDP_PARA
static const uint8_t _sfdp_head_table[32] = {0x53, 0x46, 0x44, 0x50, 0x06, 0x01, 0x02, 0xFF, 0x00, 0x06, 0x01,
0x10, 0x30, 0x00, 0x00, 0xFF, 0xC2, 0x00, 0x01, 0x04, 0x10, 0x01,
0x00, 0xFF, 0x84, 0x00, 0x01, 0x02, 0xC0, 0x00, 0x00, 0xFF
};
static const uint8_t _sfdp_basic_param_table[64] = {0x30, 0xFF, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x14, 0xEC,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0C, 0x20,
0x10, 0xDC, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0x50, 0xF9, 0x80
};
static const uint8_t _sfdp_4_byte_inst_table[8] = {0x7F, 0xEF, 0xFF, 0xFF, 0x21, 0x5C, 0xDC, 0x14};
#endif
/* Init function to initialize Different Devices CS static list */
static PinName *generate_initialized_active_ospif_csel_arr();
@ -232,6 +241,11 @@ OSPIFBlockDevice::OSPIFBlockDevice(PinName io0, PinName io1, PinName io2, PinNam
_attempt_4_byte_addressing = true;
_4byte_msb_reg_write_inst = OSPIF_INST_4BYTE_REG_WRITE_DEFAULT;
_support_4_byte_inst = false;
#ifdef MX_FLASH_SUPPORT_RWW
_wait_flag = NOT_STARTED;
_busy_bank = 0xffffffff;
#endif
}
int OSPIFBlockDevice::init()
@ -358,6 +372,14 @@ int OSPIFBlockDevice::deinit()
return result;
}
if (false == _is_mem_ready()) {
tr_error("Device not ready after write, failed");
}
#ifdef MX_FLASH_SUPPORT_RWW
_wait_flag = NOT_STARTED;
#endif
change_mode(SPI);
// Disable Device for Writing
@ -383,6 +405,29 @@ int OSPIFBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size)
int status = OSPIF_BD_ERROR_OK;
tr_debug("Read Inst: 0x%xh", _read_instruction);
#ifdef MX_FLASH_SUPPORT_RWW
bool need_wait;
need_wait = (_wait_flag != NOT_STARTED) && ((addr & MX_FLASH_BANK_SIZE_MASK) == _busy_bank);
// Wait for ready
if (need_wait) {
_busy_mutex.lock();
if (_is_mem_ready_rww(addr, false) == false) {
return OSPIF_BD_ERROR_OK;
}
} else {
if (_wait_flag == WRITE_WAIT_STARTED) {
tr_debug("\r\n RWW1 CNT");
} else if (_wait_flag == ERASE_WAIT_STARTED) {
tr_debug("\r\n RWE2 CNT");
}
}
#endif
_mutex.lock();
// In DOPI mode, the number of read data should be even
@ -397,8 +442,13 @@ int OSPIFBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size)
_mutex.unlock();
return status;
#ifdef MX_FLASH_SUPPORT_RWW
if (need_wait) {
_busy_mutex.unlock();
}
#endif
return status;
}
int OSPIFBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t size)
@ -418,6 +468,16 @@ int OSPIFBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t size
chunk = (offset + size < _page_size_bytes) ? size : (_page_size_bytes - offset);
written_bytes = chunk;
#ifdef MX_FLASH_SUPPORT_RWW
_busy_mutex.lock();
// Wait for ready
if (_is_mem_ready_rww(addr, true) == false) {
return OSPIF_BD_ERROR_OK;
}
#endif
_mutex.lock();
//Send WREN
@ -437,10 +497,14 @@ int OSPIFBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t size
goto exit_point;
}
buffer = static_cast<const uint8_t *>(buffer) + chunk;
addr += chunk;
size -= chunk;
#ifdef MX_FLASH_SUPPORT_RWW
_wait_flag = WRITE_WAIT_STARTED;
_busy_bank = addr & MX_FLASH_BANK_SIZE_MASK;
_mutex.unlock();
_busy_mutex.unlock();
#else
if (false == _is_mem_ready()) {
tr_error("Device not ready after write, failed");
program_failed = true;
@ -448,6 +512,10 @@ int OSPIFBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t size
goto exit_point;
}
_mutex.unlock();
#endif
buffer = static_cast<const uint8_t *>(buffer) + chunk;
addr += chunk;
size -= chunk;
}
exit_point:
@ -511,6 +579,15 @@ int OSPIFBlockDevice::erase(bd_addr_t addr, bd_size_t size)
tr_debug("Erase - Region: %d, Type:%d ",
region, type);
#ifdef MX_FLASH_SUPPORT_RWW
_busy_mutex.lock();
// Wait for ready
if (_is_mem_ready_rww(addr, true) == false) {
return OSPIF_BD_ERROR_OK;
}
#endif
_mutex.lock();
if (_set_write_enable() != 0) {
@ -527,15 +604,14 @@ int OSPIFBlockDevice::erase(bd_addr_t addr, bd_size_t size)
goto exit_point;
}
addr += eu_size;
size -= eu_size;
#ifdef MX_FLASH_SUPPORT_RWW
_wait_flag = ERASE_WAIT_STARTED;
_busy_bank = addr & MX_FLASH_BANK_SIZE_MASK;
if ((size > 0) && (addr > _sfdp_info.smptbl.region_high_boundary[region])) {
// erase crossed to next region
region++;
bitfield = _sfdp_info.smptbl.region_erase_types_bitfld[region];
}
_mutex.unlock();
_busy_mutex.unlock();
#else
if (false == _is_mem_ready()) {
tr_error("OSPI After Erase Device not ready - failed");
erase_failed = true;
@ -544,6 +620,16 @@ int OSPIFBlockDevice::erase(bd_addr_t addr, bd_size_t size)
}
_mutex.unlock();
#endif
addr += eu_size;
size -= eu_size;
if ((size > 0) && (addr > _sfdp_info.smptbl.region_high_boundary[region])) {
// erase crossed to next region
region++;
bitfield = _sfdp_info.smptbl.region_erase_types_bitfld[region];
}
}
exit_point:
@ -1537,6 +1623,51 @@ bool OSPIFBlockDevice::_is_mem_ready()
return mem_ready;
}
#ifdef MX_FLASH_SUPPORT_RWW
bool OSPIFBlockDevice::_is_mem_ready_rww(bd_addr_t addr, uint8_t rw)
{
uint16_t cr2_value = 0;
bool mem_ready = true;
static uint32_t rww_cnt = 0; // For testing
static uint32_t rwe_cnt = 0; // For testing
bd_addr_t bank_addr = addr & MX_FLASH_BANK_SIZE_MASK;
if ((_wait_flag == NOT_STARTED) || (!rw && bank_addr != _busy_bank)) {
return mem_ready;
}
//Read CR2 Register 1 from device, the number of read byte need to be even in octa flash DOPI mode
if (OSPI_STATUS_OK != _ospi_send_general_command(OSPIF_INST_RDCR2, bank_addr + OSPIF_CR2_BANK_STATUS_ADDR,
NULL, 0,
(char *) &cr2_value, OSPI_DEFAULT_STATUS_REGISTERS)) { // store received value in cr2_value
tr_error("Reading CR2 Register failed");
}
cr2_value &= OSPIF_CR2_RWWBS;
if ((cr2_value == OSPIF_CR2_RWWBS) || (rw && (cr2_value == OSPIF_CR2_RWWDS))) {
// Wait until device ready
if (false == _is_mem_ready()) {
tr_error(" _is_mem_ready Failed");
mem_ready = false;
}
_wait_flag = NOT_STARTED;
} else if (!rw && (cr2_value == OSPIF_CR2_RWWDS)) {
// For testing
if (_wait_flag == WRITE_WAIT_STARTED) {
rww_cnt++;
tr_debug("rww_cnt = 0x%x ", rww_cnt);
} else {
rwe_cnt++;
tr_debug("rwe_cnt = 0x%x ", rwe_cnt);
}
}
return mem_ready;
}
#endif
/***************************************************/
/*********** OSPI Driver API Functions *************/
/***************************************************/
@ -1665,9 +1796,12 @@ ospi_status_t OSPIFBlockDevice::_ospi_send_general_command(ospi_inst_t instructi
if ((_inst_width == OSPI_CFG_BUS_OCTA) || (_inst_width == OSPI_CFG_BUS_OCTA_DTR)) {
if ((instruction == OSPIF_INST_RSR1) || (instruction == OSPIF_INST_RDID) ||
(instruction == OSPIF_INST_RDCR2) || (instruction == OSPIF_INST_RDCR)) {
_ospi.configure_format(_inst_width, _inst_size, _address_width, _address_size, OSPI_CFG_BUS_SINGLE, 0, _data_width, _dummy_cycles);
addr = 0;
} else if (instruction == OSPIF_INST_WSR1) {
_ospi.configure_format(_inst_width, _inst_size, _address_width, _address_size, OSPI_CFG_BUS_SINGLE,
0, _data_width, _dummy_cycles);
if (instruction != OSPIF_INST_RDCR2) {
addr = 0;
}
} else if ((instruction == OSPIF_INST_WSR1)) {
addr = 0;
}
}

View File

@ -433,6 +433,89 @@ void test_multi_threads()
}
#endif
#if defined(MX_FLASH_SUPPORT_RWW)
void test_rww_rwe()
{
utest_printf("\nTest rww and rwe Starts..\n");
TEST_SKIP_UNLESS_MESSAGE(block_device != NULL, "no block device found.");
utest_printf("\ntest read bank1 data when write bank0\n");
// Determine start_address in bank 0
bd_addr_t start_address_B0 = sectors_addr[rand() % num_of_sectors];
utest_printf("start_address_B0=0x%016" PRIx64 "\n", start_address_B0);
// Determine start_address in bank 1
bd_addr_t start_address_B1 = start_address_B0 | 0x1000000;
utest_printf("start_address_B1=0x%016" PRIx64 "\n", start_address_B1);
// Determine data_buf_size
bd_size_t erase_size = block_device->get_erase_size(start_address_B0);
TEST_ASSERT(erase_size > 0);
bd_size_t data_buf_size = block_device->get_program_size();;
// Allocate buffer for write test data
uint8_t *data_buf = new (std::nothrow) uint8_t[data_buf_size];
TEST_SKIP_UNLESS_MESSAGE(data_buf != NULL, "Not enough memory for test");
// Allocate buffer for read test data
uint8_t *out_data_buf = new (std::nothrow) uint8_t[data_buf_size];
TEST_SKIP_UNLESS_MESSAGE(out_data_buf != NULL, "Not enough memory for test");
// First must Erase given memory region
utest_printf("erasing given memory region in bank0\n");
int err = block_device->erase(start_address_B0, erase_size);
TEST_ASSERT_EQUAL(0, err);
utest_printf("erasing given memory region in bank1\n");
err = block_device->erase(start_address_B1, erase_size);
TEST_ASSERT_EQUAL(0, err);
// Write random data to selected region to make sure data is not accidentally set to "erased" value.
// With this pre-write, the test case will fail even if block_device->erase() is broken.
for (bd_size_t i = 0; i < data_buf_size; i++) {
data_buf[i] = (uint8_t) rand();
}
utest_printf("writing given memory region in bank1\n");
err = block_device->program((const void *)data_buf, start_address_B1, data_buf_size);
TEST_ASSERT_EQUAL(0, err);
memset(out_data_buf, 0, data_buf_size);
utest_printf("writing given memory region in bank0\n");
err = block_device->program((const void *)data_buf, start_address_B0, data_buf_size);
TEST_ASSERT_EQUAL(0, err);
// Read written memory region in bank1 to verify it contains information
// utest_printf("reading written memory region in bank1 when write bank0\n");
err = block_device->read((void *)out_data_buf, start_address_B1, data_buf_size);
TEST_ASSERT_EQUAL(0, err);
// Verify erased memory region
utest_printf("verifying written memory region\n");
for (bd_size_t i = 0; i < data_buf_size; i++) {
TEST_ASSERT_EQUAL(out_data_buf[i], data_buf[i]);
}
utest_printf("\ntest read bank1 data when erase bank0\n");
utest_printf("erasing given memory region in bank0\n");
memset(out_data_buf, 0, data_buf_size);
err = block_device->erase(start_address_B0, erase_size);
TEST_ASSERT_EQUAL(0, err);
// Read written memory region in bank1 to verify it contains information
utest_printf("reading written memory region in bank1 when erase bank0\n");
err = block_device->read((void *)out_data_buf, start_address_B1, data_buf_size);
TEST_ASSERT_EQUAL(0, err);
delete[] out_data_buf;
delete[] data_buf;
}
#endif
void test_erase_functionality()
{
utest_printf("\nTest BlockDevice::get_erase_value()..\n");
@ -816,6 +899,9 @@ template_case_t template_cases[] = {
{"Testing BlockDevice erase functionality", test_erase_functionality, greentea_failure_handler},
{"Testing program read small data sizes", test_program_read_small_data_sizes, greentea_failure_handler},
{"Testing unaligned erase blocks", test_unaligned_erase_blocks, greentea_failure_handler},
#if defined(MX_FLASH_SUPPORT_RWW)
{"Testing read while write and read while erase", test_rww_rwe, greentea_failure_handler},
#endif
{"Testing Deinit block device", test_deinit_bd, greentea_failure_handler},
};

View File

@ -34,7 +34,7 @@
/* Max amount of flash size is 4Gbytes */
/* hence 2^(31+1), then FLASH_SIZE_DEFAULT = 1<<31 */
#define OSPI_FLASH_SIZE_DEFAULT 0x80000000
#define OSPI_FLASH_SIZE_DEFAULT 0x4000000 //512Mbits
static uint32_t get_alt_bytes_size(const uint32_t num_bytes)
{
@ -252,7 +252,7 @@ static ospi_status_t _ospi_init_direct(ospi_t *obj, const ospi_pinmap_t *pinmap,
obj->handle.Init.ClockPrescaler = 4; // default value, will be overwritten in ospi_frequency
obj->handle.Init.FifoThreshold = 4;
obj->handle.Init.SampleShifting = HAL_OSPI_SAMPLE_SHIFTING_NONE;
obj->handle.Init.DeviceSize = POSITION_VAL(OSPI_FLASH_SIZE_DEFAULT) - 1;
obj->handle.Init.DeviceSize = 32;
obj->handle.Init.ChipSelectHighTime = 3;
obj->handle.Init.FreeRunningClock = HAL_OSPI_FREERUNCLK_DISABLE;
#if defined(HAL_OSPI_WRAP_NOT_SUPPORTED) // removed in STM32L4