From 48026bb824fd2d9cfb00ecd040db6ef3a416bae9 Mon Sep 17 00:00:00 2001 From: Clyne Sullivan Date: Fri, 22 Jan 2021 21:43:36 -0500 Subject: upload initial port --- ChibiOS_20.3.2/os/hal/lib/complex/mfs/hal_mfs.c | 1482 ++++++++++++++++++++ ChibiOS_20.3.2/os/hal/lib/complex/mfs/hal_mfs.h | 451 ++++++ ChibiOS_20.3.2/os/hal/lib/complex/mfs/hal_mfs.mk | 9 + ChibiOS_20.3.2/os/hal/lib/complex/readme.txt | 6 + .../devices/macronix_mx25/hal_flash_device.c | 666 +++++++++ .../devices/macronix_mx25/hal_flash_device.h | 441 ++++++ .../devices/macronix_mx25/hal_flash_device.mk | 11 + .../devices/micron_n25q/hal_flash_device.c | 586 ++++++++ .../devices/micron_n25q/hal_flash_device.h | 337 +++++ .../devices/micron_n25q/hal_flash_device.mk | 11 + .../os/hal/lib/complex/serial_nor/hal_serial_nor.c | 770 ++++++++++ .../os/hal/lib/complex/serial_nor/hal_serial_nor.h | 210 +++ 12 files changed, 4980 insertions(+) create mode 100644 ChibiOS_20.3.2/os/hal/lib/complex/mfs/hal_mfs.c create mode 100644 ChibiOS_20.3.2/os/hal/lib/complex/mfs/hal_mfs.h create mode 100644 ChibiOS_20.3.2/os/hal/lib/complex/mfs/hal_mfs.mk create mode 100644 ChibiOS_20.3.2/os/hal/lib/complex/readme.txt create mode 100644 ChibiOS_20.3.2/os/hal/lib/complex/serial_nor/devices/macronix_mx25/hal_flash_device.c create mode 100644 ChibiOS_20.3.2/os/hal/lib/complex/serial_nor/devices/macronix_mx25/hal_flash_device.h create mode 100644 ChibiOS_20.3.2/os/hal/lib/complex/serial_nor/devices/macronix_mx25/hal_flash_device.mk create mode 100644 ChibiOS_20.3.2/os/hal/lib/complex/serial_nor/devices/micron_n25q/hal_flash_device.c create mode 100644 ChibiOS_20.3.2/os/hal/lib/complex/serial_nor/devices/micron_n25q/hal_flash_device.h create mode 100644 ChibiOS_20.3.2/os/hal/lib/complex/serial_nor/devices/micron_n25q/hal_flash_device.mk create mode 100644 ChibiOS_20.3.2/os/hal/lib/complex/serial_nor/hal_serial_nor.c create mode 100644 ChibiOS_20.3.2/os/hal/lib/complex/serial_nor/hal_serial_nor.h (limited to 'ChibiOS_20.3.2/os/hal/lib/complex') diff --git a/ChibiOS_20.3.2/os/hal/lib/complex/mfs/hal_mfs.c b/ChibiOS_20.3.2/os/hal/lib/complex/mfs/hal_mfs.c new file mode 100644 index 0000000..7b898ff --- /dev/null +++ b/ChibiOS_20.3.2/os/hal/lib/complex/mfs/hal_mfs.c @@ -0,0 +1,1482 @@ +/* + ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio. + + This file is part of ChibiOS. + + ChibiOS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file hal_mfs.c + * @brief Managed Flash Storage module code. + * @details This module manages a flash partition as a generic storage where + * arbitrary data records can be created, updated, deleted and + * retrieved.
+ * A managed partition is composed of two banks of equal size, a + * bank is composed of one or more erasable sectors, a sector is + * divided in writable pages.
+ * The module handles flash wear leveling and recovery of damaged + * banks (where possible) caused by power loss during operations. + * Both operations are transparent to the user. + * + * @addtogroup HAL_MFS + * @{ + */ + +#include + +#include "hal.h" + +#include "hal_mfs.h" + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +/** + * @brief Data record size aligned. + */ +#define ALIGNED_REC_SIZE(n) \ + (flash_offset_t)MFS_ALIGN_NEXT(sizeof (mfs_data_header_t) + (size_t)(n)) + +/** + * @brief Data record header size aligned. + */ +#define ALIGNED_DHDR_SIZE \ + ALIGNED_REC_SIZE(0) + +/** + * @brief Aligned size of a type. + */ +#define ALIGNED_SIZEOF(t) \ + (((sizeof (t) - 1U) | MFS_ALIGN_MASK) + 1U) + +/** + * @brief Combines two values (0..3) in one (0..15). + */ +#define PAIR(a, b) (((unsigned)(a) << 2U) | (unsigned)(b)) + +/** + * @brief Error check helper. + */ +#define RET_ON_ERROR(err) do { \ + mfs_error_t e = (err); \ + if (e != MFS_NO_ERROR) { \ + return e; \ + } \ +} while (false) + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +static const uint16_t crc16_table[256] = { + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, + 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, + 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, + 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, + 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, + 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, + 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, + 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, + 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, + 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, + 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, + 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, + 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, + 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, + 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, + 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, + 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, + 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, + 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, + 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, + 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, + 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, + 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0 +}; + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +uint16_t crc16(uint16_t crc, const uint8_t *data, size_t n) { + + while (n > 0U) { + crc = (crc << 8U) ^ crc16_table[(crc >> 8U) ^ (uint16_t)*data]; + data++; + n--; + } + + return crc; +} + +static void mfs_state_reset(MFSDriver *mfsp) { + unsigned i; + + mfsp->current_bank = MFS_BANK_0; + mfsp->current_counter = 0U; + mfsp->next_offset = 0U; + mfsp->used_space = 0U; + + for (i = 0; i < MFS_CFG_MAX_RECORDS; i++) { + mfsp->descriptors[i].offset = 0U; + mfsp->descriptors[i].size = 0U; + } +} + +static flash_offset_t mfs_flash_get_bank_offset(MFSDriver *mfsp, + mfs_bank_t bank) { + + return bank == MFS_BANK_0 ? flashGetSectorOffset(mfsp->config->flashp, + mfsp->config->bank0_start) : + flashGetSectorOffset(mfsp->config->flashp, + mfsp->config->bank1_start); +} + +/** + * @brief Flash read. + * + * @param[in] mfsp pointer to the @p MFSDriver object + * @param[in] offset flash offset + * @param[in] n number of bytes to be read + * @param[out] rp pointer to the data buffer + * @return The operation status. + * + * @notapi + */ +static mfs_error_t mfs_flash_read(MFSDriver *mfsp, flash_offset_t offset, + size_t n, uint8_t *rp) { + flash_error_t ferr; + + ferr = flashRead(mfsp->config->flashp, offset, n, rp); + if (ferr != FLASH_NO_ERROR) { + mfsp->state = MFS_ERROR; + return MFS_ERR_FLASH_FAILURE; + } + + return MFS_NO_ERROR; +} + +/** + * @brief Flash write. + * @note If the option @p MFS_CFG_WRITE_VERIFY is enabled then the flash + * is also read back for verification. + * + * @param[in] mfsp pointer to the @p MFSDriver object + * @param[in] offset flash offset + * @param[in] n number of bytes to be written + * @param[in] wp pointer to the data buffer + * @return The operation status. + * + * @notapi + */ +static mfs_error_t mfs_flash_write(MFSDriver *mfsp, + flash_offset_t offset, + size_t n, + const uint8_t *wp) { + flash_error_t ferr; + + ferr = flashProgram(mfsp->config->flashp, offset, n, wp); + if (ferr != FLASH_NO_ERROR) { + mfsp->state = MFS_ERROR; + return MFS_ERR_FLASH_FAILURE; + } + +#if MFS_CFG_WRITE_VERIFY == TRUE + /* Verifying the written data by reading it back and comparing.*/ + while (n > 0U) { + size_t chunk = n <= MFS_CFG_BUFFER_SIZE ? n : MFS_CFG_BUFFER_SIZE; + + RET_ON_ERROR(mfs_flash_read(mfsp, offset, chunk, mfsp->buffer.data8)); + + if (memcmp((void *)mfsp->buffer.data8, (void *)wp, chunk)) { + mfsp->state = MFS_ERROR; + return MFS_ERR_FLASH_FAILURE; + } + n -= chunk; + offset += (flash_offset_t)chunk; + wp += chunk; + } +#endif + + return MFS_NO_ERROR; +} + +/** + * @brief Flash copy. + * @note If the option @p MFS_CFG_WRITE_VERIFY is enabled then the flash + * is also read back for verification. + * + * @param[in] mfsp pointer to the @p MFSDriver object + * @param[in] doffset destination flash offset + * @param[in] soffset source flash offset + * @param[in] n number of bytes to be copied + * @return The operation status. + * + * @notapi + */ +static mfs_error_t mfs_flash_copy(MFSDriver *mfsp, + flash_offset_t doffset, + flash_offset_t soffset, + uint32_t n) { + + /* Splitting the operation in smaller operations because the buffer is + small.*/ + while (n > 0U) { + /* Data size that can be written in a single program page operation.*/ + size_t chunk = (size_t)(((doffset | (MFS_CFG_BUFFER_SIZE - 1U)) + 1U) - + doffset); + if (chunk > n) { + chunk = n; + } + + RET_ON_ERROR(mfs_flash_read(mfsp, soffset, chunk, mfsp->buffer.data8)); + RET_ON_ERROR(mfs_flash_write(mfsp, doffset, chunk, mfsp->buffer.data8)); + + /* Next page.*/ + soffset += chunk; + doffset += chunk; + n -= chunk; + } + + return MFS_NO_ERROR; +} + +/** + * @brief Erases and verifies all sectors belonging to a bank. + * + * @param[in] mfsp pointer to the @p MFSDriver object + * @param[in] bank bank to be erased + * @return The operation status. + * + * @notapi + */ +static mfs_error_t mfs_bank_erase(MFSDriver *mfsp, mfs_bank_t bank) { + flash_sector_t sector, end; + + if (bank == MFS_BANK_0) { + sector = mfsp->config->bank0_start; + end = mfsp->config->bank0_start + mfsp->config->bank0_sectors; + } + else { + sector = mfsp->config->bank1_start; + end = mfsp->config->bank1_start + mfsp->config->bank1_sectors; + } + + while (sector < end) { + flash_error_t ferr; + + ferr = flashStartEraseSector(mfsp->config->flashp, sector); + if (ferr != FLASH_NO_ERROR) { + mfsp->state = MFS_ERROR; + return MFS_ERR_FLASH_FAILURE; + } + ferr = flashWaitErase(mfsp->config->flashp); + if (ferr != FLASH_NO_ERROR) { + mfsp->state = MFS_ERROR; + return MFS_ERR_FLASH_FAILURE; + } + ferr = flashVerifyErase(mfsp->config->flashp, sector); + if (ferr != FLASH_NO_ERROR) { + mfsp->state = MFS_ERROR; + return MFS_ERR_FLASH_FAILURE; + } + + sector++; + } + + return MFS_NO_ERROR; +} + +/** + * @brief Erases and verifies all sectors belonging to a bank. + * + * @param[in] mfsp pointer to the @p MFSDriver object + * @param[in] bank bank to be verified + * @return The operation status. + * + * @notapi + */ +static mfs_error_t mfs_bank_verify_erase(MFSDriver *mfsp, mfs_bank_t bank) { + flash_sector_t sector, end; + + if (bank == MFS_BANK_0) { + sector = mfsp->config->bank0_start; + end = mfsp->config->bank0_start + mfsp->config->bank0_sectors; + } + else { + sector = mfsp->config->bank1_start; + end = mfsp->config->bank1_start + mfsp->config->bank1_sectors; + } + + while (sector < end) { + flash_error_t ferr; + + ferr = flashVerifyErase(mfsp->config->flashp, sector); + if (ferr == FLASH_ERROR_VERIFY) { + return MFS_ERR_NOT_ERASED; + } + if (ferr != FLASH_NO_ERROR) { + mfsp->state = MFS_ERROR; + return MFS_ERR_FLASH_FAILURE; + } + + sector++; + } + + return MFS_NO_ERROR; +} + +/** + * @brief Writes the validation header in a bank. + * + * @param[in] mfsp pointer to the @p MFSDriver object + * @param[in] bank bank to be validated + * @param[in] cnt value for the flash usage counter + * @return The operation status. + * + * @notapi + */ +static mfs_error_t mfs_bank_write_header(MFSDriver *mfsp, + mfs_bank_t bank, + uint32_t cnt) { + flash_sector_t sector; + mfs_bank_header_t bhdr; + + if (bank == MFS_BANK_0) { + sector = mfsp->config->bank0_start; + } + else { + sector = mfsp->config->bank1_start; + } + + bhdr.fields.magic1 = MFS_BANK_MAGIC_1; + bhdr.fields.magic2 = MFS_BANK_MAGIC_2; + bhdr.fields.counter = cnt; + bhdr.fields.reserved1 = (uint16_t)mfsp->config->erased; + bhdr.fields.crc = crc16(0xFFFFU, bhdr.hdr8, + sizeof (mfs_bank_header_t) - sizeof (uint16_t)); + + return mfs_flash_write(mfsp, + flashGetSectorOffset(mfsp->config->flashp, sector), + sizeof (mfs_bank_header_t), + bhdr.hdr8); +} + +/** + * @brief Checks integrity of the header in the shared buffer. + * + * @param[in] mfsp pointer to the @p MFSDriver object + * @return The header state. + * + * @notapi + */ +static mfs_bank_state_t mfs_bank_check_header(MFSDriver *mfsp) { + uint16_t crc; + + if ((mfsp->buffer.bhdr.hdr32[0] == mfsp->config->erased) && + (mfsp->buffer.bhdr.hdr32[1] == mfsp->config->erased) && + (mfsp->buffer.bhdr.hdr32[2] == mfsp->config->erased) && + (mfsp->buffer.bhdr.hdr32[3] == mfsp->config->erased)) { + return MFS_BANK_ERASED; + } + + /* Checking header fields integrity.*/ + if ((mfsp->buffer.bhdr.fields.magic1 != MFS_BANK_MAGIC_1) || + (mfsp->buffer.bhdr.fields.magic2 != MFS_BANK_MAGIC_2) || + (mfsp->buffer.bhdr.fields.counter == mfsp->config->erased) || + (mfsp->buffer.bhdr.fields.reserved1 != (uint16_t)mfsp->config->erased)) { + return MFS_BANK_GARBAGE; + } + + /* Verifying header CRC.*/ + crc = crc16(0xFFFFU, mfsp->buffer.bhdr.hdr8, + sizeof (mfs_bank_header_t) - sizeof (uint16_t)); + if (crc != mfsp->buffer.bhdr.fields.crc) { + return MFS_BANK_GARBAGE; + } + + return MFS_BANK_OK; +} + +/** + * @brief Scans blocks searching for records. + * @note The block integrity is strongly checked. + * + * @param[in] mfsp pointer to the @p MFSDriver object + * @param[in] bank the bank identifier + * @param[out] wflagp warning flag on anomalies + * + * @return The operation status. + * + * @notapi + */ +static mfs_error_t mfs_bank_scan_records(MFSDriver *mfsp, + mfs_bank_t bank, + bool *wflagp) { + flash_offset_t hdr_offset, start_offset, end_offset; + + /* No warning by default.*/ + *wflagp = false; + + /* Boundaries.*/ + start_offset = mfs_flash_get_bank_offset(mfsp, bank); + hdr_offset = start_offset + (flash_offset_t)ALIGNED_SIZEOF(mfs_bank_header_t); + end_offset = start_offset + mfsp->config->bank_size; + + /* Scanning records until there is there is not enough space left for an + header.*/ + while (hdr_offset < end_offset - ALIGNED_DHDR_SIZE) { + union { + mfs_data_header_t dhdr; + uint8_t data8[ALIGNED_SIZEOF(mfs_data_header_t)]; + } u; + uint16_t crc; + + /* Reading the current record header.*/ + RET_ON_ERROR(mfs_flash_read(mfsp, hdr_offset, + sizeof (mfs_data_header_t), + u.data8)); + + /* Checking if the found header is in erased state.*/ + if ((u.dhdr.hdr32[0] == mfsp->config->erased) && + (u.dhdr.hdr32[1] == mfsp->config->erased) && + (u.dhdr.hdr32[2] == mfsp->config->erased)) { + break; + } + + /* It is not erased so checking for integrity.*/ + if ((u.dhdr.fields.magic1 != MFS_HEADER_MAGIC_1) || + (u.dhdr.fields.magic2 != MFS_HEADER_MAGIC_2) || + (u.dhdr.fields.id < 1U) || + (u.dhdr.fields.id > (uint32_t)MFS_CFG_MAX_RECORDS) || + (u.dhdr.fields.size > end_offset - hdr_offset)) { + *wflagp = true; + break; + } + + /* Finally checking the CRC, we need to perform it in chunks because + we have a limited buffer.*/ + crc = 0xFFFFU; + if (u.dhdr.fields.size > 0U) { + flash_offset_t data = hdr_offset + sizeof (mfs_data_header_t); + uint32_t total = u.dhdr.fields.size; + + while (total > 0U) { + uint32_t chunk = total > MFS_CFG_BUFFER_SIZE ? MFS_CFG_BUFFER_SIZE : + total; + + /* Reading the data chunk.*/ + RET_ON_ERROR(mfs_flash_read(mfsp, data, chunk, mfsp->buffer.data8)); + + /* CRC on the read data chunk.*/ + crc = crc16(crc, &mfsp->buffer.data8[0], chunk); + + /* Next chunk.*/ + data += chunk; + total -= chunk; + } + } + if (crc != u.dhdr.fields.crc) { + /* If the CRC is invalid then this record is ignored but scanning + continues because there could be more valid records afterward.*/ + *wflagp = true; + } + else { + /* Zero-sized records are erase markers.*/ + if (u.dhdr.fields.size == 0U) { + mfsp->descriptors[u.dhdr.fields.id - 1U].offset = 0U; + mfsp->descriptors[u.dhdr.fields.id - 1U].size = 0U; + } + else { + mfsp->descriptors[u.dhdr.fields.id - 1U].offset = hdr_offset; + mfsp->descriptors[u.dhdr.fields.id - 1U].size = u.dhdr.fields.size; + } + } + + /* On the next header.*/ + hdr_offset = hdr_offset + ALIGNED_REC_SIZE(u.dhdr.fields.size); + } + + /* Next writable offset.*/ + mfsp->next_offset = hdr_offset; + + return MFS_NO_ERROR; +} + +/** + * @brief Determines the state of a bank. + * @note This function does not test the bank integrity by scanning + * the data area, it just checks the header. + * + * @param[in] mfsp pointer to the @p MFSDriver object + * @param[in] bank bank to be checked + * @param[out] statep bank state, it can be: + * - MFS_BANK_ERASED + * - MFS_BANK_GARBAGE + * - MFS_BANK_OK + * . + * @param[out] cntp bank counter + * @return The operation status. + * + * @notapi + */ +static mfs_error_t mfs_bank_get_state(MFSDriver *mfsp, + mfs_bank_t bank, + mfs_bank_state_t *statep, + uint32_t *cntp) { + + /* Reading the current bank header.*/ + RET_ON_ERROR(mfs_flash_read(mfsp, mfs_flash_get_bank_offset(mfsp, bank), + sizeof (mfs_bank_header_t), + mfsp->buffer.data8)); + + /* Getting the counter regardless of the bank state, it is only valid if + the state is MFS_BANK_OK.*/ + *cntp = mfsp->buffer.bhdr.fields.counter; + + /* Checking just the header.*/ + *statep = mfs_bank_check_header(mfsp); + if (*statep == MFS_BANK_ERASED) { + mfs_error_t err; + + /* Checking if the bank is really all erased.*/ + err = mfs_bank_verify_erase(mfsp, bank); + if (err == MFS_ERR_NOT_ERASED) { + *statep = MFS_BANK_GARBAGE; + } + } + + return MFS_NO_ERROR; +} + +/** + * @brief Enforces a garbage collection. + * @details Storage data is compacted into a single bank. + * + * @param[out] mfsp pointer to the @p MFSDriver object + * @return The operation status. + * + * @notapi + */ +static mfs_error_t mfs_garbage_collect(MFSDriver *mfsp) { + unsigned i; + mfs_bank_t sbank, dbank; + flash_offset_t dest_offset; + + sbank = mfsp->current_bank; + if (sbank == MFS_BANK_0) { + dbank = MFS_BANK_1; + } + else { + dbank = MFS_BANK_0; + } + + /* Write address.*/ + dest_offset = mfs_flash_get_bank_offset(mfsp, dbank) + + ALIGNED_SIZEOF(mfs_bank_header_t); + + /* Copying the most recent record instances only.*/ + for (i = 0; i < MFS_CFG_MAX_RECORDS; i++) { + uint32_t totsize = ALIGNED_REC_SIZE(mfsp->descriptors[i].size); + if (mfsp->descriptors[i].offset != 0) { + RET_ON_ERROR(mfs_flash_copy(mfsp, dest_offset, + mfsp->descriptors[i].offset, + totsize)); + mfsp->descriptors[i].offset = dest_offset; + dest_offset += totsize; + } + } + + /* New current bank.*/ + mfsp->current_bank = dbank; + mfsp->current_counter += 1U; + mfsp->next_offset = dest_offset; + + /* The header is written after the data.*/ + RET_ON_ERROR(mfs_bank_write_header(mfsp, dbank, mfsp->current_counter)); + + /* The source bank is erased last.*/ + RET_ON_ERROR(mfs_bank_erase(mfsp, sbank)); + + return MFS_NO_ERROR; +} + +/** + * @brief Performs a flash partition mount attempt. + * + * @param[in] mfsp pointer to the @p MFSDriver object + * @return The operation status. + * + * @api + */ +static mfs_error_t mfs_try_mount(MFSDriver *mfsp) { + mfs_bank_state_t sts0, sts1; + mfs_bank_t bank; + uint32_t cnt0 = 0, cnt1 = 0; + bool w1 = false, w2 = false; + + /* Resetting the bank state.*/ + mfs_state_reset(mfsp); + + /* Assessing the state of the two banks.*/ + RET_ON_ERROR(mfs_bank_get_state(mfsp, MFS_BANK_0, &sts0, &cnt0)); + RET_ON_ERROR(mfs_bank_get_state(mfsp, MFS_BANK_1, &sts1, &cnt1)); + + /* Handling all possible scenarios, each one requires its own recovery + strategy.*/ + switch (PAIR(sts0, sts1)) { + + case PAIR(MFS_BANK_ERASED, MFS_BANK_ERASED): + /* Both banks erased, first initialization.*/ + RET_ON_ERROR(mfs_bank_write_header(mfsp, MFS_BANK_0, 1)); + bank = MFS_BANK_0; + break; + + case PAIR(MFS_BANK_OK, MFS_BANK_OK): + /* Both banks appear to be valid but one must be newer, erasing the + older one.*/ + if (cnt0 > cnt1) { + /* Bank 0 is newer.*/ + RET_ON_ERROR(mfs_bank_erase(mfsp, MFS_BANK_1)); + bank = MFS_BANK_0; + } + else { + /* Bank 1 is newer.*/ + RET_ON_ERROR(mfs_bank_erase(mfsp, MFS_BANK_0)); + bank = MFS_BANK_1; + } + w1 = true; + break; + + case PAIR(MFS_BANK_GARBAGE, MFS_BANK_GARBAGE): + /* Both banks are unreadable, reinitializing.*/ + RET_ON_ERROR(mfs_bank_erase(mfsp, MFS_BANK_0)); + RET_ON_ERROR(mfs_bank_erase(mfsp, MFS_BANK_1)); + RET_ON_ERROR(mfs_bank_write_header(mfsp, MFS_BANK_0, 1)); + bank = MFS_BANK_0; + w1 = true; + break; + + case PAIR(MFS_BANK_ERASED, MFS_BANK_OK): + /* Normal situation, bank one is used.*/ + bank = MFS_BANK_1; + break; + + case PAIR(MFS_BANK_OK, MFS_BANK_ERASED): + /* Normal situation, bank zero is used.*/ + bank = MFS_BANK_0; + break; + + case PAIR(MFS_BANK_ERASED, MFS_BANK_GARBAGE): + /* Bank zero is erased, bank one is not readable.*/ + RET_ON_ERROR(mfs_bank_erase(mfsp, MFS_BANK_1)); + RET_ON_ERROR(mfs_bank_write_header(mfsp, MFS_BANK_0, 1)); + bank = MFS_BANK_0; + w1 = true; + break; + + case PAIR(MFS_BANK_GARBAGE, MFS_BANK_ERASED): + /* Bank zero is not readable, bank one is erased.*/ + RET_ON_ERROR(mfs_bank_erase(mfsp, MFS_BANK_0)); + RET_ON_ERROR(mfs_bank_write_header(mfsp, MFS_BANK_1, 1)); + bank = MFS_BANK_1; + w1 = true; + break; + + case PAIR(MFS_BANK_OK, MFS_BANK_GARBAGE): + /* Bank zero is normal, bank one is unreadable.*/ + RET_ON_ERROR(mfs_bank_erase(mfsp, MFS_BANK_1)); + bank = MFS_BANK_0; + w1 = true; + break; + + case PAIR(MFS_BANK_GARBAGE, MFS_BANK_OK): + /* Bank zero is unreadable, bank one is normal.*/ + RET_ON_ERROR(mfs_bank_erase(mfsp, MFS_BANK_0)); + bank = MFS_BANK_1; + w1 = true; + break; + + default: + return MFS_ERR_INTERNAL; + } + + /* Mounting the bank.*/ + { + unsigned i; + + /* Reading the bank header again.*/ + RET_ON_ERROR(mfs_flash_read(mfsp, mfs_flash_get_bank_offset(mfsp, bank), + sizeof (mfs_bank_header_t), + mfsp->buffer.data8)); + + /* Checked again for extra safety.*/ + if (mfs_bank_check_header(mfsp) != MFS_BANK_OK) { + return MFS_ERR_INTERNAL; + } + + /* Storing the bank data.*/ + mfsp->current_bank = bank; + mfsp->current_counter = mfsp->buffer.bhdr.fields.counter; + + /* Scanning for the most recent instance of all records.*/ + RET_ON_ERROR(mfs_bank_scan_records(mfsp, bank, &w2)); + + /* Calculating the effective used size.*/ + mfsp->used_space = ALIGNED_SIZEOF(mfs_bank_header_t); + for (i = 0; i < MFS_CFG_MAX_RECORDS; i++) { + if (mfsp->descriptors[i].offset != 0U) { + mfsp->used_space += ALIGNED_REC_SIZE(mfsp->descriptors[i].size); + } + } + } + + /* In case of detected problems then a garbage collection is performed in + order to repair/remove anomalies.*/ + if (w2) { + RET_ON_ERROR(mfs_garbage_collect(mfsp)); + } + + return (w1 || w2) ? MFS_WARN_REPAIR : MFS_NO_ERROR; +} + +/** + * @brief Configures and activates a MFS driver. + * + * @param[in] mfsp pointer to the @p MFSDriver object + * @return The operation status. + * @retval MFS_NO_ERROR if the operation has been successfully + * completed. + * @retval MFS_WARN_GC if the operation triggered a garbage + * collection. + * @retval MFS_ERR_FLASH_FAILURE if the flash memory is unusable because HW + * failures. Makes the driver enter the + * @p MFS_ERROR state. + * @retval MFS_ERR_INTERNAL if an internal logic failure is detected. + * + * @api + */ +mfs_error_t mfs_mount(MFSDriver *mfsp) { + unsigned i; + + /* Resetting previous state.*/ + mfs_state_reset(mfsp); + + /* Attempting to mount the managed partition.*/ + for (i = 0; i < MFS_CFG_MAX_REPAIR_ATTEMPTS; i++) { + mfs_error_t err; + + err = mfs_try_mount(mfsp); + if (err == MFS_ERR_INTERNAL) { + /* Special case, do not retry on internal errors but report + immediately.*/ + mfsp->state = MFS_ERROR; + return err; + } + if (!MFS_IS_ERROR(err)) { + mfsp->state = MFS_READY; + return err; + } + } + + /* Driver start failed.*/ + mfsp->state = MFS_ERROR; + return MFS_ERR_FLASH_FAILURE; +} + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Initializes an instance. + * + * @param[out] mfsp pointer to the @p MFSDriver object + * + * @init + */ +void mfsObjectInit(MFSDriver *mfsp) { + + osalDbgCheck(mfsp != NULL); + + mfsp->state = MFS_STOP; + mfsp->config = NULL; +} + +/** + * @brief Configures and activates a MFS driver. + * + * @param[in] mfsp pointer to the @p MFSDriver object + * @param[in] config pointer to the configuration + * @return The operation status. + * @retval MFS_NO_ERROR if the operation has been + * completed. + * @retval MFS_WARN_GC if the operation triggered a garbage + * collection. + * @retval MFS_ERR_FLASH_FAILURE if the flash memory is unusable because HW + * failures. Makes the driver enter the + * @p MFS_ERROR state. + * @retval MFS_ERR_INTERNAL if an internal logic failure is detected. + * + * @api + */ +mfs_error_t mfsStart(MFSDriver *mfsp, const MFSConfig *config) { + + osalDbgCheck((mfsp != NULL) && (config != NULL)); + osalDbgAssert((mfsp->state == MFS_STOP) || (mfsp->state == MFS_READY) || + (mfsp->state == MFS_ERROR), "invalid state"); + + /* Storing configuration.*/ + mfsp->config = config; + + return mfs_mount(mfsp); +} + +/** + * @brief Deactivates a MFS driver. + * + * @param[in] mfsp pointer to the @p MFSDriver object + * + * @api + */ +void mfsStop(MFSDriver *mfsp) { + + osalDbgCheck(mfsp != NULL); + osalDbgAssert((mfsp->state == MFS_STOP) || (mfsp->state == MFS_READY) || + (mfsp->state == MFS_ERROR), "invalid state"); + + mfsp->config = NULL; + mfsp->state = MFS_STOP; +} + +/** + * @brief Destroys the state of the managed storage by erasing the flash. + * + * @param[in] mfsp pointer to the @p MFSDriver object + * @return The operation status. + * @retval MFS_ERR_INV_STATE if the driver is in not in @p MFS_READY + * state. + * @retval MFS_NO_ERROR if the operation has been successfully + * completed. + * @retval MFS_ERR_FLASH_FAILURE if the flash memory is unusable because HW + * failures. Makes the driver enter the + * @p MFS_ERROR state. + * @retval MFS_ERR_INTERNAL if an internal logic failure is detected. + * + * @api + */ +mfs_error_t mfsErase(MFSDriver *mfsp) { + + osalDbgCheck(mfsp != NULL); + + if (mfsp->state != MFS_READY) { + return MFS_ERR_INV_STATE; + } + + RET_ON_ERROR(mfs_bank_erase(mfsp, MFS_BANK_0)); + RET_ON_ERROR(mfs_bank_erase(mfsp, MFS_BANK_1)); + + return mfs_mount(mfsp); +} + +/** + * @brief Retrieves and reads a data record. + * + * @param[in] mfsp pointer to the @p MFSDriver object + * @param[in] id record numeric identifier, the valid range is between + * @p 1 and @p MFS_CFG_MAX_RECORDS + * @param[in,out] np on input is the maximum buffer size, on return it is + * the size of the data copied into the buffer + * @param[out] buffer pointer to a buffer for record data + * @return The operation status. + * @retval MFS_NO_ERROR if the operation has been successfully + * completed. + * @retval MFS_ERR_INV_STATE if the driver is in not in @p MFS_READY + * state. + * @retval MFS_ERR_INV_SIZE if the passed buffer is not large enough to + * contain the record data. + * @retval MFS_ERR_NOT_FOUND if the specified id does not exists. + * @retval MFS_ERR_FLASH_FAILURE if the flash memory is unusable because HW + * failures. Makes the driver enter the + * @p MFS_ERROR state. + * @retval MFS_ERR_INTERNAL if an internal logic failure is detected. + * + * @api + */ +mfs_error_t mfsReadRecord(MFSDriver *mfsp, mfs_id_t id, + size_t *np, uint8_t *buffer) { + uint16_t crc; + + osalDbgCheck((mfsp != NULL) && + (id >= 1U) && (id <= (mfs_id_t)MFS_CFG_MAX_RECORDS) && + (np != NULL) && (*np > 0U) && (buffer != NULL)); + + if ((mfsp->state != MFS_READY) && (mfsp->state != MFS_TRANSACTION)) { + return MFS_ERR_INV_STATE; + } + + /* Checking if the requested record actually exists.*/ + if (mfsp->descriptors[id - 1U].offset == 0U) { + return MFS_ERR_NOT_FOUND; + } + + /* Making sure to not overflow the buffer.*/ + if (*np < mfsp->descriptors[id - 1U].size) { + return MFS_ERR_INV_SIZE; + } + + /* Header read from flash.*/ + RET_ON_ERROR(mfs_flash_read(mfsp, + mfsp->descriptors[id - 1U].offset, + sizeof (mfs_data_header_t), + mfsp->buffer.data8)); + + /* Data read from flash.*/ + *np = mfsp->descriptors[id - 1U].size; + RET_ON_ERROR(mfs_flash_read(mfsp, + mfsp->descriptors[id - 1U].offset + sizeof (mfs_data_header_t), + *np, + buffer)); + + /* Checking CRC.*/ + crc = crc16(0xFFFFU, buffer, *np); + if (crc != mfsp->buffer.dhdr.fields.crc) { + mfsp->state = MFS_ERROR; + return MFS_ERR_FLASH_FAILURE; + } + + return MFS_NO_ERROR; +} + +/** + * @brief Creates or updates a data record. + * + * @param[in] mfsp pointer to the @p MFSDriver object + * @param[in] id record numeric identifier, the valid range is between + * @p 1 and @p MFS_CFG_MAX_RECORDS + * @param[in] n size of data to be written, it cannot be zero + * @param[in] buffer pointer to a buffer for record data + * @return The operation status. + * @retval MFS_NO_ERROR if the operation has been successfully + * completed. + * @retval MFS_WARN_GC if the operation triggered a garbage + * collection. + * @retval MFS_ERR_INV_STATE if the driver is in not in @p MFS_READY + * state. + * @retval MFS_ERR_OUT_OF_MEM if there is not enough flash space for the + * operation. + * @retval MFS_ERR_TRANSACTION_NUM if the transaction operations buffer space + * has been exceeded. + * @retval MFS_ERR_TRANSACTION_SIZE if the transaction allocated space + * has been exceeded. + * @retval MFS_ERR_FLASH_FAILURE if the flash memory is unusable because HW + * failures. Makes the driver enter the + * @p MFS_ERROR state. + * @retval MFS_ERR_INTERNAL if an internal logic failure is detected. + * + * @api + */ +mfs_error_t mfsWriteRecord(MFSDriver *mfsp, mfs_id_t id, + size_t n, const uint8_t *buffer) { + flash_offset_t free, asize, rspace; + + osalDbgCheck((mfsp != NULL) && + (id >= 1U) && (id <= (mfs_id_t)MFS_CFG_MAX_RECORDS) && + (n > 0U) && (buffer != NULL)); + + /* Aligned record size.*/ + asize = ALIGNED_REC_SIZE(n); + + /* Normal mode code path.*/ + if (mfsp->state == MFS_READY) { + bool warning = false; + + /* If the required space is beyond the available (compacted) block + size then an error is returned. + NOTE: The space for one extra header is reserved in order to allow + for an erase operation after the space has been fully allocated.*/ + rspace = ALIGNED_DHDR_SIZE + asize; + if (rspace > mfsp->config->bank_size - mfsp->used_space) { + return MFS_ERR_OUT_OF_MEM; + } + + /* Checking for immediately (not compacted) available space.*/ + free = (mfs_flash_get_bank_offset(mfsp, mfsp->current_bank) + + mfsp->config->bank_size) - mfsp->next_offset; + if (rspace > free) { + /* We need to perform a garbage collection, there is enough space + but it has to be freed.*/ + warning = true; + RET_ON_ERROR(mfs_garbage_collect(mfsp)); + } + + /* Writing the data header without the magic, it will be written last.*/ + mfsp->buffer.dhdr.fields.id = (uint16_t)id; + mfsp->buffer.dhdr.fields.size = (uint32_t)n; + mfsp->buffer.dhdr.fields.crc = crc16(0xFFFFU, buffer, n); + RET_ON_ERROR(mfs_flash_write(mfsp, + mfsp->next_offset + (sizeof (uint32_t) * 2U), + sizeof (mfs_data_header_t) - (sizeof (uint32_t) * 2U), + mfsp->buffer.data8 + (sizeof (uint32_t) * 2U))); + + /* Writing the data part.*/ + RET_ON_ERROR(mfs_flash_write(mfsp, + mfsp->next_offset + sizeof (mfs_data_header_t), + n, + buffer)); + + /* Finally writing the magic number, it seals the operation.*/ + mfsp->buffer.dhdr.fields.magic1 = (uint32_t)MFS_HEADER_MAGIC_1; + mfsp->buffer.dhdr.fields.magic2 = (uint32_t)MFS_HEADER_MAGIC_2; + RET_ON_ERROR(mfs_flash_write(mfsp, + mfsp->next_offset, + sizeof (uint32_t) * 2U, + mfsp->buffer.data8)); + + /* The size of the old record instance, if present, must be subtracted + to the total used size.*/ + if (mfsp->descriptors[id - 1U].offset != 0U) { + mfsp->used_space -= ALIGNED_REC_SIZE(mfsp->descriptors[id - 1U].size); + } + + /* Adjusting bank-related metadata.*/ + mfsp->descriptors[id - 1U].offset = mfsp->next_offset; + mfsp->descriptors[id - 1U].size = (uint32_t)n; + mfsp->next_offset += asize; + mfsp->used_space += asize; + + return warning ? MFS_WARN_GC : MFS_NO_ERROR; + } + +#if MFS_CFG_TRANSACTION_MAX > 0 + /* Transaction mode code path.*/ + if (mfsp->state == MFS_TRANSACTION) { + mfs_transaction_op_t *top; + + /* Checking if the maximum number of operations in a transaction is + Exceeded.*/ + if (mfsp->tr_nops >= MFS_CFG_TRANSACTION_MAX) { + return MFS_ERR_TRANSACTION_NUM; + } + + /* If the required space is greater than the space allocated for the + transaction then error.*/ + rspace = asize; + if (rspace > mfsp->tr_limit_offet - mfsp->tr_next_offset) { + return MFS_ERR_TRANSACTION_SIZE; + } + + /* Writing the data header without the magic, it will be written last.*/ + mfsp->buffer.dhdr.fields.id = (uint16_t)id; + mfsp->buffer.dhdr.fields.size = (uint32_t)n; + mfsp->buffer.dhdr.fields.crc = crc16(0xFFFFU, buffer, n); + RET_ON_ERROR(mfs_flash_write(mfsp, + mfsp->tr_next_offset + (sizeof (uint32_t) * 2U), + sizeof (mfs_data_header_t) - (sizeof (uint32_t) * 2U), + mfsp->buffer.data8 + (sizeof (uint32_t) * 2U))); + + /* Writing the data part.*/ + RET_ON_ERROR(mfs_flash_write(mfsp, + mfsp->tr_next_offset + sizeof (mfs_data_header_t), + n, + buffer)); + + /* Adding a transaction operation record.*/ + top = &mfsp->tr_ops[mfsp->tr_nops]; + top->offset = mfsp->tr_next_offset; + top->size = n; + top->id = id; + + /* Number of records and next write position updated.*/ + mfsp->tr_nops++; + mfsp->tr_next_offset += asize; + + return MFS_NO_ERROR; + } +#endif /* MFS_CFG_TRANSACTION_MAX > 0 */ + + /* Invalid state.*/ + return MFS_ERR_INV_STATE; +} + +/** + * @brief Erases a data record. + * + * @param[in] mfsp pointer to the @p MFSDriver object + * @param[in] id record numeric identifier, the valid range is between + * @p 1 and @p MFS_CFG_MAX_RECORDS + * @return The operation status. + * @retval MFS_NO_ERROR if the operation has been successfully + * completed. + * @retval MFS_WARN_GC if the operation triggered a garbage + * collection. + * @retval MFS_ERR_INV_STATE if the driver is in not in @p MFS_READY + * state. + * @retval MFS_ERR_OUT_OF_MEM if there is not enough flash space for the + * operation. + * @retval MFS_ERR_TRANSACTION_NUM if the transaction operations buffer space + * has been exceeded. + * @retval MFS_ERR_TRANSACTION_SIZE if the transaction allocated space + * has been exceeded. + * @retval MFS_ERR_FLASH_FAILURE if the flash memory is unusable because HW + * failures. Makes the driver enter the + * @p MFS_ERROR state. + * @retval MFS_ERR_INTERNAL if an internal logic failure is detected. + * + * @api + */ +mfs_error_t mfsEraseRecord(MFSDriver *mfsp, mfs_id_t id) { + flash_offset_t free, asize, rspace; + + osalDbgCheck((mfsp != NULL) && + (id >= 1U) && (id <= (mfs_id_t)MFS_CFG_MAX_RECORDS)); + + /* Aligned record size.*/ + asize = ALIGNED_DHDR_SIZE; + + /* Normal mode code path.*/ + if (mfsp->state == MFS_READY) { + bool warning = false; + + /* Checking if the requested record actually exists.*/ + if (mfsp->descriptors[id - 1U].offset == 0U) { + return MFS_ERR_NOT_FOUND; + } + + /* If the required space is beyond the available (compacted) block + size then an internal error is returned, it should never happen.*/ + rspace = asize; + if (rspace > mfsp->config->bank_size - mfsp->used_space) { + return MFS_ERR_INTERNAL; + } + + /* Checking for immediately (not compacted) available space.*/ + free = (mfs_flash_get_bank_offset(mfsp, mfsp->current_bank) + + mfsp->config->bank_size) - mfsp->next_offset; + if (rspace > free) { + /* We need to perform a garbage collection, there is enough space + but it has to be freed.*/ + warning = true; + RET_ON_ERROR(mfs_garbage_collect(mfsp)); + } + + /* Writing the data header with size set to zero, it means that the + record is logically erased.*/ + mfsp->buffer.dhdr.fields.magic1 = (uint32_t)MFS_HEADER_MAGIC_1; + mfsp->buffer.dhdr.fields.magic2 = (uint32_t)MFS_HEADER_MAGIC_2; + mfsp->buffer.dhdr.fields.id = (uint16_t)id; + mfsp->buffer.dhdr.fields.size = (uint32_t)0; + mfsp->buffer.dhdr.fields.crc = (uint16_t)0xFFFF; + RET_ON_ERROR(mfs_flash_write(mfsp, + mfsp->next_offset, + sizeof (mfs_data_header_t), + mfsp->buffer.data8)); + + /* Adjusting bank-related metadata.*/ + mfsp->used_space -= ALIGNED_REC_SIZE(mfsp->descriptors[id - 1U].size); + mfsp->next_offset += sizeof (mfs_data_header_t); + mfsp->descriptors[id - 1U].offset = 0U; + mfsp->descriptors[id - 1U].size = 0U; + + return warning ? MFS_WARN_GC : MFS_NO_ERROR; + } + +#if MFS_CFG_TRANSACTION_MAX > 0 + /* Transaction mode code path.*/ + if (mfsp->state == MFS_TRANSACTION) { + mfs_transaction_op_t *top; + + /* Checking if the requested record actually exists.*/ + if (mfsp->descriptors[id - 1U].offset == 0U) { + return MFS_ERR_NOT_FOUND; + } + + /* Checking if the maximum number of operations in a transaction is + Exceeded.*/ + if (mfsp->tr_nops >= MFS_CFG_TRANSACTION_MAX) { + return MFS_ERR_TRANSACTION_NUM; + } + + /* If the required space is greater than the space allocated for the + transaction then error.*/ + rspace = asize; + if (rspace > mfsp->tr_limit_offet - mfsp->tr_next_offset) { + return MFS_ERR_TRANSACTION_SIZE; + } + + /* Writing the data header with size set to zero, it means that the + record is logically erased. Note, the magic number is not set.*/ + mfsp->buffer.dhdr.fields.id = (uint16_t)id; + mfsp->buffer.dhdr.fields.size = (uint32_t)0; + mfsp->buffer.dhdr.fields.crc = (uint16_t)0xFFFF; + RET_ON_ERROR(mfs_flash_write(mfsp, + mfsp->tr_next_offset + (sizeof (uint32_t) * 2U), + sizeof (mfs_data_header_t) - (sizeof (uint32_t) * 2U), + mfsp->buffer.data8 + (sizeof (uint32_t) * 2U))); + + /* Adding a transaction operation record.*/ + top = &mfsp->tr_ops[mfsp->tr_nops]; + top->offset = mfsp->tr_next_offset; + top->size = 0U; + top->id = id; + + /* Number of records and next write position updated.*/ + mfsp->tr_nops++; + mfsp->tr_next_offset += asize; + + return MFS_NO_ERROR; + } +#endif /* MFS_CFG_TRANSACTION_MAX > 0 */ + + return MFS_ERR_INV_STATE; +} + +/** + * @brief Enforces a garbage collection operation. + * @details Garbage collection involves: integrity check, optionally repairs, + * obsolete data removal, data compaction and a flash bank swap. + * + * @param[in] mfsp pointer to the @p MFSDriver object + * @return The operation status. + * @retval MFS_NO_ERROR if the operation has been successfully + * completed. + * @retval MFS_ERR_INV_STATE if the driver is in not in @p MFS_READY + * state. + * @retval MFS_ERR_FLASH_FAILURE if the flash memory is unusable because HW + * failures. Makes the driver enter the + * @p MFS_ERROR state. + * @retval MFS_ERR_INTERNAL if an internal logic failure is detected. + * + * @api + */ +mfs_error_t mfsPerformGarbageCollection(MFSDriver *mfsp) { + + osalDbgCheck(mfsp != NULL); + + if (mfsp->state != MFS_READY) { + return MFS_ERR_INV_STATE; + } + + return mfs_garbage_collect(mfsp); +} + +#if (MFS_CFG_TRANSACTION_MAX > 0) || defined(__DOXYGEN__) +/** + * @brief Puts the driver in transaction mode. + * @note The parameters @p n and @p size are used to make an + * estimation of the space required for the transaction to succeed. + * Note that the estimated size must include also the extra space + * required by alignment enforcement option. If the estimated size + * is wrong (by defect) what could happen is that there is a failure + * in the middle of a transaction and a roll-back would be required. + * @note The conditions for starting a transaction are: + * - The driver must be started. + * - There must be enough compacted storage to accommodate the whole + * transaction. If the required space is available but it is not + * compacted then a garbage collect operation is performed. + * . + * + * @param[in] mfsp pointer to the @p MFSDriver object + * @param[in] size estimated total size of written records in transaction, + * this includes, data, headers and alignment gaps + * @return The operation status. + * @retval MFS_NO_ERROR if the operation has been successfully + * completed. + * @retval MFS_ERR_INV_STATE if the driver is in not in @p MFS_READY + * state. + * @retval MFS_ERR_FLASH_FAILURE if the flash memory is unusable because HW + * failures. Makes the driver enter the + * @p MFS_ERROR state. + * @retval MFS_ERR_INTERNAL if an internal logic failure is detected. + * + * @api + */ +mfs_error_t mfsStartTransaction(MFSDriver *mfsp, size_t size) { + flash_offset_t free, tspace, rspace; + + osalDbgCheck((mfsp != NULL) && (size > ALIGNED_DHDR_SIZE)); + + /* The driver must be in ready mode.*/ + if (mfsp->state != MFS_READY) { + return MFS_ERR_INV_STATE; + } + + /* Estimating the required contiguous compacted space.*/ + tspace = (flash_offset_t)MFS_ALIGN_NEXT(size); + rspace = tspace + ALIGNED_DHDR_SIZE; + + /* If the required space is beyond the available (compacted) block + size then an error is returned.*/ + if (rspace > mfsp->config->bank_size - mfsp->used_space) { + return MFS_ERR_OUT_OF_MEM; + } + + /* Checking for immediately (not compacted) available space.*/ + free = (mfs_flash_get_bank_offset(mfsp, mfsp->current_bank) + + mfsp->config->bank_size) - mfsp->next_offset; + if (rspace > free) { + /* We need to perform a garbage collection, there is enough space + but it has to be freed.*/ + RET_ON_ERROR(mfs_garbage_collect(mfsp)); + } + + /* Entering transaction mode.*/ + mfsp->state = MFS_TRANSACTION; + + /* Initializing transaction state.*/ + mfsp->tr_next_offset = mfsp->next_offset; + mfsp->tr_nops = 0U; + mfsp->tr_limit_offet = mfsp->tr_next_offset + tspace; + + return MFS_NO_ERROR; +} + +/** + * @brief A transaction is committed and finalized atomically. + * + * @param[in] mfsp pointer to the @p MFSDriver object + * @return The operation status. + * @retval MFS_NO_ERROR if the operation has been successfully + * completed. + * @retval MFS_ERR_INV_STATE if the driver is in not in @p MFS_TRANSACTION + * state. + * @retval MFS_ERR_FLASH_FAILURE if the flash memory is unusable because HW + * failures. Makes the driver enter the + * @p MFS_ERROR state. + * @retval MFS_ERR_INTERNAL if an internal logic failure is detected. + * + * @api + */ +mfs_error_t mfsCommitTransaction(MFSDriver *mfsp) { + mfs_transaction_op_t *top; + + osalDbgCheck(mfsp != NULL); + + /* The driver must be in transaction mode.*/ + if (mfsp->state != MFS_TRANSACTION) { + return MFS_ERR_INV_STATE; + } + + /* Scanning all buffered operations in reverse order.*/ + mfsp->buffer.dhdr.fields.magic1 = (uint32_t)MFS_HEADER_MAGIC_1; + mfsp->buffer.dhdr.fields.magic2 = (uint32_t)MFS_HEADER_MAGIC_2; + top = &mfsp->tr_ops[mfsp->tr_nops]; + while (top > &mfsp->tr_ops[0]) { + /* On the previous element.*/ + top--; + + /* Finalizing the operation by writing the magic number.*/ + RET_ON_ERROR(mfs_flash_write(mfsp, + top->offset, + sizeof (uint32_t) * 2U, + mfsp->buffer.data8)); + } + + /* Transaction fully committed by writing the last (first in transaction) + magic number, now updating the internal state using the buffered data.*/ + mfsp->next_offset = mfsp->tr_next_offset; + while (top < &mfsp->tr_ops[mfsp->tr_nops]) { + unsigned i = (unsigned)top->id - 1U; + + /* The calculation is a bit different depending on write or erase record + operations.*/ + if (top->size > 0U) { + /* It is a write.*/ + if (mfsp->descriptors[i].offset != 0U) { + /* The size of the old record instance, if present, must be subtracted + to the total used size.*/ + mfsp->used_space -= ALIGNED_REC_SIZE(mfsp->descriptors[i].size); + } + + /* Adjusting bank-related metadata.*/ + mfsp->used_space += ALIGNED_REC_SIZE(top->size); + mfsp->descriptors[i].offset = top->offset; + mfsp->descriptors[i].size = top->size; + } + else { + /* It is an erase.*/ + mfsp->used_space -= ALIGNED_REC_SIZE(mfsp->descriptors[i].size); + mfsp->descriptors[i].offset = 0U; + mfsp->descriptors[i].size = 0U; + } + + /* On the next element.*/ + top++; + } + + /* Returning to ready mode.*/ + mfsp->state = MFS_READY; + + return MFS_NO_ERROR; +} + +/** + * @brief A transaction is rolled back atomically. + * @details This function performs a garbage collection in order to discard + * all written data that has not been finalized. + * + * @param[in] mfsp pointer to the @p MFSDriver object + * @return The operation status. + * @retval MFS_NO_ERROR if the operation has been successfully + * completed. + * @retval MFS_ERR_INV_STATE if the driver is in not in @p MFS_TRANSACTION + * state. + * @retval MFS_ERR_FLASH_FAILURE if the flash memory is unusable because HW + * failures. Makes the driver enter the + * @p MFS_ERROR state. + * @retval MFS_ERR_INTERNAL if an internal logic failure is detected. + * + * @api + */ +mfs_error_t mfsRollbackTransaction(MFSDriver *mfsp) { + mfs_error_t err; + + osalDbgCheck(mfsp != NULL); + + if (mfsp->state != MFS_TRANSACTION) { + return MFS_ERR_INV_STATE; + } + + /* Returning to ready mode.*/ + mfsp->state = MFS_READY; + + /* If no operations have been performed then there is no need to perform + a garbage collection.*/ + if (mfsp->tr_nops > 0U) { + err = mfs_garbage_collect(mfsp); + } + else { + err = MFS_NO_ERROR; + } + + return err; +} +#endif /* MFS_CFG_TRANSACTION_MAX > 0 */ + +/** @} */ diff --git a/ChibiOS_20.3.2/os/hal/lib/complex/mfs/hal_mfs.h b/ChibiOS_20.3.2/os/hal/lib/complex/mfs/hal_mfs.h new file mode 100644 index 0000000..43ffcbd --- /dev/null +++ b/ChibiOS_20.3.2/os/hal/lib/complex/mfs/hal_mfs.h @@ -0,0 +1,451 @@ +/* + ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio. + + This file is part of ChibiOS. + + ChibiOS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + ChibiOS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +/** + * @file hal_mfs.h + * @brief Managed Flash Storage module header. + * + * @addtogroup HAL_MFS + * @{ + */ + +#ifndef HAL_MFS_H +#define HAL_MFS_H + +#include "hal_flash.h" + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +#define MFS_BANK_MAGIC_1 0xEC705ADEU +#define MFS_BANK_MAGIC_2 0xF0339CC5U +#define MFS_HEADER_MAGIC_1 0x5FAE45F0U +#define MFS_HEADER_MAGIC_2 0xF045AE5FU + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief Maximum number of indexed records in the managed storage. + * @note Record indexes go from 1 to @p MFS_CFG_MAX_RECORDS. + */ +#if !defined(MFS_CFG_MAX_RECORDS) || defined(__DOXYGEN__) +#define MFS_CFG_MAX_RECORDS 32 +#endif + +/** + * @brief Maximum number of repair attempts on partition mount. + */ +#if !defined(MFS_CFG_MAX_REPAIR_ATTEMPTS) || defined(__DOXYGEN__) +#define MFS_CFG_MAX_REPAIR_ATTEMPTS 3 +#endif + +/** + * @brief Verify written data. + */ +#if !defined(MFS_CFG_WRITE_VERIFY) || defined(__DOXYGEN__) +#define MFS_CFG_WRITE_VERIFY TRUE +#endif + +/** + * @brief Enables a stronger and slower check procedure on mount. + * @details Strong checking requires reading of the whole written data and + * this can be slow, normal checking only checks integrity of + * metadata, data errors would be detected on read. + */ +#if !defined(MFS_CFG_STRONG_CHECKING) || defined(__DOXYGEN__) +#define MFS_CFG_STRONG_CHECKING TRUE +#endif + +/** + * @brief Size of the buffer used for data copying. + * @note The buffer size must be a power of two and not smaller than + * 16 bytes. + * @note Larger buffers improve performance, buffers with size multiple + * of the flash program page size work better. + */ +#if !defined(MFS_CFG_BUFFER_SIZE) || defined(__DOXYGEN__) +#define MFS_CFG_BUFFER_SIZE 32 +#endif + +/** + * @brief Enforced memory alignment. + * @details This value must be a power of two, it enforces a memory alignment + * for records in the flash array. This is required when alignment + * constraints exist, for example when using a DTR mode on OSPI + * devices. + */ +#if !defined(MFS_CFG_MEMORY_ALIGNMENT) || defined(__DOXYGEN__) +#define MFS_CFG_MEMORY_ALIGNMENT 2 +#endif + +/** + * @brief Maximum number of objects writable in a single transaction. + */ +#if !defined(MFS_CFG_TRANSACTION_MAX) || defined(__DOXYGEN__) +#define MFS_CFG_TRANSACTION_MAX 16 +#endif +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if MFS_CFG_MAX_RECORDS < 0 +#error "invalid MFS_CFG_MAX_RECORDS value" +#endif + +#if (MFS_CFG_MAX_REPAIR_ATTEMPTS < 1) || \ + (MFS_CFG_MAX_REPAIR_ATTEMPTS > 10) +#error "invalid MFS_MAX_REPAIR_ATTEMPTS value" +#endif + +#if MFS_CFG_BUFFER_SIZE < 16 +#error "invalid MFS_CFG_BUFFER_SIZE value" +#endif + +#if (MFS_CFG_BUFFER_SIZE & (MFS_CFG_BUFFER_SIZE - 1)) != 0 +#error "MFS_CFG_BUFFER_SIZE is not a power of two" +#endif + +#if (MFS_CFG_MEMORY_ALIGNMENT < 1) || \ + (MFS_CFG_MEMORY_ALIGNMENT > MFS_CFG_BUFFER_SIZE) +#error "invalid MFS_CFG_MEMORY_ALIGNMENT value" +#endif + +#if (MFS_CFG_MEMORY_ALIGNMENT & (MFS_CFG_MEMORY_ALIGNMENT - 1)) != 0 +#error "MFS_CFG_MEMORY_ALIGNMENT is not a power of two" +#endif + +#if (MFS_CFG_TRANSACTION_MAX < 0) || \ + (MFS_CFG_TRANSACTION_MAX > MFS_CFG_MAX_RECORDS) +#error "invalid MFS_CFG_TRANSACTION_MAX value" +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief Type of a flash bank. + */ +typedef enum { + MFS_BANK_0 = 0, + MFS_BANK_1 = 1 +} mfs_bank_t; + +/** + * @brief Type of driver state machine states. + */ +typedef enum { + MFS_UNINIT = 0, + MFS_STOP = 1, + MFS_READY = 2, + MFS_TRANSACTION = 3, + MFS_ERROR = 4 +} mfs_state_t; + +/** + * @brief Type of an MFS error code. + * @note Errors are negative integers, informative warnings are positive + * integers. + */ +typedef enum { + MFS_NO_ERROR = 0, + MFS_WARN_REPAIR = 1, + MFS_WARN_GC = 2, + MFS_ERR_INV_STATE = -1, + MFS_ERR_INV_SIZE = -2, + MFS_ERR_NOT_FOUND = -3, + MFS_ERR_OUT_OF_MEM = -4, + MFS_ERR_TRANSACTION_NUM = -5, + MFS_ERR_TRANSACTION_SIZE = -6, + MFS_ERR_NOT_ERASED = -7, + MFS_ERR_FLASH_FAILURE = -8, + MFS_ERR_INTERNAL = -9 +} mfs_error_t; + +/** + * @brief Type of a bank state assessment. + */ +typedef enum { + MFS_BANK_ERASED = 0, + MFS_BANK_OK = 1, + MFS_BANK_GARBAGE = 2 +} mfs_bank_state_t; + +/** + * @brief Type of a record identifier. + */ +typedef uint32_t mfs_id_t; + +/** + * @brief Type of a bank header. + * @note The header resides in the first 16 bytes of a bank. + */ +typedef union { + struct { + /** + * @brief Bank magic 1. + */ + uint32_t magic1; + /** + * @brief Bank magic 2. + */ + uint32_t magic2; + /** + * @brief Usage counter of the bank. + * @details This value is increased each time a bank swap is performed. It + * indicates how much wearing the flash has already endured. + */ + uint32_t counter; + /** + * @brief Reserved field. + */ + uint16_t reserved1; + /** + * @brief Header CRC. + */ + uint16_t crc; + } fields; + uint8_t hdr8[16]; + uint32_t hdr32[4]; +} mfs_bank_header_t; + +/** + * @brief Type of a data block header. + * @details This structure is placed before each written data block. + */ +typedef union { + struct { + /** + * @brief Data header magic 1. + */ + uint32_t magic1; + /** + * @brief Data header magic 2. + */ + uint32_t magic2; + /** + * @brief Record identifier. + */ + uint16_t id; + /** + * @brief Data CRC. + */ + uint16_t crc; + /** + * @brief Data size. + * @note The next record is located at @p MFS_ALIGN_NEXT(size). + */ + uint32_t size; + } fields; + uint8_t hdr8[16]; + uint32_t hdr32[4]; +} mfs_data_header_t; + +typedef struct { + /** + * @brief Offset of the record header. + */ + flash_offset_t offset; + /** + * @brief Record data size. + */ + uint32_t size; +} mfs_record_descriptor_t; + +/** + * @brief Type of a MFS configuration structure. + */ +typedef struct { + /** + * @brief Flash driver associated to this MFS instance. + */ + BaseFlash *flashp; + /** + * @brief Erased value. + */ + uint32_t erased; + /** + * @brief Banks size. + */ + flash_offset_t bank_size; + /** + * @brief Base sector index for bank 0. + */ + flash_sector_t bank0_start; + /** + * @brief Number of sectors for bank 0. + * @note The total size of bank0 sectors must be greater or equal to + * @p bank_size. + */ + flash_sector_t bank0_sectors; + /** + * @brief Base sector index for bank 1. + */ + flash_sector_t bank1_start; + /** + * @brief Number of sectors for bank 1. + * @note The total size of bank1 sectors must be greater or equal to + * @p bank_size. + */ + flash_sector_t bank1_sectors; +} MFSConfig; + +/** + * @brief Type of a buffered write/erase operation within a transaction. + */ +typedef struct { + /** + * @brief Written header offset. + */ + flash_offset_t offset; + /** + * @brief Written data size. + */ + size_t size; + /** + * @brief Record identifier. + */ + mfs_id_t id; +} mfs_transaction_op_t; + +/** + * @brief Type of an MFS instance. + */ +typedef struct { + /** + * @brief Driver state. + */ + mfs_state_t state; + /** + * @brief Current configuration data. + */ + const MFSConfig *config; + /** + * @brief Bank currently in use. + */ + mfs_bank_t current_bank; + /** + * @brief Usage counter of the current bank. + */ + uint32_t current_counter; + /** + * @brief Pointer to the next free position in the current bank. + */ + flash_offset_t next_offset; + /** + * @brief Used space in the current bank without considering erased records. + */ + flash_offset_t used_space; + /** + * @brief Offsets of the most recent instance of the records. + * @note Zero means that there is not a record with that id. + */ + mfs_record_descriptor_t descriptors[MFS_CFG_MAX_RECORDS]; +#if (MFS_CFG_TRANSACTION_MAX > 0) || defined(__DOXYGEN__) + /** + * @brief Next write offset for current transaction. + */ + flash_offset_t tr_next_offset; + /** + * @brief Maximum offset for the transaction. + */ + flash_offset_t tr_limit_offet; + /** + * @brief Number of buffered operations in current transaction. + */ + uint32_t tr_nops; + /** + * @brief Buffered operations in current transaction. + */ + mfs_transaction_op_t tr_ops[MFS_CFG_TRANSACTION_MAX]; +#endif + /** + * @brief Transient buffer. + */ + union { + mfs_data_header_t dhdr; + mfs_bank_header_t bhdr; + uint8_t data8[MFS_CFG_BUFFER_SIZE]; + uint16_t data16[MFS_CFG_BUFFER_SIZE / sizeof (uint16_t)]; + uint32_t data32[MFS_CFG_BUFFER_SIZE / sizeof (uint32_t)]; + } buffer; +} MFSDriver; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/** + * @name Error codes handling macros + * @{ + */ +#define MFS_IS_ERROR(err) ((err) < MFS_NO_ERROR) +#define MFS_IS_WARNING(err) ((err) > MFS_NO_ERROR) +/** @} */ + +/** + * @name Alignment macros + * @{ + */ +#define MFS_ALIGN_MASK ((uint32_t)MFS_CFG_MEMORY_ALIGNMENT - 1U) +#define MFS_IS_ALIGNED(v) (((uint32_t)(v) & MFS_ALIGN_MASK) == 0U) +#define MFS_ALIGN_PREV(v) ((uint32_t)(v) & ~MFS_ALIGN_MASK) +#define MFS_ALIGN_NEXT(v) (MFS_ALIGN_PREV(((uint32_t)(v) - 1U)) + \ + MFS_CFG_MEMORY_ALIGNMENT) +/** @} */ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + void mfsObjectInit(MFSDriver *devp); + mfs_error_t mfsStart(MFSDriver *devp, const MFSConfig *config); + void mfsStop(MFSDriver *devp); + mfs_error_t mfsErase(MFSDriver *mfsp); + mfs_error_t mfsReadRecord(MFSDriver *devp, mfs_id_t id, + size_t *np, uint8_t *buffer); + mfs_error_t mfsWriteRecord(MFSDriver *devp, mfs_id_t id, + size_t n, const uint8_t *buffer); + mfs_error_t mfsEraseRecord(MFSDriver *devp, mfs_id_t id); + mfs_error_t mfsPerformGarbageCollection(MFSDriver *mfsp); +#if MFS_CFG_TRANSACTION_MAX > 0 + mfs_error_t mfsStartTransaction(MFSDriver *mfsp, size_t size); + mfs_error_t mfsCommitTransaction(MFSDriver *mfsp); + mfs_error_t mfsRollbackTransaction(MFSDriver *mfsp); +#endif /* MFS_CFG_TRANSACTION_MAX > 0 */ +#ifdef __cplusplus +} +#endif + +#endif /* HAL_MFS_H */ + +/** @} */ + diff --git a/ChibiOS_20.3.2/os/hal/lib/complex/mfs/hal_mfs.mk b/ChibiOS_20.3.2/os/hal/lib/complex/mfs/hal_mfs.mk new file mode 100644 index 0000000..6448737 --- /dev/null +++ b/ChibiOS_20.3.2/os/hal/lib/complex/mfs/hal_mfs.mk @@ -0,0 +1,9 @@ +# List of all the MFS subsystem files. +MFSSRC := $(CHIBIOS)/os/hal/lib/complex/mfs/hal_mfs.c + +# Required include directories +MFSINC := $(CHIBIOS)/os/hal/lib/complex/mfs + +# Shared variables +ALLCSRC += $(MFSSRC) +ALLINC += $(MFSINC) diff --git a/ChibiOS_20.3.2/os/hal/lib/complex/readme.txt b/ChibiOS_20.3.2/os/hal/lib/complex/readme.txt new file mode 100644 index 0000000..fafdfac --- /dev/null +++ b/ChibiOS_20.3.2/os/hal/lib/complex/readme.txt @@ -0,0 +1,6 @@ +This directory contains complex drivers subsystems. Complex Drivers must +abide to the following rules: +- Must use HAL and OSAL only. +- Cannot use RTOS functionalities. +- Cannot use HW resources directly. +- Must not use non-standard C language features. diff --git a/ChibiOS_20.3.2/os/hal/lib/complex/serial_nor/devices/macronix_mx25/hal_flash_device.c b/ChibiOS_20.3.2/os/hal/lib/complex/serial_nor/devices/macronix_mx25/hal_flash_device.c new file mode 100644 index 0000000..39a7475 --- /dev/null +++ b/ChibiOS_20.3.2/os/hal/lib/complex/serial_nor/devices/macronix_mx25/hal_flash_device.c @@ -0,0 +1,666 @@ +/* + ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio + + 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. +*/ + +/** + * @file hal_flash_device.c + * @brief Macronix MX25 serial flash driver code. + * + * @addtogroup MACRONIX_MX25 + * @{ + */ + +#include + +#include "hal.h" +#include "hal_serial_nor.h" + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +#define PAGE_SIZE 256U +#define PAGE_MASK (PAGE_SIZE - 1U) + +#if MX25_USE_SUB_SECTORS == TRUE +#define SECTOR_SIZE 0x00001000U +#define CMD_SECTOR_ERASE MX25_CMD_SUBSECTOR_ERASE +#else +#define SECTOR_SIZE 0x00010000U +#define CMD_SECTOR_ERASE MX25_CMD_SECTOR_ERASE +#endif + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** + * @brief MX25LM51245G descriptor. + */ +flash_descriptor_t snor_descriptor = { + .attributes = FLASH_ATTR_ERASED_IS_ONE | FLASH_ATTR_REWRITABLE | + FLASH_ATTR_SUSPEND_ERASE_CAPABLE, + .page_size = 256U, + .sectors_count = 0U, /* It is overwritten.*/ + .sectors = NULL, + .sectors_size = SECTOR_SIZE, + .address = 0U, + .size = 0U /* It is overwritten.*/ +}; + +#if (SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI) || defined(__DOXYGEN__) +#if (WSPI_SUPPORTS_MEMMAP == TRUE) || defined(__DOXYGEN__) +/** + * @brief Fast read command for memory mapped mode. + */ +const wspi_command_t snor_memmap_read = { + .addr = 0U, +#if MX25_BUS_MODE == MX25_BUS_MODE_SPI + .cmd = MX25_CMD_SPI_FAST_READ4B, + .dummy = 8, /* Note, always 8 for this command. */ + .cfg = MX25_CFG_C8_A32_DATA_SPI +#elif MX25_BUS_MODE == MX25_BUS_MODE_OPI_STR + .cmd = MX25_CMD_OPI_8READ, + .dummy = MX25_READ_DUMMY_CYCLES, + .cfg = MX25_CFG_C16_A32_DATA_8STR +#elif MX25_BUS_MODE == MX25_BUS_MODE_OPI_DTR + .cmd = MX25_CMD_OPI_8DTRD, + .dummy = MX25_READ_DUMMY_CYCLES, + .cfg = MX25_CFG_C16_A32_DATA_8DTR +#endif +}; +#endif /* WSPI_SUPPORTS_MEMMAP == TRUE */ +#endif /* SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI */ + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +#if SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI +/* Initial MX25_CMD_READ_ID command.*/ +static const wspi_command_t mx25_cmd_read_id = { + +#if MX25_SWITCH_WIDTH == TRUE + .cmd = MX25_CMD_SPI_RDID, + .cfg = MX25_CFG_C8_DATA_SPI, + .dummy = 0, +#else +#if MX25_BUS_MODE == MX25_BUS_MODE_SPI + .cmd = MX25_CMD_SPI_RDID, + .cfg = MX25_CFG_C8_DATA_SPI, + .dummy = 0, +#elif MX25_BUS_MODE == MX25_BUS_MODE_OPI_STR + .cmd = MX25_CMD_OPI_RDID, + .cfg = MX25_CFG_C16_A32_DATA_8STR, + .dummy = 4U, /*Note: always 4 dummies. */ +#elif MX25_BUS_MODE == MX25_BUS_MODE_OPI_DTR + .cmd = MX25_CMD_OPI_RDID, + .cfg = MX25_CFG_C16_A32_DATA_8DTR + .dummy = 4U, /*Note: always 4 dummies. */ +#endif +#endif + .addr = 0, + .alt = 0 +}; + +static const uint8_t mx25_manufacturer_ids[] = MX25_SUPPORTED_MANUFACTURE_IDS; +static const uint8_t mx25_memory_type_ids[] = MX25_SUPPORTED_MEMORY_TYPE_IDS; +#endif /* SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI */ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +static bool mx25_find_id(const uint8_t *set, size_t size, uint8_t element) { + size_t i; + + for (i = 0; i < size; i++) { + if (set[i] == element) { + return true; + } + } + return false; +} + +static flash_error_t mx25_poll_status(SNORDriver *devp) { + uint8_t sts[2], sec[2]; + + do { +#if MX25_NICE_WAITING == TRUE + osalThreadSleepMilliseconds(1); +#endif + /* Read status command.*/ +#if MX25_BUS_MODE == MX25_BUS_MODE_SPI + bus_cmd_receive(devp->config->busp, MX25_CMD_SPI_RDSR, 1U, sts); +#else + bus_cmd_addr_dummy_receive(devp->config->busp, MX25_CMD_OPI_RDSR, + 0U, 4U, 2U, sts); /*Note: always 4 dummies.*/ +#endif + } while ((sts[0] & 1U) != 0U); + + /* Reading security register and checking for errors.*/ +#if MX25_BUS_MODE == MX25_BUS_MODE_SPI + bus_cmd_receive(devp->config->busp, MX25_CMD_SPI_RDSCUR, 1U, sec); +#else + bus_cmd_addr_dummy_receive(devp->config->busp, MX25_CMD_OPI_RDSCUR, + 0U, 4U, 2U, sec); /*Note: always 4 dummies.*/ +#endif + if ((sec[0] & MX25_FLAGS_ALL_ERRORS) != 0U) { + + return FLASH_ERROR_PROGRAM; + } + + return FLASH_NO_ERROR; +} + +#if (SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI) || defined(__DOXYGEN__) +/** + * @brief Device software reset. + * @note It attempts to reset first in supposed final bus mode then tries + * in SPI mode. + * + * @param[in] devp pointer to a @p SNORDriver instance + */ +static void mx25_reset(SNORDriver *devp) { + + /* 1x MX25_CMD_SPI_RSTEN command.*/ + static const wspi_command_t cmd_reset_enable_1 = { + .cmd = MX25_CMD_SPI_RSTEN, + .cfg = MX25_CFG_C8_SPI, + .addr = 0, + .alt = 0, + .dummy = 0 + }; + + /* 1x MX25_CMD_SPI_RST command.*/ + static const wspi_command_t cmd_reset_memory_1 = { + .cmd = MX25_CMD_SPI_RST, + .cfg = MX25_CFG_C8_SPI, + .addr = 0, + .alt = 0, + .dummy = 0 + }; + + /* If the device is in one bit mode then the following commands are + rejected because shorter than 8 bits. If the device is in multiple + bits mode then the commands are accepted and the device is reset to + one bit mode.*/ +#if MX25_BUS_MODE == MX25_BUS_MODE_OPI_DTR + /* 8xDTR MX25_CMD_OPI_RSTEN command.*/ + static const wspi_command_t cmd_reset_enable_8dtr = { + .cmd = MX25_CMD_OPI_RSTEN, + .cfg = MX25_CFG_C16_8DTR, + .addr = 0, + .alt = 0, + .dummy = 0 + }; + + /* 8xDTR MX25_CMD_OPI_RST command.*/ + static const wspi_command_t cmd_reset_memory_8dtr = { + .cmd = MX25_CMD_OPI_RST, + .cfg = MX25_CFG_C16_8DTR, + .addr = 0, + .alt = 0, + .dummy = 0 + }; + + wspiCommand(devp->config->busp, &cmd_reset_enable_8dtr); + wspiCommand(devp->config->busp, &cmd_reset_memory_8dtr); +#else + /* 8xSTR MX25_CMD_OPI_RSTEN command.*/ + static const wspi_command_t cmd_reset_enable_8str = { + .cmd = MX25_CMD_OPI_RSTEN, + .cfg = MX25_CFG_C16_8STR, + .addr = 0, + .alt = 0, + .dummy = 0 + }; + + /* 8xSTR MX25_CMD_OPI_RST command.*/ + static const wspi_command_t cmd_reset_memory_8str = { + .cmd = MX25_CMD_OPI_RST, + .cfg = MX25_CFG_C16_8STR, + .addr = 0, + .alt = 0, + .dummy = 0 + }; + + wspiCommand(devp->config->busp, &cmd_reset_enable_8str); + wspiCommand(devp->config->busp, &cmd_reset_memory_8str); +#endif + + /* Now the device should be in one bit mode for sure and we perform a + device reset.*/ + wspiCommand(devp->config->busp, &cmd_reset_enable_1); + wspiCommand(devp->config->busp, &cmd_reset_memory_1); +} + +/** + * @brief Writes a CR2 register in after-reset bus mode. + * @note This function can only be used before the device is switched to + * the final bus width. + * + * @param[in] devp pointer to a @p SNORDriver instance + * @param[in] addr address field + * @param[in] value value to be written + */ +static void mx25_write_cr2(SNORDriver *devp, uint32_t addr, const uint8_t *value) { + + static const wspi_command_t cmd_write_enable = { +#if MX25_SWITCH_WIDTH == TRUE + .cmd = MX25_CMD_SPI_WREN, + .cfg = MX25_CFG_C8_SPI, +#else +#if MX25_BUS_MODE == MX25_BUS_MODE_SPI + .cmd = MX25_CMD_SPI_WREN, + .cfg = MX25_CFG_C8_SPI, +#elif MX25_BUS_MODE == MX25_BUS_MODE_OPI_STR + .cmd = MX25_CMD_OPI_WREN, + .cfg = MX25_CFG_C16_8STR, +#elif MX25_BUS_MODE == MX25_BUS_MODE_OPI_DTR + .cmd = MX25_CMD_OPI_WREN, + .cfg = MX25_CFG_C16_8DTR, +#endif +#endif + .addr = 0, + .alt = 0, + .dummy = 0 + }; + + const wspi_command_t cmd_write_cr2 = { + +#if MX25_SWITCH_WIDTH == TRUE + .cmd = MX25_CMD_SPI_WRCR2, + .cfg = MX25_CFG_C8_A32_DATA_SPI, +#else +#if MX25_BUS_MODE == MX25_BUS_MODE_SPI + .cmd = MX25_CMD_SPI_WRCR2, + .cfg = MX25_CFG_C8_A32_DATA_SPI, +#elif MX25_BUS_MODE == MX25_BUS_MODE_OPI_STR + .cmd = MX25_CMD_OPI_WRCR2, + .cfg = MX25_CFG_C16_A32_DATA_8STR, +#elif MX25_BUS_MODE == MX25_BUS_MODE_OPI_DTR + .cmd = MX25_CMD_OPI_WRCR2, + .cfg = MX25_CFG_C16_A32_DATA_8DTR, +#endif +#endif + .addr = addr, + .alt = 0, + .dummy = 0 + }; + + wspiCommand(devp->config->busp, &cmd_write_enable); + wspiSend(devp->config->busp, &cmd_write_cr2, 1, value); +} +#endif /* SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI */ + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +/** + * @brief Device initialization. + * + * @param[in] devp pointer to a @p SNORDriver instance + */ +void snor_device_init(SNORDriver *devp) { + +#if SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_SPI + /* Reading device ID.*/ + bus_cmd_receive(devp->config->busp, MX25_CMD_READ_ID, + sizeof devp->device_id, devp->device_id); + +#else /* SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI */ + +#if MX25_RESET_ON_INIT == TRUE + { + /* Attempting a reset of the device, it could be in an unexpected state + because a CPU reset does not reset the memory too.*/ + mx25_reset(devp); + + /* The device requires at least 10uS to recover after a reset, it could + need up to 100mS in cause a reset occurred during a chip erase, 50uS + covers most cases.*/ + osalThreadSleepMicroseconds(50); + } +#endif + + /* Reading device ID and unique ID.*/ + wspiReceive(devp->config->busp, &mx25_cmd_read_id, 3U, devp->device_id); +#endif /* SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI */ + + /* Checking if the device is white listed.*/ + osalDbgAssert(mx25_find_id(mx25_manufacturer_ids, + sizeof mx25_manufacturer_ids, + devp->device_id[0]), + "invalid manufacturer id"); + osalDbgAssert(mx25_find_id(mx25_memory_type_ids, + sizeof mx25_memory_type_ids, + devp->device_id[1]), + "invalid memory type id"); + +#if SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI + /* Setting up the dummy cycles to be used for fast read operations.*/ + { + static const uint8_t v[1] = {~((MX25_READ_DUMMY_CYCLES - 6U) / 2U) & 7U}; + mx25_write_cr2(devp, 0x00000300U, v); + } +#endif + +#if (SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI) && (MX25_SWITCH_WIDTH == TRUE) + { + uint8_t id[8]; +#if MX25_BUS_MODE == MX25_BUS_MODE_SPI + static const uint8_t v[1] = {0x00}; +#elif MX25_BUS_MODE == MX25_BUS_MODE_OPI_STR + static const uint8_t v[1] = {0x01}; +#elif MX25_BUS_MODE == MX25_BUS_MODE_OPI_DTR + static const uint8_t v[1] = {0x02}; +#endif + + /* Setting up final bus width.*/ + mx25_write_cr2(devp, 0x00000000U, v); + + /* Reading ID again for confirmation, in DTR mode bytes are read twice, + it needs adjusting.*/ +#if MX25_BUS_MODE == MX25_BUS_MODE_SPI + bus_cmd_receive(devp->config->busp, MX25_CMD_SPI_RDID, 3U, id); +#elif MX25_BUS_MODE == MX25_BUS_MODE_OPI_STR + bus_cmd_addr_dummy_receive(devp->config->busp, MX25_CMD_OPI_RDID, + 0U, 4U, 3U, id); /*Note: always 4 dummies. */ +#elif MX25_BUS_MODE == MX25_BUS_MODE_OPI_DTR + bus_cmd_addr_dummy_receive(devp->config->busp, MX25_CMD_OPI_RDID, + 0U, 4U, 6U, id); /*Note: always 4 dummies. */ + id[1] = id[2]; + id[2] = id[4]; +#endif + + /* Checking if the device is white listed.*/ + osalDbgAssert(memcmp(id, devp->device_id, 3) == 0, + "id confirmation failed"); + } +#endif + + /* Setting up the device size.*/ + snor_descriptor.sectors_count = (1U << ((size_t)devp->device_id[2] & 0x1FU)) / + SECTOR_SIZE; + snor_descriptor.size = (size_t)snor_descriptor.sectors_count * SECTOR_SIZE; +} + +/** + * @brief Device read. + * + * @param[in] devp pointer to a @p SNORDriver instance + * @param[in] offset flash offset + * @param[in] n number of bytes + * @param[out] rp pointer to the buffer + */ +flash_error_t snor_device_read(SNORDriver *devp, flash_offset_t offset, + size_t n, uint8_t *rp) { + +#if SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI + /* Fast read command in WSPI mode.*/ +#if MX25_BUS_MODE == MX25_BUS_MODE_SPI + bus_cmd_addr_dummy_receive(devp->config->busp, MX25_CMD_SPI_FAST_READ4B, + offset, 8, /* Note, always 8 dummy cycles. */ + n, rp); +#elif MX25_BUS_MODE == MX25_BUS_MODE_OPI_DTR + bus_cmd_addr_dummy_receive(devp->config->busp, MX25_CMD_OPI_8DTRD, + offset, MX25_READ_DUMMY_CYCLES, n, rp); +#elif MX25_BUS_MODE == MX25_BUS_MODE_OPI_STR + bus_cmd_addr_dummy_receive(devp->config->busp, MX25_CMD_OPI_8READ, + offset, MX25_READ_DUMMY_CYCLES, n, rp); +#endif +#else + /* Normal read command in SPI mode.*/ + bus_cmd_addr_receive(devp->config->busp, MX25_CMD_SPI_READ4B, + offset, n, rp); +#endif + + return FLASH_NO_ERROR; +} + + +/** + * @brief Device program. + * + * @param[in] devp pointer to a @p SNORDriver instance + * @param[in] offset flash offset + * @param[in] n number of bytes + * @param[in] pp pointer to the buffer + */ +flash_error_t snor_device_program(SNORDriver *devp, flash_offset_t offset, + size_t n, const uint8_t *pp) { + + /* Data is programmed page by page.*/ + while (n > 0U) { + flash_error_t err; + + /* Data size that can be written in a single program page operation.*/ + size_t chunk = (size_t)(((offset | PAGE_MASK) + 1U) - offset); + if (chunk > n) { + chunk = n; + } + +#if MX25_BUS_MODE == MX25_BUS_MODE_SPI + /* Enabling write operation.*/ + bus_cmd(devp->config->busp, MX25_CMD_SPI_WREN); + + /* Page program command.*/ + bus_cmd_addr_send(devp->config->busp, MX25_CMD_SPI_PP4B, offset, + chunk, pp); +#else + /* Enabling write operation.*/ + bus_cmd(devp->config->busp, MX25_CMD_OPI_WREN); + + /* Page program command.*/ + bus_cmd_addr_send(devp->config->busp, MX25_CMD_OPI_PP, offset, + chunk, pp); +#endif + + /* Wait for status and check errors.*/ + err = mx25_poll_status(devp); + if (err != FLASH_NO_ERROR) { + + return err; + } + + /* Next page.*/ + offset += chunk; + pp += chunk; + n -= chunk; + } + + return FLASH_NO_ERROR; +} + +/** + * @brief Device global erase start. + * + * @param[in] devp pointer to a @p SNORDriver instance + */ +flash_error_t snor_device_start_erase_all(SNORDriver *devp) { + +#if MX25_BUS_MODE == MX25_BUS_MODE_SPI + /* Enabling write operation.*/ + bus_cmd(devp->config->busp, MX25_CMD_SPI_WREN); + + /* Bulk erase command.*/ + bus_cmd(devp->config->busp, MX25_CMD_SPI_CE); +#else + /* Enabling write operation.*/ + bus_cmd(devp->config->busp, MX25_CMD_OPI_WREN); + + /* Bulk erase command.*/ + bus_cmd(devp->config->busp, MX25_CMD_OPI_CE); +#endif + + return FLASH_NO_ERROR; +} + + +/** + * @brief Device sector erase start. + * + * @param[in] devp pointer to a @p SNORDriver instance + * @param[in] sector flash sector + */ +flash_error_t snor_device_start_erase_sector(SNORDriver *devp, + flash_sector_t sector) { + flash_offset_t offset = (flash_offset_t)(sector * SECTOR_SIZE); + +#if MX25_BUS_MODE == MX25_BUS_MODE_SPI + /* Enabling write operation.*/ + bus_cmd(devp->config->busp, MX25_CMD_SPI_WREN); + +#if MX25_USE_SUB_SECTORS == FALSE + /* Block erase command.*/ + bus_cmd_addr(devp->config->busp, MX25_CMD_SPI_BE4B, offset); +#else + /* Sector erase command.*/ + bus_cmd_addr(devp->config->busp, MX25_CMD_SPI_SE4B, offset); +#endif +#else + /* Enabling write operation.*/ + bus_cmd(devp->config->busp, MX25_CMD_OPI_WREN); + +#if MX25_USE_SUB_SECTORS == FALSE + /* Block erase command.*/ + bus_cmd_addr(devp->config->busp, MX25_CMD_OPI_BE, offset); +#else + /* Sector erase command.*/ + bus_cmd_addr(devp->config->busp, MX25_CMD_OPI_SE, offset); +#endif +#endif + + return FLASH_NO_ERROR; +} + +/** + * @brief Device erase verify. + * + * @param[in] devp pointer to a @p SNORDriver instance + * @param[in] sector flash sector + */ +flash_error_t snor_device_verify_erase(SNORDriver *devp, + flash_sector_t sector) { + uint8_t cmpbuf[MX25_COMPARE_BUFFER_SIZE]; + flash_offset_t offset; + size_t n; + + /* Read command.*/ + offset = (flash_offset_t)(sector * SECTOR_SIZE); + n = SECTOR_SIZE; + while (n > 0U) { + uint8_t *p; + +#if SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI +#if MX25_BUS_MODE == MX25_BUS_MODE_SPI + bus_cmd_addr_dummy_receive(devp->config->busp, MX25_CMD_SPI_FAST_READ4B, + offset, 8, /* Note, always 8 dummy cycles. */ + sizeof cmpbuf, cmpbuf); +#elif MX25_BUS_MODE == MX25_BUS_MODE_OPI_STR + bus_cmd_addr_dummy_receive(devp->config->busp, MX25_CMD_OPI_8READ, + offset, MX25_READ_DUMMY_CYCLES, + sizeof cmpbuf, cmpbuf); +#elif MX25_BUS_MODE == MX25_BUS_MODE_OPI_DTR + bus_cmd_addr_dummy_receive(devp->config->busp, MX25_CMD_OPI_8DTRD, + offset, MX25_READ_DUMMY_CYCLES, + sizeof cmpbuf, cmpbuf); +#endif +#else + /* Normal read command in SPI mode.*/ + bus_cmd_addr_receive(devp->config->busp, MX25_CMD_SPI_READ4B, + offset, sizeof cmpbuf, cmpbuf); +#endif + + /* Checking for erased state of current buffer.*/ + for (p = cmpbuf; p < &cmpbuf[MX25_COMPARE_BUFFER_SIZE]; p++) { + if (*p != 0xFFU) { + /* Ready state again.*/ + devp->state = FLASH_READY; + + return FLASH_ERROR_VERIFY; + } + } + + offset += sizeof cmpbuf; + n -= sizeof cmpbuf; + } + + return FLASH_NO_ERROR; +} + +/** + * @brief Queries if there is an erase in progress. + * + * @param[in] devp pointer to a @p SNORDriver instance + * @param[out] msec suggested number of milliseconds before calling this + * function again + */ +flash_error_t snor_device_query_erase(SNORDriver *devp, uint32_t *msec) { + uint8_t sts[2], sec[2]; + + /* Read status register.*/ +#if MX25_BUS_MODE == MX25_BUS_MODE_SPI + bus_cmd_receive(devp->config->busp, MX25_CMD_SPI_RDSR, 1U, sts); +#else + bus_cmd_addr_dummy_receive(devp->config->busp, MX25_CMD_OPI_RDSR, + 0U, 4U, 2U, sts); /*Note: always 4 dummies. */ +#endif + + /* Read security register.*/ +#if MX25_BUS_MODE == MX25_BUS_MODE_SPI + bus_cmd_receive(devp->config->busp, MX25_CMD_SPI_RDSCUR, 1U, sec); +#else + bus_cmd_addr_dummy_receive(devp->config->busp, MX25_CMD_OPI_RDSCUR, + 0U, 4U, 2U, sec); /*Note: always 4 dummies. */ +#endif + + /* If the WIP bit is one (busy) or the flash in a suspended state then + report that the operation is still in progress.*/ + if (((sts[0] & 1) != 0U) || ((sec[0] & 8) != 0U)) { + + /* Recommended time before polling again, this is a simplified + implementation.*/ + if (msec != NULL) { + *msec = 1U; + } + + return FLASH_BUSY_ERASING; + } + + /* Checking for errors.*/ + if ((sec[0] & MX25_FLAGS_ALL_ERRORS) != 0U) { + + /* Erase operation failed.*/ + return FLASH_ERROR_ERASE; + } + + return FLASH_NO_ERROR; +} + +flash_error_t snor_device_read_sfdp(SNORDriver *devp, flash_offset_t offset, + size_t n, uint8_t *rp) { + + (void)devp; + (void)rp; + (void)offset; + (void)n; + + return FLASH_NO_ERROR; +} + +/** @} */ diff --git a/ChibiOS_20.3.2/os/hal/lib/complex/serial_nor/devices/macronix_mx25/hal_flash_device.h b/ChibiOS_20.3.2/os/hal/lib/complex/serial_nor/devices/macronix_mx25/hal_flash_device.h new file mode 100644 index 0000000..45a8ef9 --- /dev/null +++ b/ChibiOS_20.3.2/os/hal/lib/complex/serial_nor/devices/macronix_mx25/hal_flash_device.h @@ -0,0 +1,441 @@ +/* + ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio + + 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. +*/ + +/** + * @file hal_flash_device.h + * @brief Macronix MX25 serial flash driver header. + * + * @addtogroup MACRONIX_MX25 + * @{ + */ + +#ifndef HAL_FLASH_DEVICE_H +#define HAL_FLASH_DEVICE_H + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/** + * @name Device capabilities + * @{ + */ +#define SNOR_DEVICE_SUPPORTS_XIP FALSE +/** @} */ + +/** + * @name Device identification + * @{ + */ +#define MX25_SUPPORTED_MANUFACTURE_IDS {0xC2} +#define MX25_SUPPORTED_MEMORY_TYPE_IDS {0x85} +/** @} */ + +/** + * @name Command codes, SPI mode + * @{ + */ +#define MX25_CMD_SPI_READ3B 0x03U +#define MX25_CMD_SPI_FAST_READ3B 0x0BU +#define MX25_CMD_SPI_PP3B 0x02U +#define MX25_CMD_SPI_SE3B 0x20U +#define MX25_CMD_SPI_BE3B 0xD8U +#define MX25_CMD_SPI_CE 0xC7U +#define MX25_CMD_SPI_READ4B 0x13U +#define MX25_CMD_SPI_FAST_READ4B 0x0CU +#define MX25_CMD_SPI_PP4B 0x12U +#define MX25_CMD_SPI_SE4B 0x21U +#define MX25_CMD_SPI_BE4B 0xDCU +#define MX25_CMD_SPI_WREN 0x06U +#define MX25_CMD_SPI_WRDI 0x04U +#define MX25_CMD_SPI_PE_SUSPEND 0xB0U +#define MX25_CMD_SPI_PE_RESUME 0x30U +#define MX25_CMD_SPI_DP 0xB9U +#define MX25_CMD_SPI_SBL 0xC0U +#define MX25_CMD_SPI_ENSO 0xB1U +#define MX25_CMD_SPI_EXSO 0xC1U +#define MX25_CMD_SPI_NOP 0x00U +#define MX25_CMD_SPI_RSTEN 0x66U +#define MX25_CMD_SPI_RST 0x99U +#define MX25_CMD_SPI_RDID 0x9FU +#define MX25_CMD_SPI_RDSFDP 0x5AU +#define MX25_CMD_SPI_RDSR 0x05U +#define MX25_CMD_SPI_RDCR 0x15U +#define MX25_CMD_SPI_WRSR 0x01U +#define MX25_CMD_SPI_RDCR2 0x71U +#define MX25_CMD_SPI_WRCR2 0x72U +#define MX25_CMD_SPI_RDFBR 0x16U +#define MX25_CMD_SPI_WRFBR 0x17U +#define MX25_CMD_SPI_ESFBR 0x18U +#define MX25_CMD_SPI_RDSCUR 0x2BU +#define MX25_CMD_SPI_WRSCUR 0x2FU +#define MX25_CMD_SPI_WRLR 0x2CU +#define MX25_CMD_SPI_RDLR 0x2DU +#define MX25_CMD_SPI_WRSPB 0xE3U +#define MX25_CMD_SPI_ESSPB 0xE4U +#define MX25_CMD_SPI_RDSPB 0xE2U +#define MX25_CMD_SPI_WRDPB 0xE1U +#define MX25_CMD_SPI_RDDPB 0xE0U +#define MX25_CMD_SPI_WPSEL 0x68U +#define MX25_CMD_SPI_GBLK 0x7EU +#define MX25_CMD_SPI_GBULK 0x98U +#define MX25_CMD_SPI_RDPASS 0x27U +#define MX25_CMD_SPI_WRPASS 0x28U +#define MX25_CMD_SPI_PASSULK 0x29U +/** @} */ + +/** + * @name Command codes, OPI mode + * @{ + */ +#define MX25_CMD_OPI_8READ 0xEC13U +#define MX25_CMD_OPI_8DTRD 0xEE11U +#define MX25_CMD_OPI_RDID 0x9F60U +#define MX25_CMD_OPI_RDSFDP 0x5AA5U +#define MX25_CMD_OPI_PP 0x12EDU +#define MX25_CMD_OPI_SE 0x21DEU +#define MX25_CMD_OPI_BE 0xDC23U +#define MX25_CMD_OPI_CE 0xC738U +#define MX25_CMD_OPI_WREN 0x06F9U +#define MX25_CMD_OPI_WRDI 0x04FBU +#define MX25_CMD_OPI_PE_SUSPEND 0xB04FU +#define MX25_CMD_OPI_PE_RESUME 0x30CFU +#define MX25_CMD_OPI_DP 0xB946U +#define MX25_CMD_OPI_SBL 0xC03FU +#define MX25_CMD_OPI_ENSO 0xB14EU +#define MX25_CMD_OPI_EXSO 0xC13EU +#define MX25_CMD_OPI_NOP 0x00FFU +#define MX25_CMD_OPI_RSTEN 0x6699U +#define MX25_CMD_OPI_RST 0x9966U +#define MX25_CMD_OPI_RDSR 0x05FAU +#define MX25_CMD_OPI_RDCR 0x15EAU +#define MX25_CMD_OPI_WRSR 0x01FEU +#define MX25_CMD_OPI_WRCR 0x01FEU +#define MX25_CMD_OPI_RDCR2 0x718EU +#define MX25_CMD_OPI_WRCR2 0x728DU +#define MX25_CMD_OPI_RDFBR 0x16E9U +#define MX25_CMD_OPI_WRFBR 0x17E8U +#define MX25_CMD_OPI_ESFBR 0x18E7U +#define MX25_CMD_OPI_RDSCUR 0x2BD4U +#define MX25_CMD_OPI_WRSCUR 0x2FD0U +#define MX25_CMD_OPI_WRLR 0x2CD3U +#define MX25_CMD_OPI_RDLR 0x2DD2U +#define MX25_CMD_OPI_WRSPB 0xE31CU +#define MX25_CMD_OPI_ESSPB 0xE41BU +#define MX25_CMD_OPI_RDSPB 0xE21DU +#define MX25_CMD_OPI_WRDPB 0xE11EU +#define MX25_CMD_OPI_RDDPB 0xE01FU +#define MX25_CMD_OPI_WPSEL 0x6897U +#define MX25_CMD_OPI_GBLK 0x7E81U +#define MX25_CMD_OPI_GBULK 0x9867U +#define MX25_CMD_OPI_RDPASS 0x27D8U +#define MX25_CMD_OPI_WRPASS 0x28D7U +#define MX25_CMD_OPI_PASSULK 0x29D6U +/** @} */ + +/** + * @name Flags status register bits + * @{ + */ +#define MX25_FLAGS_WPSEL 0x80U +#define MX25_FLAGS_E_FAIL 0x40U +#define MX25_FLAGS_P_FAIL 0x20U +#define MX25_FLAGS_ESB 0x08U +#define MX25_FLAGS_PSB 0x04U +#define MX25_FLAGS_LDSO 0x02U +#define MX25_FLAGS_SECURED_OTP 0x01U +#define MX25_FLAGS_ALL_ERRORS (MX25_FLAGS_E_FAIL | \ + MX25_FLAGS_P_FAIL) +/** @} */ + +/** + * @name Bus interface modes. + * @{ + */ +#define MX25_BUS_MODE_SPI 0U +#define MX25_BUS_MODE_OPI_STR 1U +#define MX25_BUS_MODE_OPI_DTR 2U +/** @} */ + +/** + * @name MX25-required transfer modes + * @{ + */ +#define MX25_CFG_C8_SPI (WSPI_CFG_CMD_SIZE_8 | \ + WSPI_CFG_CMD_MODE_ONE_LINE | \ + WSPI_CFG_ADDR_MODE_NONE | \ + WSPI_CFG_ALT_MODE_NONE | \ + WSPI_CFG_DATA_MODE_NONE) + +#define MX25_CFG_C16_8STR (WSPI_CFG_CMD_SIZE_16 | \ + WSPI_CFG_CMD_MODE_EIGHT_LINES | \ + WSPI_CFG_ADDR_MODE_NONE | \ + WSPI_CFG_ALT_MODE_NONE | \ + WSPI_CFG_DATA_MODE_NONE) + +#define MX25_CFG_C16_8DTR (WSPI_CFG_CMD_SIZE_16 | \ + WSPI_CFG_CMD_MODE_EIGHT_LINES | \ + WSPI_CFG_ADDR_MODE_NONE | \ + WSPI_CFG_ALT_MODE_NONE | \ + WSPI_CFG_DATA_MODE_NONE | \ + WSPI_CFG_ALL_DTR) + +#define MX25_CFG_C8_A32_SPI (WSPI_CFG_CMD_SIZE_8 | \ + WSPI_CFG_CMD_MODE_ONE_LINE | \ + WSPI_CFG_ADDR_MODE_ONE_LINE | \ + WSPI_CFG_ADDR_SIZE_32 | \ + WSPI_CFG_ALT_MODE_NONE | \ + WSPI_CFG_DATA_MODE_NONE) + +#define MX25_CFG_C16_A32_8STR (WSPI_CFG_CMD_SIZE_16 | \ + WSPI_CFG_CMD_MODE_EIGHT_LINES | \ + WSPI_CFG_ADDR_MODE_EIGHT_LINES | \ + WSPI_CFG_ADDR_SIZE_32 | \ + WSPI_CFG_ALT_MODE_NONE | \ + WSPI_CFG_DATA_MODE_NONE) + +#define MX25_CFG_C16_A32_8DTR (WSPI_CFG_CMD_SIZE_16 | \ + WSPI_CFG_CMD_MODE_EIGHT_LINES | \ + WSPI_CFG_ADDR_MODE_EIGHT_LINES | \ + WSPI_CFG_ADDR_SIZE_32 | \ + WSPI_CFG_ALT_MODE_NONE | \ + WSPI_CFG_DATA_MODE_NONE | \ + WSPI_CFG_ALL_DTR) + +#define MX25_CFG_C8_DATA_SPI (WSPI_CFG_CMD_SIZE_8 | \ + WSPI_CFG_CMD_MODE_ONE_LINE | \ + WSPI_CFG_ADDR_MODE_NONE | \ + WSPI_CFG_ALT_MODE_NONE | \ + WSPI_CFG_DATA_MODE_ONE_LINE) + +#define MX25_CFG_C16_DATA_8STR (WSPI_CFG_CMD_SIZE_16 | \ + WSPI_CFG_CMD_MODE_EIGHT_LINES | \ + WSPI_CFG_ADDR_MODE_NONE | \ + WSPI_CFG_ALT_MODE_NONE | \ + WSPI_CFG_DATA_MODE_EIGHT_LINES) + +#define MX25_CFG_C16_DATA_8DTR (WSPI_CFG_CMD_SIZE_16 | \ + WSPI_CFG_CMD_MODE_EIGHT_LINES | \ + WSPI_CFG_ADDR_MODE_NONE | \ + WSPI_CFG_ALT_MODE_NONE | \ + WSPI_CFG_DATA_MODE_EIGHT_LINES | \ + WSPI_CFG_ALL_DTR | \ + WSPI_CFG_DQS_ENABLE) + +#define MX25_CFG_C8_A32_DATA_SPI (WSPI_CFG_CMD_SIZE_8 | \ + WSPI_CFG_CMD_MODE_ONE_LINE | \ + WSPI_CFG_ADDR_MODE_ONE_LINE | \ + WSPI_CFG_ADDR_SIZE_32 | \ + WSPI_CFG_ALT_MODE_NONE | \ + WSPI_CFG_DATA_MODE_ONE_LINE) + +#define MX25_CFG_C16_A32_DATA_8STR (WSPI_CFG_CMD_SIZE_16 | \ + WSPI_CFG_CMD_MODE_EIGHT_LINES | \ + WSPI_CFG_ADDR_MODE_EIGHT_LINES | \ + WSPI_CFG_ADDR_SIZE_32 | \ + WSPI_CFG_ALT_MODE_NONE | \ + WSPI_CFG_DATA_MODE_EIGHT_LINES) + +#define MX25_CFG_C16_A32_DATA_8DTR (WSPI_CFG_CMD_SIZE_16 | \ + WSPI_CFG_CMD_MODE_EIGHT_LINES | \ + WSPI_CFG_ADDR_MODE_EIGHT_LINES | \ + WSPI_CFG_ADDR_SIZE_32 | \ + WSPI_CFG_ALT_MODE_NONE | \ + WSPI_CFG_DATA_MODE_EIGHT_LINES | \ + WSPI_CFG_ALL_DTR | \ + WSPI_CFG_DQS_ENABLE) +/** @} */ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @brief SW reset on initialization. + * @details Enforces a reset on initialization, this could be required if + * the device is not reset in HW or during debugging. + * @note It is only effective if the WSPI driver is in use, it does + * nothing when SPI driver is used. + */ +#if !defined(MX25_RESET_ON_INIT) || defined(__DOXYGEN__) +#define MX25_RESET_ON_INIT TRUE +#endif + +/** + * @brief Switch WSPI bus width on initialization. + * @details A bus width initialization is performed by writing the + * Enhanced Volatile Configuration Register. If the flash + * device is configured using the Non Volatile Configuration + * Register then this option is not required. + * @note This option is only valid in QSPI bus modes. + */ +#if !defined(MX25_SWITCH_WIDTH) || defined(__DOXYGEN__) +#define MX25_SWITCH_WIDTH TRUE +#endif + +/** + * @brief Device bus mode to be used. + * #note if @p MX25_SWITCH_WIDTH is @p FALSE then this is the bus mode + * that the device is expected to be using. + * #note if @p MX25_SWITCH_WIDTH is @p TRUE then this is the bus mode + * that the device will be switched in. + * @note This option is only valid in WSPI bus mode. + */ +#if !defined(MX25_BUS_MODE) || defined(__DOXYGEN__) +#define MX25_BUS_MODE MX25_BUS_MODE_OPI_DTR +#endif + +/** + * @brief Delays insertions. + * @details If enabled this options inserts delays into the flash waiting + * routines releasing some extra CPU time for threads with lower + * priority, this may slow down the driver a bit however. + */ +#if !defined(MX25_NICE_WAITING) || defined(__DOXYGEN__) +#define MX25_NICE_WAITING TRUE +#endif + +/** + * @brief Uses 4kB sub-sectors rather than 64kB sectors. + */ +#if !defined(MX25_USE_SUB_SECTORS) || defined(__DOXYGEN__) +#define MX25_USE_SUB_SECTORS FALSE +#endif + +/** + * @brief Size of the compare buffer. + * @details This buffer is allocated in the stack frame of the function + * @p flashVerifyErase() and its size must be a power of two. + * Larger buffers lead to better verify performance but increase + * stack usage for that function. + */ +#if !defined(MX25_COMPARE_BUFFER_SIZE) || defined(__DOXYGEN__) +#define MX25_COMPARE_BUFFER_SIZE 32 +#endif + +/** + * @brief Number of dummy cycles for fast read (1..15). + * @details This is the number of dummy cycles to be used for fast read + * operations. + */ +#if !defined(MX25_READ_DUMMY_CYCLES) || defined(__DOXYGEN__) +#define MX25_READ_DUMMY_CYCLES 6 +#endif + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if (MX25_COMPARE_BUFFER_SIZE & (MX25_COMPARE_BUFFER_SIZE - 1)) != 0 +#error "invalid MX25_COMPARE_BUFFER_SIZE value" +#endif + +#if (MX25_READ_DUMMY_CYCLES < 6) || (MX25_READ_DUMMY_CYCLES > 20) || \ + ((MX25_READ_DUMMY_CYCLES & 1) != 0) +#error "invalid MX25_READ_DUMMY_CYCLES value (6, 8, 10, 12, 14, 16, 18, 20)" +#endif + +#if (SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_SPI) && \ + (MX25_BUS_MODE != MX25_BUS_MODE_SPI) +#error "only MX25_BUS_MODE_SPI is allowed when using SPI driver" +#endif + +#if (MX25_BUS_MODE == MX25_BUS_MODE_OPI_DTR) || defined(__DOXYGEN__) +/** + * @brief WSPI settings for command only. + */ +#define SNOR_WSPI_CFG_CMD MX25_CFG_C16_8DTR + +/** + * @brief WSPI settings for command and address. + */ +#define SNOR_WSPI_CFG_CMD_ADDR MX25_CFG_C16_A32_8DTR + +/** + * @brief WSPI settings for command and data. + */ +#define SNOR_WSPI_CFG_CMD_DATA MX25_CFG_C16_DATA_8DTR + +/** + * @brief WSPI settings for command, address and data. + */ +#define SNOR_WSPI_CFG_CMD_ADDR_DATA MX25_CFG_C16_A32_DATA_8DTR + +#elif MX25_BUS_MODE == MX25_BUS_MODE_OPI_STR +#define SNOR_WSPI_CFG_CMD MX25_CFG_C16_8STR +#define SNOR_WSPI_CFG_CMD_ADDR MX25_CFG_C16_A32_8STR +#define SNOR_WSPI_CFG_CMD_DATA MX25_CFG_C16_DATA_8STR +#define SNOR_WSPI_CFG_CMD_ADDR_DATA MX25_CFG_C16_A32_DATA_8STR + +#elif MX25_BUS_MODE == MX25_BUS_MODE_SPI +#define SNOR_WSPI_CFG_CMD MX25_CFG_C8_SPI +#define SNOR_WSPI_CFG_CMD_ADDR MX25_CFG_C8_A32_SPI +#define SNOR_WSPI_CFG_CMD_DATA MX25_CFG_C8_DATA_SPI +#define SNOR_WSPI_CFG_CMD_ADDR_DATA MX25_CFG_C8_A32_DATA_SPI + +#else +#error "invalid MX25_BUS_MODE setting" +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if !defined(__DOXYGEN__) +extern flash_descriptor_t snor_descriptor; +#endif + +#if (SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI) && (WSPI_SUPPORTS_MEMMAP == TRUE) +extern const wspi_command_t snor_memmap_read; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void snor_device_init(SNORDriver *devp); + flash_error_t snor_device_read(SNORDriver *devp, flash_offset_t offset, + size_t n, uint8_t *rp); + flash_error_t snor_device_program(SNORDriver *devp, flash_offset_t offset, + size_t n, const uint8_t *pp); + flash_error_t snor_device_start_erase_all(SNORDriver *devp); + flash_error_t snor_device_start_erase_sector(SNORDriver *devp, + flash_sector_t sector); + flash_error_t snor_device_verify_erase(SNORDriver *devp, + flash_sector_t sector); + flash_error_t snor_device_query_erase(SNORDriver *devp, uint32_t *msec); + flash_error_t snor_device_read_sfdp(SNORDriver *devp, flash_offset_t offset, + size_t n, uint8_t *rp); +#if (SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI) && \ + (SNOR_DEVICE_SUPPORTS_XIP == TRUE) + void snor_activate_xip(SNORDriver *devp); + void snor_reset_xip(SNORDriver *devp); +#endif +#ifdef __cplusplus +} +#endif + +#endif /* HAL_FLASH_DEVICE_H */ + +/** @} */ + diff --git a/ChibiOS_20.3.2/os/hal/lib/complex/serial_nor/devices/macronix_mx25/hal_flash_device.mk b/ChibiOS_20.3.2/os/hal/lib/complex/serial_nor/devices/macronix_mx25/hal_flash_device.mk new file mode 100644 index 0000000..4fb2168 --- /dev/null +++ b/ChibiOS_20.3.2/os/hal/lib/complex/serial_nor/devices/macronix_mx25/hal_flash_device.mk @@ -0,0 +1,11 @@ +# List of all the Micron N25Q device files. +SERNORSRC := $(CHIBIOS)/os/hal/lib/complex/serial_nor/hal_serial_nor.c \ + $(CHIBIOS)/os/hal/lib/complex/serial_nor/devices/macronix_mx25/hal_flash_device.c + +# Required include directories +SERNORINC := $(CHIBIOS)/os/hal/lib/complex/serial_nor \ + $(CHIBIOS)/os/hal/lib/complex/serial_nor/devices/macronix_mx25 + +# Shared variables +ALLCSRC += $(SERNORSRC) +ALLINC += $(SERNORINC) diff --git a/ChibiOS_20.3.2/os/hal/lib/complex/serial_nor/devices/micron_n25q/hal_flash_device.c b/ChibiOS_20.3.2/os/hal/lib/complex/serial_nor/devices/micron_n25q/hal_flash_device.c new file mode 100644 index 0000000..1b5d81f --- /dev/null +++ b/ChibiOS_20.3.2/os/hal/lib/complex/serial_nor/devices/micron_n25q/hal_flash_device.c @@ -0,0 +1,586 @@ +/* + ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio + + 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. +*/ + +/** + * @file hal_flash_device.c + * @brief Micron N25Q serial flash driver code. + * + * @addtogroup MICRON_N25Q + * @{ + */ + +#include + +#include "hal.h" +#include "hal_serial_nor.h" + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +#define PAGE_SIZE 256U +#define PAGE_MASK (PAGE_SIZE - 1U) + +#if N25Q_USE_SUB_SECTORS == TRUE +#define SECTOR_SIZE 0x00001000U +#define CMD_SECTOR_ERASE N25Q_CMD_SUBSECTOR_ERASE +#else +#define SECTOR_SIZE 0x00010000U +#define CMD_SECTOR_ERASE N25Q_CMD_SECTOR_ERASE +#endif + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/** + * @brief N25Q128 descriptor. + */ +flash_descriptor_t snor_descriptor = { + .attributes = FLASH_ATTR_ERASED_IS_ONE | FLASH_ATTR_REWRITABLE | + FLASH_ATTR_SUSPEND_ERASE_CAPABLE, + .page_size = 256U, + .sectors_count = 0U, /* It is overwritten.*/ + .sectors = NULL, + .sectors_size = SECTOR_SIZE, + .address = 0U, + .size = 0U /* It is overwritten.*/ + +}; + +#if (SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI) || defined(__DOXYGEN__) +#if (WSPI_SUPPORTS_MEMMAP == TRUE) || defined(__DOXYGEN__) +/** + * @brief Fast read command for memory mapped mode. + */ +const wspi_command_t snor_memmap_read = { + .cmd = N25Q_CMD_FAST_READ, + .addr = 0, + .dummy = N25Q_READ_DUMMY_CYCLES - 2, + .cfg = WSPI_CFG_ADDR_SIZE_24 | +#if N25Q_BUS_MODE == N25Q_BUS_MODE_WSPI1L + WSPI_CFG_CMD_MODE_ONE_LINE | + WSPI_CFG_ADDR_MODE_ONE_LINE | + WSPI_CFG_DATA_MODE_ONE_LINE | +#elif N25Q_BUS_MODE == N25Q_BUS_MODE_WSPI2L + WSPI_CFG_CMD_MODE_TWO_LINES | + WSPI_CFG_ADDR_MODE_TWO_LINES | + WSPI_CFG_DATA_MODE_TWO_LINES | +#else + WSPI_CFG_CMD_MODE_FOUR_LINES | + WSPI_CFG_ADDR_MODE_FOUR_LINES | + WSPI_CFG_DATA_MODE_FOUR_LINES | +#endif + WSPI_CFG_ALT_MODE_FOUR_LINES | /* Always 4 lines, note.*/ + WSPI_CFG_ALT_SIZE_8 | + WSPI_CFG_SIOO +}; +#endif +#endif + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +#if SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI +/* Initial N25Q_CMD_READ_ID command.*/ +static const wspi_command_t n25q_cmd_read_id = { + .cmd = N25Q_CMD_READ_ID, + .cfg = 0U | +#if N25Q_SWITCH_WIDTH == TRUE + WSPI_CFG_CMD_MODE_ONE_LINE | + WSPI_CFG_DATA_MODE_ONE_LINE, +#else +#if N25Q_BUS_MODE == N25Q_BUS_MODE_WSPI1L + WSPI_CFG_CMD_MODE_ONE_LINE | + WSPI_CFG_DATA_MODE_ONE_LINE, +#elif N25Q_BUS_MODE == N25Q_BUS_MODE_WSPI2L + WSPI_CFG_CMD_MODE_TWO_LINES | + WSPI_CFG_DATA_MODE_TWO_LINES, +#elif N25Q_BUS_MODE == N25Q_BUS_MODE_WSPI4L + WSPI_CFG_CMD_MODE_FOUR_LINES | + WSPI_CFG_DATA_MODE_FOUR_LINES, +#else + WSPI_CFG_CMD_MODE_EIGHT_LINES | + WSPI_CFG_DATA_MODE_EIGHT_LINES, +#endif +#endif + .addr = 0, + .alt = 0, + .dummy = 0 +}; + +/* Initial N25Q_CMD_WRITE_ENHANCED_V_CONF_REGISTER command.*/ +static const wspi_command_t n25q_cmd_write_evconf = { + .cmd = N25Q_CMD_WRITE_ENHANCED_V_CONF_REGISTER, + .cfg = 0U | +#if N25Q_SWITCH_WIDTH == TRUE + WSPI_CFG_CMD_MODE_ONE_LINE | + WSPI_CFG_DATA_MODE_ONE_LINE, +#else +#if N25Q_BUS_MODE == N25Q_BUS_MODE_WSPI1L + WSPI_CFG_CMD_MODE_ONE_LINE | + WSPI_CFG_DATA_MODE_ONE_LINE, +#elif N25Q_BUS_MODE == N25Q_BUS_MODE_WSPI2L + WSPI_CFG_CMD_MODE_TWO_LINES | + WSPI_CFG_DATA_MODE_TWO_LINES, +#elif N25Q_BUS_MODE == N25Q_BUS_MODE_WSPI4L + WSPI_CFG_CMD_MODE_FOUR_LINES | + WSPI_CFG_DATA_MODE_FOUR_LINES, +#else + WSPI_CFG_CMD_MODE_EIGHT_LINES | + WSPI_CFG_DATA_MODE_EIGHT_LINES, +#endif +#endif + .addr = 0, + .alt = 0, + .dummy = 0 +}; + +/* Initial N25Q_CMD_WRITE_ENABLE command.*/ +static const wspi_command_t n25q_cmd_write_enable = { + .cmd = N25Q_CMD_WRITE_ENABLE, + .cfg = 0U | +#if N25Q_SWITCH_WIDTH == TRUE + WSPI_CFG_CMD_MODE_ONE_LINE, +#else +#if N25Q_BUS_MODE == N25Q_BUS_MODE_WSPI1L + WSPI_CFG_CMD_MODE_ONE_LINE, +#elif N25Q_BUS_MODE == N25Q_BUS_MODE_WSPI2L + WSPI_CFG_CMD_MODE_TWO_LINES, +#elif N25Q_BUS_MODE == N25Q_BUS_MODE_WSPI4L + WSPI_CFG_CMD_MODE_FOUR_LINES, +#else + WSPI_CFG_CMD_MODE_EIGHT_LINES, +#endif +#endif + .addr = 0, + .alt = 0, + .dummy = 0 +}; + +/* Bus width initialization.*/ +#if N25Q_BUS_MODE == N25Q_BUS_MODE_WSPI1L +static const uint8_t n25q_evconf_value[1] = {0xCF}; +#elif N25Q_BUS_MODE == N25Q_BUS_MODE_WSPI2L +static const uint8_t n25q_evconf_value[1] = {0x8F}; +#else +static const uint8_t n25q_evconf_value[1] = {0x4F}; +#endif +#endif /* SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI */ + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +static bool n25q_find_id(const uint8_t *set, size_t size, uint8_t element) { + size_t i; + + for (i = 0; i < size; i++) { + if (set[i] == element) { + return true; + } + } + return false; +} + +static flash_error_t n25q_poll_status(SNORDriver *devp) { + uint8_t sts; + + do { +#if N25Q_NICE_WAITING == TRUE + osalThreadSleepMilliseconds(1); +#endif + /* Read status command.*/ + bus_cmd_receive(devp->config->busp, N25Q_CMD_READ_FLAG_STATUS_REGISTER, + 1, &sts); + } while ((sts & N25Q_FLAGS_PROGRAM_ERASE) == 0U); + + /* Checking for errors.*/ + if ((sts & N25Q_FLAGS_ALL_ERRORS) != 0U) { + /* Clearing status register.*/ + bus_cmd(devp->config->busp, N25Q_CMD_CLEAR_FLAG_STATUS_REGISTER); + + /* Program operation failed.*/ + return FLASH_ERROR_PROGRAM; + } + + return FLASH_NO_ERROR; +} + +#if (SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI) || defined(__DOXYGEN__) +static void n25q_reset_memory(SNORDriver *devp) { + + /* 1x N25Q_CMD_RESET_ENABLE command.*/ + static const wspi_command_t cmd_reset_enable_1 = { + .cmd = N25Q_CMD_RESET_ENABLE, + .cfg = WSPI_CFG_CMD_MODE_ONE_LINE, + .addr = 0, + .alt = 0, + .dummy = 0 + }; + + /* 1x N25Q_CMD_RESET_MEMORY command.*/ + static const wspi_command_t cmd_reset_memory_1 = { + .cmd = N25Q_CMD_RESET_MEMORY, + .cfg = WSPI_CFG_CMD_MODE_ONE_LINE, + .addr = 0, + .alt = 0, + .dummy = 0 + }; + + /* If the device is in one bit mode then the following commands are + rejected because shorter than 8 bits. If the device is in multiple + bits mode then the commands are accepted and the device is reset to + one bit mode.*/ +#if N25Q_BUS_MODE == N25Q_BUS_MODE_WSPI4L + /* 4x N25Q_CMD_RESET_ENABLE command.*/ + static const wspi_command_t cmd_reset_enable_4 = { + .cmd = N25Q_CMD_RESET_ENABLE, + .cfg = WSPI_CFG_CMD_MODE_FOUR_LINES, + .addr = 0, + .alt = 0, + .dummy = 0 + }; + + /* 4x N25Q_CMD_RESET_MEMORY command.*/ + static const wspi_command_t cmd_reset_memory_4 = { + .cmd = N25Q_CMD_RESET_MEMORY, + .cfg = WSPI_CFG_CMD_MODE_FOUR_LINES, + .addr = 0, + .alt = 0, + .dummy = 0 + }; + + wspiCommand(devp->config->busp, &cmd_reset_enable_4); + wspiCommand(devp->config->busp, &cmd_reset_memory_4); +#else + /* 2x N25Q_CMD_RESET_ENABLE command.*/ + static const wspi_command_t cmd_reset_enable_2 = { + .cfg = WSPI_CFG_CMD(N25Q_CMD_RESET_ENABLE) | + WSPI_CFG_CMD_MODE_TWO_LINES, + .addr = 0, + .alt = 0, + .dummy = 0 + }; + + /* 2x N25Q_CMD_RESET_MEMORY command.*/ + static const wspi_command_t cmd_reset_memory_2 = { + .cfg = WSPI_CFG_CMD(N25Q_CMD_RESET_MEMORY) | + WSPI_CFG_CMD_MODE_TWO_LINES, + .addr = 0, + .alt = 0, + .dummy = 0 + }; + + wspiCommand(devp->config->busp, &cmd_reset_enable_2); + wspiCommand(devp->config->busp, &cmd_reset_memory_2); +#endif + + /* Now the device should be in one bit mode for sure and we perform a + device reset.*/ + wspiCommand(devp->config->busp, &cmd_reset_enable_1); + wspiCommand(devp->config->busp, &cmd_reset_memory_1); +} +#endif /* SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI */ + +static const uint8_t n25q_manufacturer_ids[] = N25Q_SUPPORTED_MANUFACTURE_IDS; +static const uint8_t n25q_memory_type_ids[] = N25Q_SUPPORTED_MEMORY_TYPE_IDS; + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +void snor_device_init(SNORDriver *devp) { + +#if SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_SPI + /* Reading device ID.*/ + bus_cmd_receive(devp->config->busp, N25Q_CMD_READ_ID, + sizeof devp->device_id, devp->device_id); + +#else /* SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI */ + /* Attempting a reset of the XIP mode, it could be in an unexpected state + because a CPU reset does not reset the memory too.*/ + snor_reset_xip(devp); + + /* Attempting a reset of the device, it could be in an unexpected state + because a CPU reset does not reset the memory too.*/ + n25q_reset_memory(devp); + + /* Reading device ID and unique ID.*/ + wspiReceive(devp->config->busp, &n25q_cmd_read_id, + sizeof devp->device_id, devp->device_id); +#endif /* SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI */ + + /* Checking if the device is white listed.*/ + osalDbgAssert(n25q_find_id(n25q_manufacturer_ids, + sizeof n25q_manufacturer_ids, + devp->device_id[0]), + "invalid manufacturer id"); + osalDbgAssert(n25q_find_id(n25q_memory_type_ids, + sizeof n25q_memory_type_ids, + devp->device_id[1]), + "invalid memory type id"); + +#if (SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI) && (N25Q_SWITCH_WIDTH == TRUE) + /* Setting up final bus width.*/ + wspiCommand(devp->config->busp, &n25q_cmd_write_enable); + wspiSend(devp->config->busp, &n25q_cmd_write_evconf, 1, n25q_evconf_value); + + { + uint8_t id[3]; + + /* Reading ID again for confirmation.*/ + bus_cmd_receive(devp->config->busp, N25Q_CMD_MULTIPLE_IO_READ_ID, 3, id); + + /* Checking if the device is white listed.*/ + osalDbgAssert(memcmp(id, devp->device_id, 3) == 0, + "id confirmation failed"); + } +#endif + + /* Setting up the device size.*/ + snor_descriptor.sectors_count = (1U << (size_t)devp->device_id[2]) / + SECTOR_SIZE; + snor_descriptor.size = (size_t)snor_descriptor.sectors_count * SECTOR_SIZE; + +#if SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI + { + static const uint8_t flash_conf[1] = { + (N25Q_READ_DUMMY_CYCLES << 4U) | 0x0FU + }; + + /* Setting up the dummy cycles to be used for fast read operations.*/ + bus_cmd(devp->config->busp, N25Q_CMD_WRITE_ENABLE); + bus_cmd_send(devp->config->busp, N25Q_CMD_WRITE_V_CONF_REGISTER, + 1, flash_conf); + } +#endif +} + +flash_error_t snor_device_read(SNORDriver *devp, flash_offset_t offset, + size_t n, uint8_t *rp) { + +#if SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI + /* Fast read command in WSPI mode.*/ + bus_cmd_addr_dummy_receive(devp->config->busp, N25Q_CMD_FAST_READ, + offset, N25Q_READ_DUMMY_CYCLES, n, rp); +#else + /* Normal read command in SPI mode.*/ + bus_cmd_addr_receive(devp->config->busp, N25Q_CMD_READ, + offset, n, rp); +#endif + + return FLASH_NO_ERROR; +} + +flash_error_t snor_device_program(SNORDriver *devp, flash_offset_t offset, + size_t n, const uint8_t *pp) { + + /* Data is programmed page by page.*/ + while (n > 0U) { + flash_error_t err; + + /* Data size that can be written in a single program page operation.*/ + size_t chunk = (size_t)(((offset | PAGE_MASK) + 1U) - offset); + if (chunk > n) { + chunk = n; + } + + /* Enabling write operation.*/ + bus_cmd(devp->config->busp, N25Q_CMD_WRITE_ENABLE); + + /* Page program command.*/ + bus_cmd_addr_send(devp->config->busp, N25Q_CMD_PAGE_PROGRAM, offset, + chunk, pp); + + /* Wait for status and check errors.*/ + err = n25q_poll_status(devp); + if (err != FLASH_NO_ERROR) { + + return err; + } + + /* Next page.*/ + offset += chunk; + pp += chunk; + n -= chunk; + } + + return FLASH_NO_ERROR; +} + +flash_error_t snor_device_start_erase_all(SNORDriver *devp) { + + /* Enabling write operation.*/ + bus_cmd(devp->config->busp, N25Q_CMD_WRITE_ENABLE); + + /* Bulk erase command.*/ + bus_cmd(devp->config->busp, N25Q_CMD_BULK_ERASE); + + return FLASH_NO_ERROR; +} + +flash_error_t snor_device_start_erase_sector(SNORDriver *devp, + flash_sector_t sector) { + flash_offset_t offset = (flash_offset_t)(sector * SECTOR_SIZE); + + /* Enabling write operation.*/ + bus_cmd(devp->config->busp, N25Q_CMD_WRITE_ENABLE); + + /* Sector erase command.*/ + bus_cmd_addr(devp->config->busp, N25Q_CMD_SECTOR_ERASE, offset); + + return FLASH_NO_ERROR; +} + +flash_error_t snor_device_verify_erase(SNORDriver *devp, + flash_sector_t sector) { + uint8_t cmpbuf[N25Q_COMPARE_BUFFER_SIZE]; + flash_offset_t offset; + size_t n; + + /* Read command.*/ + offset = (flash_offset_t)(sector * SECTOR_SIZE); + n = SECTOR_SIZE; + while (n > 0U) { + uint8_t *p; + +#if SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI + bus_cmd_addr_dummy_receive(devp->config->busp, N25Q_CMD_FAST_READ, + offset, N25Q_READ_DUMMY_CYCLES, + sizeof cmpbuf, cmpbuf); +#else + /* Normal read command in SPI mode.*/ + bus_cmd_addr_receive(devp->config->busp, N25Q_CMD_READ, + offset, sizeof cmpbuf, cmpbuf); +#endif + + /* Checking for erased state of current buffer.*/ + for (p = cmpbuf; p < &cmpbuf[N25Q_COMPARE_BUFFER_SIZE]; p++) { + if (*p != 0xFFU) { + /* Ready state again.*/ + devp->state = FLASH_READY; + + return FLASH_ERROR_VERIFY; + } + } + + offset += sizeof cmpbuf; + n -= sizeof cmpbuf; + } + + return FLASH_NO_ERROR; +} + +flash_error_t snor_device_query_erase(SNORDriver *devp, uint32_t *msec) { + uint8_t sts; + + /* Read status command.*/ + bus_cmd_receive(devp->config->busp, N25Q_CMD_READ_FLAG_STATUS_REGISTER, + 1, &sts); + + /* If the P/E bit is zero (busy) or the flash in a suspended state then + report that the operation is still in progress.*/ + if (((sts & N25Q_FLAGS_PROGRAM_ERASE) == 0U) || + ((sts & N25Q_FLAGS_ERASE_SUSPEND) != 0U)) { + + /* Recommended time before polling again, this is a simplified + implementation.*/ + if (msec != NULL) { + *msec = 1U; + } + + return FLASH_BUSY_ERASING; + } + + /* Checking for errors.*/ + if ((sts & N25Q_FLAGS_ALL_ERRORS) != 0U) { + + /* Clearing status register.*/ + bus_cmd(devp->config->busp, N25Q_CMD_CLEAR_FLAG_STATUS_REGISTER); + + /* Erase operation failed.*/ + return FLASH_ERROR_ERASE; + } + + return FLASH_NO_ERROR; +} + +flash_error_t snor_device_read_sfdp(SNORDriver *devp, flash_offset_t offset, + size_t n, uint8_t *rp) { + + (void)devp; + (void)rp; + (void)offset; + (void)n; + + return FLASH_NO_ERROR; +} + +#if (SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI) || defined(__DOXYGEN__) +void snor_activate_xip(SNORDriver *devp) { + static const uint8_t flash_status_xip[1] = { + (N25Q_READ_DUMMY_CYCLES << 4U) | 0x07U + }; + + /* Activating XIP mode in the device.*/ + bus_cmd(devp->config->busp, N25Q_CMD_WRITE_ENABLE); + bus_cmd_send(devp->config->busp, N25Q_CMD_WRITE_V_CONF_REGISTER, + 1, flash_status_xip); +} + +void snor_reset_xip(SNORDriver *devp) { + static const uint8_t flash_conf[1] = { + (N25Q_READ_DUMMY_CYCLES << 4U) | 0x0FU + }; + wspi_command_t cmd; + uint8_t buf[1]; + + /* Resetting XIP mode by reading one byte without XIP confirmation bit.*/ + cmd.cmd = 0U; + cmd.alt = 0xFFU; + cmd.addr = 0U; + cmd.dummy = N25Q_READ_DUMMY_CYCLES - 2U; + cmd.cfg = WSPI_CFG_CMD_MODE_NONE | + WSPI_CFG_ADDR_SIZE_24 | +#if N25Q_BUS_MODE == N25Q_BUS_MODE_WSPI1L + WSPI_CFG_ADDR_MODE_ONE_LINE | + WSPI_CFG_DATA_MODE_ONE_LINE | +#elif N25Q_BUS_MODE == N25Q_BUS_MODE_WSPI2L + WSPI_CFG_ADDR_MODE_TWO_LINES | + WSPI_CFG_DATA_MODE_TWO_LINES | +#elif N25Q_BUS_MODE == N25Q_BUS_MODE_WSPI4L + WSPI_CFG_ADDR_MODE_FOUR_LINES | + WSPI_CFG_DATA_MODE_FOUR_LINES | +#else + WSPI_CFG_ADDR_MODE_EIGHT_LINES | + WSPI_CFG_DATA_MODE_EIGHT_LINES | +#endif + WSPI_CFG_ALT_MODE_FOUR_LINES | /* Always 4 lines, note.*/ + WSPI_CFG_ALT_SIZE_8; + wspiReceive(devp->config->busp, &cmd, 1, buf); + + /* Enabling write operation.*/ + bus_cmd(devp->config->busp, N25Q_CMD_WRITE_ENABLE); + + /* Rewriting volatile configuration register.*/ + bus_cmd_send(devp->config->busp, N25Q_CMD_WRITE_V_CONF_REGISTER, + 1, flash_conf); +} +#endif /* SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI */ + +/** @} */ diff --git a/ChibiOS_20.3.2/os/hal/lib/complex/serial_nor/devices/micron_n25q/hal_flash_device.h b/ChibiOS_20.3.2/os/hal/lib/complex/serial_nor/devices/micron_n25q/hal_flash_device.h new file mode 100644 index 0000000..14d00c7 --- /dev/null +++ b/ChibiOS_20.3.2/os/hal/lib/complex/serial_nor/devices/micron_n25q/hal_flash_device.h @@ -0,0 +1,337 @@ +/* + ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio + + 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. +*/ + +/** + * @file hal_flash_device.h + * @brief Micron N25Q serial flash driver header. + * + * @addtogroup MICRON_N25Q + * @{ + */ + +#ifndef HAL_FLASH_DEVICE_H +#define HAL_FLASH_DEVICE_H + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/** + * @name Device capabilities + * @{ + */ +#define SNOR_DEVICE_SUPPORTS_XIP TRUE +/** @} */ + +/** + * @name Device identification + * @{ + */ +#define N25Q_SUPPORTED_MANUFACTURE_IDS {0x20} +#define N25Q_SUPPORTED_MEMORY_TYPE_IDS {0xBA, 0xBB} +/** @} */ + +/** + * @name Command codes + * @{ + */ +#define N25Q_CMD_RESET_ENABLE 0x66 +#define N25Q_CMD_RESET_MEMORY 0x99 +#define N25Q_CMD_READ_ID 0x9F +#define N25Q_CMD_MULTIPLE_IO_READ_ID 0xAF +#define N25Q_CMD_READ_DISCOVERY_PARAMETER 0x5A +#define N25Q_CMD_READ 0x03 +#define N25Q_CMD_FAST_READ 0x0B +#define N25Q_CMD_WRITE_ENABLE 0x06 +#define N25Q_CMD_WRITE_DISABLE 0x04 +#define N25Q_CMD_READ_STATUS_REGISTER 0x05 +#define N25Q_CMD_WRITE_STATUS_REGISTER 0x01 +#define N25Q_CMD_READ_LOCK_REGISTER 0xE8 +#define N25Q_CMD_WRITE_LOCK_REGISTER 0xE5 +#define N25Q_CMD_READ_FLAG_STATUS_REGISTER 0x70 +#define N25Q_CMD_CLEAR_FLAG_STATUS_REGISTER 0x50 +#define N25Q_CMD_READ_NV_CONFIGURATION_REGISTER 0xB5 +#define N25Q_CMD_WRITE_NV_CONFIGURATION_REGISTER 0xB1 +#define N25Q_CMD_READ_V_CONF_REGISTER 0x85 +#define N25Q_CMD_WRITE_V_CONF_REGISTER 0x81 +#define N25Q_CMD_READ_ENHANCED_V_CONF_REGISTER 0x65 +#define N25Q_CMD_WRITE_ENHANCED_V_CONF_REGISTER 0x61 +#define N25Q_CMD_PAGE_PROGRAM 0x02 +#define N25Q_CMD_SUBSECTOR_ERASE 0x20 +#define N25Q_CMD_SECTOR_ERASE 0xD8 +#define N25Q_CMD_BULK_ERASE 0xC7 +#define N25Q_CMD_PROGRAM_ERASE_RESUME 0x7A +#define N25Q_CMD_PROGRAM_ERASE_SUSPEND 0x75 +#define N25Q_CMD_READ_OTP_ARRAY 0x4B +#define N25Q_CMD_PROGRAM_OTP_ARRAY 0x42 +/** @} */ + +/** + * @name Flags status register bits + * @{ + */ +#define N25Q_FLAGS_PROGRAM_ERASE 0x80U +#define N25Q_FLAGS_ERASE_SUSPEND 0x40U +#define N25Q_FLAGS_ERASE_ERROR 0x20U +#define N25Q_FLAGS_PROGRAM_ERROR 0x10U +#define N25Q_FLAGS_VPP_ERROR 0x08U +#define N25Q_FLAGS_PROGRAM_SUSPEND 0x04U +#define N25Q_FLAGS_PROTECTION_ERROR 0x02U +#define N25Q_FLAGS_RESERVED 0x01U +#define N25Q_FLAGS_ALL_ERRORS (N25Q_FLAGS_ERASE_ERROR | \ + N25Q_FLAGS_PROGRAM_ERROR | \ + N25Q_FLAGS_VPP_ERROR | \ + N25Q_FLAGS_PROTECTION_ERROR) +/** @} */ + +/** + * @name Bus interface modes. + * @{ + */ +#define N25Q_BUS_MODE_WSPI1L 1U +#define N25Q_BUS_MODE_WSPI2L 2U +#define N25Q_BUS_MODE_WSPI4L 4U +/** @} */ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @brief Switch WSPI bus width on initialization. + * @details A bus width initialization is performed by writing the + * Enhanced Volatile Configuration Register. If the flash + * device is configured using the Non Volatile Configuration + * Register then this option is not required. + * @note This option is only valid in WSPI bus mode. + */ +#if !defined(N25Q_SWITCH_WIDTH) || defined(__DOXYGEN__) +#define N25Q_SWITCH_WIDTH TRUE +#endif + +/** + * @brief Device bus mode to be used. + * #note if @p N25Q_SWITCH_WIDTH is @p FALSE then this is the bus mode + * that the device is expected to be using. + * #note if @p N25Q_SWITCH_WIDTH is @p TRUE then this is the bus mode + * that the device will be switched in. + * @note This option is only valid in WSPI bus mode. + */ +#if !defined(N25Q_BUS_MODE) || defined(__DOXYGEN__) +#define N25Q_BUS_MODE N25Q_BUS_MODE_WSPI4L +#endif + +/** + * @brief Delays insertions. + * @details If enabled this options inserts delays into the flash waiting + * routines releasing some extra CPU time for threads with lower + * priority, this may slow down the driver a bit however. + */ +#if !defined(N25Q_NICE_WAITING) || defined(__DOXYGEN__) +#define N25Q_NICE_WAITING TRUE +#endif + +/** + * @brief Uses 4kB sub-sectors rather than 64kB sectors. + */ +#if !defined(N25Q_USE_SUB_SECTORS) || defined(__DOXYGEN__) +#define N25Q_USE_SUB_SECTORS FALSE +#endif + +/** + * @brief Size of the compare buffer. + * @details This buffer is allocated in the stack frame of the function + * @p flashVerifyErase() and its size must be a power of two. + * Larger buffers lead to better verify performance but increase + * stack usage for that function. + */ +#if !defined(N25Q_COMPARE_BUFFER_SIZE) || defined(__DOXYGEN__) +#define N25Q_COMPARE_BUFFER_SIZE 32 +#endif + +/** + * @brief Number of dummy cycles for fast read (1..15). + * @details This is the number of dummy cycles to be used for fast read + * operations. + */ +#if !defined(N25Q_READ_DUMMY_CYCLES) || defined(__DOXYGEN__) +#define N25Q_READ_DUMMY_CYCLES 8 +#endif + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if (N25Q_COMPARE_BUFFER_SIZE & (N25Q_COMPARE_BUFFER_SIZE - 1)) != 0 +#error "invalid N25Q_COMPARE_BUFFER_SIZE value" +#endif + +#if (N25Q_READ_DUMMY_CYCLES < 1) || (N25Q_READ_DUMMY_CYCLES > 15) +#error "invalid N25Q_READ_DUMMY_CYCLES value (1..15)" +#endif + +#if (N25Q_BUS_MODE == N25Q_BUS_MODE_WSPI4L) || defined(__DOXYGEN__) +/** + * @brief WSPI settings for command only. + */ +#define SNOR_WSPI_CFG_CMD (WSPI_CFG_CMD_MODE_FOUR_LINES | \ + WSPI_CFG_ADDR_MODE_NONE | \ + WSPI_CFG_ALT_MODE_NONE | \ + WSPI_CFG_DATA_MODE_NONE | \ + WSPI_CFG_CMD_SIZE_8 | \ + WSPI_CFG_ADDR_SIZE_24) + +/** + * @brief WSPI settings for command and address. + */ +#define SNOR_WSPI_CFG_CMD_ADDR (WSPI_CFG_CMD_MODE_FOUR_LINES | \ + WSPI_CFG_ADDR_MODE_FOUR_LINES | \ + WSPI_CFG_ALT_MODE_NONE | \ + WSPI_CFG_DATA_MODE_NONE | \ + WSPI_CFG_CMD_SIZE_8 | \ + WSPI_CFG_ADDR_SIZE_24) + +/** + * @brief WSPI settings for command and data. + */ +#define SNOR_WSPI_CFG_CMD_DATA (WSPI_CFG_CMD_MODE_FOUR_LINES | \ + WSPI_CFG_ADDR_MODE_NONE | \ + WSPI_CFG_ALT_MODE_NONE | \ + WSPI_CFG_DATA_MODE_FOUR_LINES | \ + WSPI_CFG_CMD_SIZE_8 | \ + WSPI_CFG_ADDR_SIZE_24) + +/** + * @brief WSPI settings for command, address and data. + */ +#define SNOR_WSPI_CFG_CMD_ADDR_DATA (WSPI_CFG_CMD_MODE_FOUR_LINES | \ + WSPI_CFG_ADDR_MODE_FOUR_LINES | \ + WSPI_CFG_ALT_MODE_NONE | \ + WSPI_CFG_DATA_MODE_FOUR_LINES | \ + WSPI_CFG_CMD_SIZE_8 | \ + WSPI_CFG_ADDR_SIZE_24) + +#elif N25Q_BUS_MODE == N25Q_BUS_MODE_WSPI2L +#define SNOR_WSPI_CFG_CMD (WSPI_CFG_CMD_MODE_TWO_LINES | \ + WSPI_CFG_ADDR_MODE_NONE | \ + WSPI_CFG_ALT_MODE_NONE | \ + WSPI_CFG_DATA_MODE_NONE | \ + WSPI_CFG_CMD_SIZE_8 | \ + WSPI_CFG_ADDR_SIZE_24) + +#define SNOR_WSPI_CFG_CMD_ADDR (WSPI_CFG_CMD_MODE_TWO_LINES | \ + WSPI_CFG_ADDR_MODE_TWO_LINES | \ + WSPI_CFG_ALT_MODE_NONE | \ + WSPI_CFG_DATA_MODE_NONE | \ + WSPI_CFG_CMD_SIZE_8 | \ + WSPI_CFG_ADDR_SIZE_24) + +#define SNOR_WSPI_CFG_CMD_DATA (WSPI_CFG_CMD_MODE_TWO_LINES | \ + WSPI_CFG_ADDR_MODE_NONE | \ + WSPI_CFG_ALT_MODE_NONE | \ + WSPI_CFG_DATA_MODE_TWO_LINES | \ + WSPI_CFG_CMD_SIZE_8 | \ + WSPI_CFG_ADDR_SIZE_24) + +#define SNOR_WSPI_CFG_CMD_ADDR_DATA (WSPI_CFG_CMD_MODE_ONE_LINE | \ + WSPI_CFG_ADDR_MODE_ONE_LINE | \ + WSPI_CFG_ALT_MODE_NONE | \ + WSPI_CFG_DATA_MODE_ONE_LINE | \ + WSPI_CFG_CMD_SIZE_8 | \ + WSPI_CFG_ADDR_SIZE_24) + +#elif N25Q_BUS_MODE == N25Q_BUS_MODE_WSPI1L +#define SNOR_WSPI_CFG_CMD (WSPI_CFG_CMD_MODE_ONE_LINE | \ + WSPI_CFG_ADDR_MODE_NONE | \ + WSPI_CFG_ALT_MODE_NONE | \ + WSPI_CFG_DATA_MODE_NONE | \ + WSPI_CFG_CMD_SIZE_8 | \ + WSPI_CFG_ADDR_SIZE_24) + +#define SNOR_WSPI_CFG_CMD_ADDR (WSPI_CFG_CMD_MODE_ONE_LINE | \ + WSPI_CFG_ADDR_MODE_ONE_LINE | \ + WSPI_CFG_ALT_MODE_NONE | \ + WSPI_CFG_DATA_MODE_NONE | \ + WSPI_CFG_CMD_SIZE_8 | \ + WSPI_CFG_ADDR_SIZE_24) + +#define SNOR_WSPI_CFG_CMD_DATA (WSPI_CFG_CMD_MODE_ONE_LINE | \ + WSPI_CFG_ADDR_MODE_NONE | \ + WSPI_CFG_ALT_MODE_NONE | \ + WSPI_CFG_DATA_MODE_ONE_LINE | \ + WSPI_CFG_CMD_SIZE_8 | \ + WSPI_CFG_ADDR_SIZE_24) + +#define SNOR_WSPI_CFG_CMD_ADDR_DATA (WSPI_CFG_CMD_MODE_ONE_LINE | \ + WSPI_CFG_ADDR_MODE_ONE_LINE | \ + WSPI_CFG_ALT_MODE_NONE | \ + WSPI_CFG_DATA_MODE_ONE_LINE | \ + WSPI_CFG_CMD_SIZE_8 | \ + WSPI_CFG_ADDR_SIZE_24) + +#else +#error "invalid N25Q_BUS_MODE setting" +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if !defined(__DOXYGEN__) +extern flash_descriptor_t snor_descriptor; +#endif + +#if (SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI) && (WSPI_SUPPORTS_MEMMAP == TRUE) +extern const wspi_command_t snor_memmap_read; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + void snor_device_init(SNORDriver *devp); + flash_error_t snor_device_read(SNORDriver *devp, flash_offset_t offset, + size_t n, uint8_t *rp); + flash_error_t snor_device_program(SNORDriver *devp, flash_offset_t offset, + size_t n, const uint8_t *pp); + flash_error_t snor_device_start_erase_all(SNORDriver *devp); + flash_error_t snor_device_start_erase_sector(SNORDriver *devp, + flash_sector_t sector); + flash_error_t snor_device_verify_erase(SNORDriver *devp, + flash_sector_t sector); + flash_error_t snor_device_query_erase(SNORDriver *devp, uint32_t *msec); + flash_error_t snor_device_read_sfdp(SNORDriver *devp, flash_offset_t offset, + size_t n, uint8_t *rp); +#if (SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI) && \ + (SNOR_DEVICE_SUPPORTS_XIP == TRUE) + void snor_activate_xip(SNORDriver *devp); + void snor_reset_xip(SNORDriver *devp); +#endif +#ifdef __cplusplus +} +#endif + +#endif /* HAL_FLASH_DEVICE_H */ + +/** @} */ + diff --git a/ChibiOS_20.3.2/os/hal/lib/complex/serial_nor/devices/micron_n25q/hal_flash_device.mk b/ChibiOS_20.3.2/os/hal/lib/complex/serial_nor/devices/micron_n25q/hal_flash_device.mk new file mode 100644 index 0000000..43ddcef --- /dev/null +++ b/ChibiOS_20.3.2/os/hal/lib/complex/serial_nor/devices/micron_n25q/hal_flash_device.mk @@ -0,0 +1,11 @@ +# List of all the Micron N25Q device files. +SNORSRC := $(CHIBIOS)/os/hal/lib/complex/serial_nor/hal_serial_nor.c \ + $(CHIBIOS)/os/hal/lib/complex/serial_nor/devices/micron_n25q/hal_flash_device.c + +# Required include directories +SNORINC := $(CHIBIOS)/os/hal/lib/complex/serial_nor \ + $(CHIBIOS)/os/hal/lib/complex/serial_nor/devices/micron_n25q + +# Shared variables +ALLCSRC += $(SNORSRC) +ALLINC += $(SNORINC) diff --git a/ChibiOS_20.3.2/os/hal/lib/complex/serial_nor/hal_serial_nor.c b/ChibiOS_20.3.2/os/hal/lib/complex/serial_nor/hal_serial_nor.c new file mode 100644 index 0000000..82d9083 --- /dev/null +++ b/ChibiOS_20.3.2/os/hal/lib/complex/serial_nor/hal_serial_nor.c @@ -0,0 +1,770 @@ +/* + ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio + + 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. +*/ + +/** + * @file hal_serial_nor.c + * @brief Serial NOR serial flash driver code. + * + * @addtogroup HAL_SERIAL_NOR + * @{ + */ + +#include "hal.h" +#include "hal_serial_nor.h" + +/*===========================================================================*/ +/* Driver local definitions. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver exported variables. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +static const flash_descriptor_t *snor_get_descriptor(void *instance); +static flash_error_t snor_read(void *instance, flash_offset_t offset, + size_t n, uint8_t *rp); +static flash_error_t snor_program(void *instance, flash_offset_t offset, + size_t n, const uint8_t *pp); +static flash_error_t snor_start_erase_all(void *instance); +static flash_error_t snor_start_erase_sector(void *instance, + flash_sector_t sector); +static flash_error_t snor_verify_erase(void *instance, + flash_sector_t sector); +static flash_error_t snor_query_erase(void *instance, uint32_t *msec); +static flash_error_t snor_read_sfdp(void *instance, flash_offset_t offset, + size_t n, uint8_t *rp); + +/** + * @brief Virtual methods table. + */ +static const struct SNORDriverVMT snor_vmt = { + (size_t)0, + snor_get_descriptor, snor_read, snor_program, + snor_start_erase_all, snor_start_erase_sector, + snor_query_erase, snor_verify_erase, + snor_read_sfdp +}; + +/*===========================================================================*/ +/* Driver local functions. */ +/*===========================================================================*/ + +/** + * @brief Returns a pointer to the device descriptor. + * + * @param[in] instance instance pointer + * @return Pointer to a static descriptor structure. + */ +static const flash_descriptor_t *snor_get_descriptor(void *instance) { + SNORDriver *devp = (SNORDriver *)instance; + + osalDbgCheck(instance != NULL); + osalDbgAssert((devp->state != FLASH_UNINIT) && (devp->state != FLASH_STOP), + "invalid state"); + + return &snor_descriptor; +} + +static flash_error_t snor_read(void *instance, flash_offset_t offset, + size_t n, uint8_t *rp) { + SNORDriver *devp = (SNORDriver *)instance; + flash_error_t err; + + osalDbgCheck((instance != NULL) && (rp != NULL) && (n > 0U)); + osalDbgCheck((size_t)offset + n <= (size_t)snor_descriptor.sectors_count * + (size_t)snor_descriptor.sectors_size); + osalDbgAssert((devp->state == FLASH_READY) || (devp->state == FLASH_ERASE), + "invalid state"); + + if (devp->state == FLASH_ERASE) { + return FLASH_BUSY_ERASING; + } + + /* Bus acquired.*/ + bus_acquire(devp->config->busp, devp->config->buscfg); + + /* FLASH_READY state while the operation is performed.*/ + devp->state = FLASH_READ; + + /* Actual read implementation.*/ + err = snor_device_read(devp, offset, n, rp); + + /* Ready state again.*/ + devp->state = FLASH_READY; + + /* Bus released.*/ + bus_release(devp->config->busp); + + return err; +} + +static flash_error_t snor_program(void *instance, flash_offset_t offset, + size_t n, const uint8_t *pp) { + SNORDriver *devp = (SNORDriver *)instance; + flash_error_t err; + + osalDbgCheck((instance != NULL) && (pp != NULL) && (n > 0U)); + osalDbgCheck((size_t)offset + n <= (size_t)snor_descriptor.sectors_count * + (size_t)snor_descriptor.sectors_size); + osalDbgAssert((devp->state == FLASH_READY) || (devp->state == FLASH_ERASE), + "invalid state"); + + if (devp->state == FLASH_ERASE) { + return FLASH_BUSY_ERASING; + } + + /* Bus acquired.*/ + bus_acquire(devp->config->busp, devp->config->buscfg); + + /* FLASH_PGM state while the operation is performed.*/ + devp->state = FLASH_PGM; + + /* Actual program implementation.*/ + err = snor_device_program(devp, offset, n, pp); + + /* Ready state again.*/ + devp->state = FLASH_READY; + + /* Bus released.*/ + bus_release(devp->config->busp); + + return err; +} + +static flash_error_t snor_start_erase_all(void *instance) { + SNORDriver *devp = (SNORDriver *)instance; + flash_error_t err; + + osalDbgCheck(instance != NULL); + osalDbgAssert((devp->state == FLASH_READY) || (devp->state == FLASH_ERASE), + "invalid state"); + + if (devp->state == FLASH_ERASE) { + return FLASH_BUSY_ERASING; + } + + /* Bus acquired.*/ + bus_acquire(devp->config->busp, devp->config->buscfg); + + /* FLASH_ERASE state while the operation is performed.*/ + devp->state = FLASH_ERASE; + + /* Actual erase implementation.*/ + err = snor_device_start_erase_all(devp); + + /* Ready state again.*/ + devp->state = FLASH_READY; + + /* Bus released.*/ + bus_release(devp->config->busp); + + return err; +} + +static flash_error_t snor_start_erase_sector(void *instance, + flash_sector_t sector) { + SNORDriver *devp = (SNORDriver *)instance; + flash_error_t err; + + osalDbgCheck(instance != NULL); + osalDbgCheck(sector < snor_descriptor.sectors_count); + osalDbgAssert((devp->state == FLASH_READY) || (devp->state == FLASH_ERASE), + "invalid state"); + + if (devp->state == FLASH_ERASE) { + return FLASH_BUSY_ERASING; + } + + /* Bus acquired.*/ + bus_acquire(devp->config->busp, devp->config->buscfg); + + /* FLASH_ERASE state while the operation is performed.*/ + devp->state = FLASH_ERASE; + + /* Actual erase implementation.*/ + err = snor_device_start_erase_sector(devp, sector); + + /* Bus released.*/ + bus_release(devp->config->busp); + + return err; +} + +static flash_error_t snor_verify_erase(void *instance, + flash_sector_t sector) { + SNORDriver *devp = (SNORDriver *)instance; + flash_error_t err; + + osalDbgCheck(instance != NULL); + osalDbgCheck(sector < snor_descriptor.sectors_count); + osalDbgAssert((devp->state == FLASH_READY) || (devp->state == FLASH_ERASE), + "invalid state"); + + if (devp->state == FLASH_ERASE) { + return FLASH_BUSY_ERASING; + } + + /* Bus acquired.*/ + bus_acquire(devp->config->busp, devp->config->buscfg); + + /* FLASH_READY state while the operation is performed.*/ + devp->state = FLASH_READ; + + /* Actual verify erase implementation.*/ + err = snor_device_verify_erase(devp, sector); + + /* Ready state again.*/ + devp->state = FLASH_READY; + + /* Bus released.*/ + bus_release(devp->config->busp); + + return err; +} + +static flash_error_t snor_query_erase(void *instance, uint32_t *msec) { + SNORDriver *devp = (SNORDriver *)instance; + flash_error_t err; + + osalDbgCheck(instance != NULL); + osalDbgAssert((devp->state == FLASH_READY) || (devp->state == FLASH_ERASE), + "invalid state"); + + /* If there is an erase in progress then the device must be checked.*/ + if (devp->state == FLASH_ERASE) { + + /* Bus acquired.*/ + bus_acquire(devp->config->busp, devp->config->buscfg); + + /* Actual query erase implementation.*/ + err = snor_device_query_erase(devp, msec); + + /* The device is ready to accept commands.*/ + if (err == FLASH_NO_ERROR) { + devp->state = FLASH_READY; + } + + /* Bus released.*/ + bus_release(devp->config->busp); + } + else { + err = FLASH_NO_ERROR; + } + + return err; +} + +static flash_error_t snor_read_sfdp(void *instance, flash_offset_t offset, + size_t n, uint8_t *rp) { + SNORDriver *devp = (SNORDriver *)instance; + flash_error_t err; + + osalDbgCheck((instance != NULL) && (rp != NULL) && (n > 0U)); + osalDbgAssert((devp->state == FLASH_READY) || (devp->state == FLASH_ERASE), + "invalid state"); + + if (devp->state == FLASH_ERASE) { + return FLASH_BUSY_ERASING; + } + + /* Bus acquired.*/ + bus_acquire(devp->config->busp, devp->config->buscfg); + + /* Actual read SFDP implementation.*/ + err = snor_device_read_sfdp(devp, offset, n, rp); + + /* The device is ready to accept commands.*/ + if (err == FLASH_NO_ERROR) { + devp->state = FLASH_READY; + } + + /* Bus released.*/ + bus_release(devp->config->busp); + + return err; +} + +/*===========================================================================*/ +/* Driver exported functions. */ +/*===========================================================================*/ + +#if ((SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI) && \ + (SNOR_SHARED_BUS == TRUE)) || defined(__DOXYGEN__) +/** + * @brief Bus acquisition and lock. + * + * @param[in] busp pointer to the bus driver + * @param[in] config bus configuration + * + * @notapi + */ +void bus_acquire(BUSDriver *busp, const BUSConfig *config) { + + (void)config; + + wspiAcquireBus(busp); + if (busp->config != config) { + wspiStart(busp, config); + } +} + +/** + * @brief Bus release. + * + * @param[in] busp pointer to the bus driver + * + * @notapi + */ +void bus_release(BUSDriver *busp) { + + wspiReleaseBus(busp); +} +#endif + +#if (SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_SPI) && \ + (SNOR_SHARED_BUS == TRUE) +void bus_acquire(BUSDriver *busp, const BUSConfig *config) { + + spiAcquireBus(busp); + if (busp->config != config) { + spiStart(busp, config); + } +} + +void bus_release(BUSDriver *busp) { + + spiReleaseBus(busp); +} +#endif + +/** + * @brief Stops the underlying bus driver. + * + * @param[in] busp pointer to the bus driver + * + * @notapi + */ +void bus_stop(BUSDriver *busp) { + +#if SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI + wspiStop(busp); +#else + spiStop(busp); +#endif +} + +/** + * @brief Sends a naked command. + * + * @param[in] busp pointer to the bus driver + * @param[in] cmd instruction code + * + * @notapi + */ +void bus_cmd(BUSDriver *busp, uint32_t cmd) { +#if SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI + wspi_command_t mode; + + mode.cmd = cmd; + mode.cfg = SNOR_WSPI_CFG_CMD; + mode.addr = 0U; + mode.alt = 0U; + mode.dummy = 0U; + wspiCommand(busp, &mode); +#else + uint8_t buf[1]; + + spiSelect(busp); + buf[0] = cmd; + spiSend(busp, 1, buf); + spiUnselect(busp); +#endif +} + +/** + * @brief Sends a command followed by a data transmit phase. + * + * @param[in] busp pointer to the bus driver + * @param[in] cmd instruction code + * @param[in] n number of bytes to receive + * @param[in] p data buffer + * + * @notapi + */ +void bus_cmd_send(BUSDriver *busp, uint32_t cmd, size_t n, const uint8_t *p) { +#if SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI + wspi_command_t mode; + + mode.cmd = cmd; + mode.cfg = SNOR_WSPI_CFG_CMD_DATA; + mode.addr = 0U; + mode.alt = 0U; + mode.dummy = 0U; + wspiSend(busp, &mode, n, p); +#else + uint8_t buf[1]; + + spiSelect(busp); + buf[0] = cmd; + spiSend(busp, 1, buf); + spiSend(busp, n, p); + spiUnselect(busp); +#endif +} + +/** + * @brief Sends a command followed by a data receive phase. + * + * @param[in] busp pointer to the bus driver + * @param[in] cmd instruction code + * @param[in] n number of bytes to receive + * @param[out] p data buffer + * + * @notapi + */ +void bus_cmd_receive(BUSDriver *busp, + uint32_t cmd, + size_t n, + uint8_t *p) { +#if SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI + wspi_command_t mode; + + mode.cmd = cmd; + mode.cfg = SNOR_WSPI_CFG_CMD_DATA; + mode.addr = 0U; + mode.alt = 0U; + mode.dummy = 0U; + wspiReceive(busp, &mode, n, p); +#else + uint8_t buf[1]; + + spiSelect(busp); + buf[0] = cmd; + spiSend(busp, 1, buf); + spiReceive(busp, n, p); + spiUnselect(busp); +#endif +} + +/** + * @brief Sends a command followed by a flash address. + * + * @param[in] busp pointer to the bus driver + * @param[in] cmd instruction code + * @param[in] offset flash offset + * + * @notapi + */ +void bus_cmd_addr(BUSDriver *busp, uint32_t cmd, flash_offset_t offset) { +#if SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI + wspi_command_t mode; + + mode.cmd = cmd; + mode.cfg = SNOR_WSPI_CFG_CMD_ADDR; + mode.addr = offset; + mode.alt = 0U; + mode.dummy = 0U; + wspiCommand(busp, &mode); +#else + uint8_t buf[4]; + + spiSelect(busp); + buf[0] = cmd; + buf[1] = (uint8_t)(offset >> 16); + buf[2] = (uint8_t)(offset >> 8); + buf[3] = (uint8_t)(offset >> 0); + spiSend(busp, 4, buf); + spiUnselect(busp); +#endif +} + +/** + * @brief Sends a command followed by a flash address and a data transmit + * phase. + * + * @param[in] busp pointer to the bus driver + * @param[in] cmd instruction code + * @param[in] offset flash offset + * @param[in] n number of bytes to receive + * @param[in] p data buffer + * + * @notapi + */ +void bus_cmd_addr_send(BUSDriver *busp, + uint32_t cmd, + flash_offset_t offset, + size_t n, + const uint8_t *p) { +#if SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI + wspi_command_t mode; + + mode.cmd = cmd; + mode.cfg = SNOR_WSPI_CFG_CMD_ADDR_DATA; + mode.addr = offset; + mode.alt = 0U; + mode.dummy = 0U; + wspiSend(busp, &mode, n, p); +#else + uint8_t buf[4]; + + spiSelect(busp); + buf[0] = cmd; + buf[1] = (uint8_t)(offset >> 16); + buf[2] = (uint8_t)(offset >> 8); + buf[3] = (uint8_t)(offset >> 0); + spiSend(busp, 4, buf); + spiSend(busp, n, p); + spiUnselect(busp); +#endif +} + +/** + * @brief Sends a command followed by a flash address and a data receive + * phase. + * + * @param[in] busp pointer to the bus driver + * @param[in] cmd instruction code + * @param[in] offset flash offset + * @param[in] n number of bytes to receive + * @param[out] p data buffer + * + * @notapi + */ +void bus_cmd_addr_receive(BUSDriver *busp, + uint32_t cmd, + flash_offset_t offset, + size_t n, + uint8_t *p) { +#if SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI + wspi_command_t mode; + + mode.cmd = cmd; + mode.cfg = SNOR_WSPI_CFG_CMD_ADDR_DATA; + mode.addr = offset; + mode.alt = 0U; + mode.dummy = 0U; + wspiReceive(busp, &mode, n, p); +#else + uint8_t buf[4]; + + spiSelect(busp); + buf[0] = cmd; + buf[1] = (uint8_t)(offset >> 16); + buf[2] = (uint8_t)(offset >> 8); + buf[3] = (uint8_t)(offset >> 0); + spiSend(busp, 4, buf); + spiReceive(busp, n, p); + spiUnselect(busp); +#endif +} + +#if (SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI) || defined(__DOXYGEN__) +/** + * @brief Sends a command followed by dummy cycles and a + * data receive phase. + * + * @param[in] busp pointer to the bus driver + * @param[in] cmd instruction code + * @param[in] dummy number of dummy cycles + * @param[in] n number of bytes to receive + * @param[out] p data buffer + * + * @notapi + */ +void bus_cmd_dummy_receive(BUSDriver *busp, + uint32_t cmd, + uint32_t dummy, + size_t n, + uint8_t *p) { + wspi_command_t mode; + + mode.cmd = cmd; + mode.cfg = SNOR_WSPI_CFG_CMD_DATA; + mode.addr = 0U; + mode.alt = 0U; + mode.dummy = dummy; + wspiReceive(busp, &mode, n, p); +} + +/** + * @brief Sends a command followed by a flash address, dummy cycles and a + * data receive phase. + * + * @param[in] busp pointer to the bus driver + * @param[in] cmd instruction code + * @param[in] offset flash offset + * @param[in] dummy number of dummy cycles + * @param[in] n number of bytes to receive + * @param[out] p data buffer + * + * @notapi + */ +void bus_cmd_addr_dummy_receive(BUSDriver *busp, + uint32_t cmd, + flash_offset_t offset, + uint32_t dummy, + size_t n, + uint8_t *p) { + wspi_command_t mode; + + mode.cmd = cmd; + mode.cfg = SNOR_WSPI_CFG_CMD_ADDR_DATA; + mode.addr = offset; + mode.alt = 0U; + mode.dummy = dummy; + wspiReceive(busp, &mode, n, p); +} +#endif /* SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI */ + +/** + * @brief Initializes an instance. + * + * @param[out] devp pointer to the @p SNORDriver object + * + * @init + */ +void snorObjectInit(SNORDriver *devp) { + + osalDbgCheck(devp != NULL); + + devp->vmt = &snor_vmt; + devp->state = FLASH_STOP; + devp->config = NULL; +} + +/** + * @brief Configures and activates SNOR driver. + * + * @param[in] devp pointer to the @p SNORDriver object + * @param[in] config pointer to the configuration + * + * @api + */ +void snorStart(SNORDriver *devp, const SNORConfig *config) { + + osalDbgCheck((devp != NULL) && (config != NULL)); + osalDbgAssert(devp->state != FLASH_UNINIT, "invalid state"); + + devp->config = config; + + if (devp->state == FLASH_STOP) { + + /* Bus acquisition.*/ + bus_acquire(devp->config->busp, devp->config->buscfg); + + /* Device identification and initialization.*/ + snor_device_init(devp); + + /* Driver in ready state.*/ + devp->state = FLASH_READY; + + /* Bus release.*/ + bus_release(devp->config->busp); + } +} + +/** + * @brief Deactivates the SNOR driver. + * + * @param[in] devp pointer to the @p SNORDriver object + * + * @api + */ +void snorStop(SNORDriver *devp) { + + osalDbgCheck(devp != NULL); + osalDbgAssert(devp->state != FLASH_UNINIT, "invalid state"); + + if (devp->state != FLASH_STOP) { + + /* Bus acquisition.*/ + bus_acquire(devp->config->busp, devp->config->buscfg); + + /* Stopping bus device.*/ + bus_stop(devp->config->busp); + + /* Driver stopped.*/ + devp->state = FLASH_STOP; + + /* Bus release.*/ + bus_release(devp->config->busp); + + /* Deleting current configuration.*/ + devp->config = NULL; + } +} + +#if (SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI) || defined(__DOXYGEN__) +#if (WSPI_SUPPORTS_MEMMAP == TRUE) || defined(__DOXYGEN__) +/** + * @brief Enters the memory Mapping mode. + * @details The memory mapping mode is only available when the WSPI mode + * is selected and the underlying WSPI controller supports the + * feature. + * + * @param[in] devp pointer to the @p SNORDriver object + * @param[out] addrp pointer to the memory start address of the mapped + * flash or @p NULL + * + * @api + */ +void snorMemoryMap(SNORDriver *devp, uint8_t **addrp) { + + /* Bus acquisition.*/ + bus_acquire(devp->config->busp, devp->config->buscfg); + +#if SNOR_DEVICE_SUPPORTS_XIP == TRUE + /* Activating XIP mode in the device.*/ + snor_activate_xip(devp); +#endif + + /* Starting WSPI memory mapped mode.*/ + wspiMapFlash(devp->config->busp, &snor_memmap_read, addrp); + + /* Bus release.*/ + bus_release(devp->config->busp); +} + +/** + * @brief Leaves the memory Mapping mode. + * + * @param[in] devp pointer to the @p SNORDriver object + * + * @api + */ +void snorMemoryUnmap(SNORDriver *devp) { + + /* Bus acquisition.*/ + bus_acquire(devp->config->busp, devp->config->buscfg); + + /* Stopping WSPI memory mapped mode.*/ + wspiUnmapFlash(devp->config->busp); + +#if SNOR_DEVICE_SUPPORTS_XIP == TRUE + snor_reset_xip(devp); +#endif + + /* Bus release.*/ + bus_release(devp->config->busp); +} +#endif /* WSPI_SUPPORTS_MEMMAP == TRUE */ +#endif /* SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI */ + +/** @} */ diff --git a/ChibiOS_20.3.2/os/hal/lib/complex/serial_nor/hal_serial_nor.h b/ChibiOS_20.3.2/os/hal/lib/complex/serial_nor/hal_serial_nor.h new file mode 100644 index 0000000..cca0297 --- /dev/null +++ b/ChibiOS_20.3.2/os/hal/lib/complex/serial_nor/hal_serial_nor.h @@ -0,0 +1,210 @@ +/* + ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio + + 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. +*/ + +/** + * @file hal_serial_nor.h + * @brief Serial NOR driver header. + * + * @addtogroup HAL_SERIAL_NOR + * @{ + */ + +#ifndef HAL_SERIAL_NOR_H +#define HAL_SERIAL_NOR_H + +#include "hal_flash.h" + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/** + * @name Bus interface modes. + * @{ + */ +#define SNOR_BUS_DRIVER_SPI 0U +#define SNOR_BUS_DRIVER_WSPI 1U +/** @} */ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @brief Physical transport interface. + */ +#if !defined(SNOR_BUS_DRIVER) || defined(__DOXYGEN__) +#define SNOR_BUS_DRIVER SNOR_BUS_DRIVER_WSPI +#endif + +/** + * @brief Shared bus switch. + * @details If set to @p TRUE the device acquires bus ownership + * on each transaction. + * @note Requires @p SPI_USE_MUTUAL_EXCLUSION or + * @p WSPI_USE_MUTUAL_EXCLUSION depending on mode selected + * with @p SNOR_BUS_MODE. + */ +#if !defined(SNOR_SHARED_BUS) || defined(__DOXYGEN__) +#define SNOR_SHARED_BUS TRUE +#endif +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if (SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_SPI) || defined(__DOXYGEN__) +#define BUSConfig SPIConfig +#define BUSDriver SPIDriver +#elif SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI +#define BUSConfig WSPIConfig +#define BUSDriver WSPIDriver +#else +#error "invalid SNOR_BUS_DRIVER setting" +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief Type of a SNOR configuration structure. + */ +typedef struct { + BUSDriver *busp; + const BUSConfig *buscfg; +} SNORConfig; + +/** + * @brief @p SNORDriver specific methods. + */ +#define _snor_flash_methods_alone \ + /* Read SFDP.*/ \ + flash_error_t (*read_sfdp)(void *instance, \ + flash_offset_t offset, \ + size_t n, \ + uint8_t *rp); + +/** + * @brief @p SNORDriver specific methods with inherited ones. + */ +#define _snor_flash_methods \ + _base_flash_methods \ + _snor_flash_methods_alone + +/** + * @extends BaseFlashVMT + * + * @brief @p SNOR virtual methods table. + */ +struct SNORDriverVMT { + _snor_flash_methods +}; + +/** + * @extends BaseFlash + * + * @brief Type of SNOR flash class. + */ +typedef struct { + /** + * @brief SNORDriver Virtual Methods Table. + */ + const struct SNORDriverVMT *vmt; + _base_flash_data + /** + * @brief Current configuration data. + */ + const SNORConfig *config; + /** + * @brief Device ID and unique ID. + */ + uint8_t device_id[20]; +} SNORDriver; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +#if SNOR_SHARED_BUS == FALSE +#define bus_acquire(busp, config) +#define bus_release(busp) +#endif + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + void bus_acquire(BUSDriver *busp, const BUSConfig *config); + void bus_release(BUSDriver *busp); + void bus_cmd(BUSDriver *busp, uint32_t cmd); + void bus_cmd_send(BUSDriver *busp, uint32_t cmd, size_t n, const uint8_t *p); + void bus_cmd_receive(BUSDriver *busp, + uint32_t cmd, + size_t n, + uint8_t *p); + void bus_cmd_addr(BUSDriver *busp, uint32_t cmd, flash_offset_t offset); + void bus_cmd_addr_send(BUSDriver *busp, + uint32_t cmd, + flash_offset_t offset, + size_t n, + const uint8_t *p); + void bus_cmd_addr_receive(BUSDriver *busp, + uint32_t cmd, + flash_offset_t offset, + size_t n, + uint8_t *p); +#if (SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI) || defined(__DOXYGEN__) + void bus_cmd_dummy_receive(BUSDriver *busp, + uint32_t cmd, + uint32_t dummy, + size_t n, + uint8_t *p); + void bus_cmd_addr_dummy_receive(BUSDriver *busp, + uint32_t cmd, + flash_offset_t offset, + uint32_t dummy, + size_t n, + uint8_t *p); +#endif + void snorObjectInit(SNORDriver *devp); + void snorStart(SNORDriver *devp, const SNORConfig *config); + void snorStop(SNORDriver *devp); +#if (SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI) || defined(__DOXYGEN__) +#if (WSPI_SUPPORTS_MEMMAP == TRUE) || defined(__DOXYGEN__) + void snorMemoryMap(SNORDriver *devp, uint8_t ** addrp); + void snorMemoryUnmap(SNORDriver *devp); +#endif /* QSPI_SUPPORTS_MEMMAP == TRUE */ +#endif /* SNOR_BUS_MODE != SNOR_BUS_MODE_SPI */ +#ifdef __cplusplus +} +#endif + +/* Device-specific implementations.*/ +#include "hal_flash_device.h" + +#endif /* HAL_SERIAL_NOR_H */ + +/** @} */ + -- cgit v1.2.3