/* * Copyright (c) 2018-2020 Arm Limited * * 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. */ #include /* Use memcpy function */ #include #include "mt25ql_flash_lib.h" #include "qspi_ip6514e_drv.h" /** Setter bit manipulation macro */ #define SET_BIT(WORD, BIT_INDEX) ((WORD) |= (1U << (BIT_INDEX))) /** Clearing bit manipulation macro */ #define CLR_BIT(WORD, BIT_INDEX) ((WORD) &= ~(1U << (BIT_INDEX))) /** Getter bit manipulation macro */ #define GET_BIT(WORD, BIT_INDEX) (bool)(((WORD) & (1U << (BIT_INDEX)))) #define BITS_PER_WORD 32U #define BYTES_PER_WORD 4U #define ARG_NOT_USED 0 #define ARG_PTR_NOT_USED NULL /** MT25QL used command */ #define WRITE_ENABLE_CMD 0x06U #define READ_ENHANCED_VOLATILE_CFG_REG_CMD 0x65U #define WRITE_ENHANCED_VOLATILE_CFG_REG_CMD 0x61U #define READ_VOLATILE_CFG_REG_CMD 0x85U #define WRITE_VOLATILE_CFG_REG_CMD 0x81U #define READ_FLAG_STATUS_REG_CMD 0x70U #define SUBSECTOR_ERASE_32KB_CMD 0x52U #define SUBSECTOR_ERASE_4KB_CMD 0x20U #define SECTOR_ERASE_CMD 0xD8U #define BULK_ERASE_CMD 0xC7U /* * The baud rate divisor in \ref mt25ql_dev_t needs to be configured adequately * to handle those commands. */ #define QUAD_OUTPUT_FAST_READ_CMD 0x6BU #define FAST_READ_CMD 0x0BU #define READ_CMD 0x03U #define QUAD_INPUT_FAST_PROGRAM_CMD 0x32U #define PAGE_PROGRAM_CMD 0x02U /** MT25QL Enhanced Volatile Configuration Register access */ #define ENHANCED_VOLATILE_CFG_REG_LEN 1U #define ENHANCED_VOLATILE_CFG_REG_QSPI_POS 7U #define ENHANCED_VOLATILE_CFG_REG_DSPI_POS 6U /** MT25QL Volatile Configuration Register access */ #define VOLATILE_CFG_REG_LEN 1U #define VOLATILE_CFG_REG_DUMMY_CYCLES_POS 4U #define VOLATILE_CFG_REG_DUMMY_CYCLES_BITS 4U /** MT25QL Flag Status Register access */ #define FLAG_STATUS_REG_LEN 1U #define FLAG_STATUS_REG_READY_POS 7U /* * 10 is the minimal number of dummy clock cycles needed to reach the maximal * frequency of the Quad Output Fast Read Command. */ #define QUAD_OUTPUT_FAST_READ_DUMMY_CYCLES 10U #define FAST_READ_DUMMY_CYCLES 8U #define RESET_STATE_DUMMY_CYCLES 8U #define DEFAULT_READ_DUMMY_CYCLES 0U #define QUAD_INPUT_FAST_PROGRAM_DUMMY_CYCLES 0U #define PAGE_PROGRAM_DUMMY_CYCLES 0U /* Only up to 8 bytes can be read or written using the Flash commands. */ #define CMD_DATA_MAX_SIZE 8U /** * \brief Change specific bits in a 32 bits word. * * \param[in,out] word Pointer of the word to change * \param[in] bits bits_length bits to put at bits_pos in the word * pointed * \param[in] bits_length Number of bits to change * \param[in] bits_pos Position of the bits to change * * \note This function will do nothing if the parameters given are incorrect: * * word is NULL * * bits_length + bits_pos > 32 * * bits_length is 0 */ static void change_bits_in_word(volatile uint32_t *word, uint32_t bits, uint32_t bits_length, uint32_t bits_pos) { uint32_t mask; if ((word == NULL) || ((bits_length + bits_pos) > BITS_PER_WORD) || (bits_length == 0U)) { /* Silently fail */ return; } /* Change all the bits */ if (bits_length == BITS_PER_WORD) { *word = bits; return; } mask = ((1U << bits_length) - 1); /* * We change the bits in three steps: * - clear bits_length bits with zeroes at bits_pos in the word * - mask bits in case it contains more than bits_length bits * - set the new bits in the cleared word * Because the data pointed by word is only read once, the data will still * be coherent after an interruption that changes it. */ *word = ((*word & ~(mask << bits_pos)) | ((bits & mask) << bits_pos)); } /** * \brief Send the Write Enable command, needed before any write. * * \param[in] dev Pointer to MT25QL device structure \ref mt25ql_dev_t */ static void send_write_enable(struct mt25ql_dev_t* dev) { qspi_ip6514e_send_simple_cmd(dev->controller, WRITE_ENABLE_CMD); } /** * \brief Set SPI mode on the flash device and on the controller. * * \param[in] dev Pointer to MT25QL device structure \ref mt25ql_dev_t * \param[in] spi_mode SPI mode to be set on flash device and controller * \ref qspi_ip6514e_spi_mode_t * * \return Return error code as specified in \ref mt25ql_error_t */ static enum mt25ql_error_t set_spi_mode(struct mt25ql_dev_t* dev, enum qspi_ip6514e_spi_mode_t spi_mode) { uint8_t enhanced_volatile_cfg_reg = 0; enum qspi_ip6514e_error_t controller_error; /* Read the Enhanced Volatile Configuration Register, modify it according * to the requested SPI mode then write back the modified value to the * register. This will activate the SPI mode on the flash side. */ controller_error = qspi_ip6514e_send_read_cmd( dev->controller, READ_ENHANCED_VOLATILE_CFG_REG_CMD, &enhanced_volatile_cfg_reg, ENHANCED_VOLATILE_CFG_REG_LEN, ARG_NOT_USED, ARG_NOT_USED, 0); /* No dummy cycles needed for this command. */ if (controller_error != QSPI_IP6514E_ERR_NONE) { return (enum mt25ql_error_t)controller_error; } switch(spi_mode) { case QSPI_IP6514E_SPI_MODE: /* Disable the Dual- and Quad-SPI modes. * Clearing the bit enables the mode, setting it disables it. */ SET_BIT(enhanced_volatile_cfg_reg, ENHANCED_VOLATILE_CFG_REG_DSPI_POS); SET_BIT(enhanced_volatile_cfg_reg, ENHANCED_VOLATILE_CFG_REG_QSPI_POS); break; case QSPI_IP6514E_DSPI_MODE: /* Disable the Quad-SPI mode and activate DSPI mode. * Clearing the bit enables the mode, setting it disables it. */ CLR_BIT(enhanced_volatile_cfg_reg, ENHANCED_VOLATILE_CFG_REG_DSPI_POS); SET_BIT(enhanced_volatile_cfg_reg, ENHANCED_VOLATILE_CFG_REG_QSPI_POS); break; case QSPI_IP6514E_QSPI_MODE: /* Disable the Dual-SPI mode and activate QSPI mode. * Clearing the bit enables the mode, setting it disables it. */ SET_BIT(enhanced_volatile_cfg_reg, ENHANCED_VOLATILE_CFG_REG_DSPI_POS); CLR_BIT(enhanced_volatile_cfg_reg, ENHANCED_VOLATILE_CFG_REG_QSPI_POS); break; default: return MT25QL_ERR_WRONG_ARGUMENT; } send_write_enable(dev); controller_error = qspi_ip6514e_send_write_cmd( dev->controller, WRITE_ENHANCED_VOLATILE_CFG_REG_CMD, &enhanced_volatile_cfg_reg, ENHANCED_VOLATILE_CFG_REG_LEN, ARG_NOT_USED, ARG_NOT_USED, 0); /* No dummy cycles needed for this command. */ if (controller_error != QSPI_IP6514E_ERR_NONE) { return (enum mt25ql_error_t)controller_error; } /* Activate the requested SPI mode on the controller side as well. */ controller_error = qspi_ip6514e_set_spi_mode(dev->controller, spi_mode, spi_mode, spi_mode); if (controller_error != QSPI_IP6514E_ERR_NONE) { return (enum mt25ql_error_t)controller_error; } return MT25QL_ERR_NONE; } /** * \brief Change the number of dummy clock cycles subsequent to all FAST READ * commands. * * \param[in] dev Pointer to MT25QL device structure \ref mt25ql_dev_t * \param[in] dummy_cycles Dummy clock cycles to set * * \return Return error code as specified in \ref mt25ql_error_t */ static enum mt25ql_error_t change_dummy_cycles(struct mt25ql_dev_t* dev, uint32_t dummy_cycles) { uint32_t volatile_cfg_reg = 0; enum qspi_ip6514e_error_t controller_error; /* * Changes the number of dummy cycles in the Volatile Configuration * Register. */ controller_error = qspi_ip6514e_send_read_cmd(dev->controller, READ_VOLATILE_CFG_REG_CMD, &volatile_cfg_reg, VOLATILE_CFG_REG_LEN, ARG_NOT_USED, ARG_NOT_USED, 0); /* No dummy cycles needed for this command. */ if (controller_error != QSPI_IP6514E_ERR_NONE) { return (enum mt25ql_error_t)controller_error; } change_bits_in_word(&volatile_cfg_reg, dummy_cycles, VOLATILE_CFG_REG_DUMMY_CYCLES_BITS, VOLATILE_CFG_REG_DUMMY_CYCLES_POS); send_write_enable(dev); controller_error = qspi_ip6514e_send_write_cmd(dev->controller, WRITE_VOLATILE_CFG_REG_CMD, &volatile_cfg_reg, VOLATILE_CFG_REG_LEN, ARG_NOT_USED, ARG_NOT_USED, 0); /* No dummy cycles needed for this command. */ if (controller_error != QSPI_IP6514E_ERR_NONE) { return (enum mt25ql_error_t)controller_error; } return MT25QL_ERR_NONE; } /** * \brief Wait until the current program/erase is finished. * * \param[in] dev Pointer to MT25QL device structure \ref mt25ql_dev_t * * \return Return error code as specified in \ref mt25ql_error_t */ static enum mt25ql_error_t wait_program_or_erase_complete( struct mt25ql_dev_t* dev) { enum qspi_ip6514e_error_t controller_error; uint8_t flag_status_reg = 0; /* Wait until the ready bit of the Flag Status Register is set */ while (!GET_BIT(flag_status_reg, FLAG_STATUS_REG_READY_POS)) { controller_error = qspi_ip6514e_send_read_cmd(dev->controller, READ_FLAG_STATUS_REG_CMD, &flag_status_reg, FLAG_STATUS_REG_LEN, ARG_NOT_USED, ARG_NOT_USED, 0); /* No dummy cycles needed for this command. */ if (controller_error != QSPI_IP6514E_ERR_NONE) { return (enum mt25ql_error_t)controller_error; } } return MT25QL_ERR_NONE; } /** * \brief Execute a program command that crosses the page size boundary. * * \param[in] dev Pointer to MT25QL device structure * \ref mt25ql_dev_t * \param[in] opcode Opcode for the command. * \param[in] write_data Pointer to a memory zone where the write_len * number of bytes are located to write for this * command. * \param[in] write_len Number of bytes to write for the command. * Between 1 and 8 bytes (both included) can be * written. * \param[in] addr Address used for the command * \param[in] addr_bytes_number Number of address bytes for this command. * If an address is not needed for the command, * use 0 for argument, otherwise between 1 and * 4 bytes (both included) can be used. * \param[in] dummy_cycles Number of dummy cycles required for the * command, between 0 and 31 (both included). * * \return Return error code as specified in \ref mt25ql_error_t * * \note This function will execute two commands: one to program the bytes up to * the page boundary and another one to program the rest. It will wait * that bytes are programmed from first command before triggering the * second one. * \note This function does not send a write enable command before the first * command and does not check that bytes were programmed after the second * command. */ static enum mt25ql_error_t send_boundary_cross_write_cmd( struct mt25ql_dev_t* dev, uint8_t opcode, const void *write_data, uint32_t write_len, uint32_t addr, uint32_t addr_bytes_number, uint32_t dummy_cycles) { enum qspi_ip6514e_error_t controller_error; enum mt25ql_error_t library_error; /* * Remaining bytes between the current address and the end of the current * page. */ uint32_t page_remainder = FLASH_PAGE_SIZE - (addr % FLASH_PAGE_SIZE); /* First write up to the end of the current page. */ controller_error = qspi_ip6514e_send_write_cmd(dev->controller, opcode, write_data, page_remainder, addr, addr_bytes_number, dummy_cycles); if (controller_error != QSPI_IP6514E_ERR_NONE) { return (enum mt25ql_error_t)controller_error; } write_data = (void *)((uint32_t)write_data + page_remainder); addr += page_remainder; /* Wait for the page to be written before sending new commands. */ library_error = wait_program_or_erase_complete(dev); if (library_error != MT25QL_ERR_NONE) { return library_error; } /* Then write the remaining data of the write_len bytes. */ send_write_enable(dev); controller_error = qspi_ip6514e_send_write_cmd(dev->controller, opcode, write_data, write_len - page_remainder, addr, addr_bytes_number, dummy_cycles); if (controller_error != QSPI_IP6514E_ERR_NONE) { return (enum mt25ql_error_t)controller_error; } return MT25QL_ERR_NONE; } enum mt25ql_error_t mt25ql_config_mode(struct mt25ql_dev_t* dev, enum mt25ql_functional_state_t f_state) { enum qspi_ip6514e_error_t controller_error; enum mt25ql_error_t library_error; switch(f_state) { case MT25QL_FUNC_STATE_DEFAULT: dev->config_state.spi_mode = QSPI_IP6514E_SPI_MODE; dev->config_state.opcode_read = READ_CMD; dev->config_state.dummy_cycles_read = DEFAULT_READ_DUMMY_CYCLES; dev->config_state.opcode_write = PAGE_PROGRAM_CMD; dev->config_state.dummy_cycles_write = PAGE_PROGRAM_DUMMY_CYCLES; break; case MT25QL_FUNC_STATE_FAST: dev->config_state.spi_mode = QSPI_IP6514E_SPI_MODE; dev->config_state.opcode_read = FAST_READ_CMD; dev->config_state.dummy_cycles_read = FAST_READ_DUMMY_CYCLES; dev->config_state.opcode_write = PAGE_PROGRAM_CMD; dev->config_state.dummy_cycles_write = PAGE_PROGRAM_DUMMY_CYCLES; break; case MT25QL_FUNC_STATE_QUAD_FAST: dev->config_state.spi_mode = QSPI_IP6514E_QSPI_MODE; dev->config_state.opcode_read = QUAD_OUTPUT_FAST_READ_CMD; dev->config_state.dummy_cycles_read = QUAD_OUTPUT_FAST_READ_DUMMY_CYCLES; dev->config_state.opcode_write = QUAD_INPUT_FAST_PROGRAM_CMD; dev->config_state.dummy_cycles_write = QUAD_INPUT_FAST_PROGRAM_DUMMY_CYCLES; break; default: return MT25QL_ERR_WRONG_ARGUMENT; } dev->config_state.func_state = f_state; /* This function will first set the Flash memory SPI mode and then set * the controller's SPI mode. It will fail if the two sides do not have * the same mode when this function is called. */ library_error = set_spi_mode(dev, dev->config_state.spi_mode); if (library_error != MT25QL_ERR_NONE) { return library_error; } /* Set the number of dummy cycles for read commands. */ library_error = change_dummy_cycles( dev, dev->config_state.dummy_cycles_read); if (library_error != MT25QL_ERR_NONE) { return library_error; } /* The rest of the configuration needs the controller to be disabled */ while(!qspi_ip6514e_is_idle(dev->controller)); qspi_ip6514e_disable(dev->controller); /* Set the baud rate divisor as configured in the device structure. */ controller_error = qspi_ip6514e_set_baud_rate_div(dev->controller, dev->baud_rate_div); if (controller_error != QSPI_IP6514E_ERR_NONE) { return (enum mt25ql_error_t)controller_error; } /* Set opcode and dummy cycles needed for read commands. */ controller_error = qspi_ip6514e_cfg_reads( dev->controller, dev->config_state.opcode_read, dev->config_state.dummy_cycles_read); if (controller_error != QSPI_IP6514E_ERR_NONE) { return (enum mt25ql_error_t)controller_error; } /* Set opcode and dummy cycles needed for write commands. */ controller_error = qspi_ip6514e_cfg_writes( dev->controller, dev->config_state.opcode_write, dev->config_state.dummy_cycles_write); if (controller_error != QSPI_IP6514E_ERR_NONE) { return (enum mt25ql_error_t)controller_error; } /* Set Flash memory constants: bytes per page and address bytes. */ controller_error = qspi_ip6514e_cfg_page_size(dev->controller, FLASH_PAGE_SIZE); if (controller_error != QSPI_IP6514E_ERR_NONE) { return (enum mt25ql_error_t)controller_error; } controller_error = qspi_ip6514e_cfg_addr_bytes(dev->controller, ADDR_BYTES); if (controller_error != QSPI_IP6514E_ERR_NONE) { return (enum mt25ql_error_t)controller_error; } qspi_ip6514e_enable(dev->controller); return MT25QL_ERR_NONE; } enum mt25ql_error_t mt25ql_restore_reset_state(struct mt25ql_dev_t* dev) { enum mt25ql_error_t library_error; /* * This function will first change the Flash memory mode to single SPI and * then change the controller to single SPI. It will fail if the two sides * do not have the same mode when this function is called. */ library_error = set_spi_mode(dev, QSPI_IP6514E_SPI_MODE); if (library_error != MT25QL_ERR_NONE) { return library_error; } /* Set the default number of dummy cycles for direct read commands. */ library_error = change_dummy_cycles(dev, RESET_STATE_DUMMY_CYCLES); if (library_error != MT25QL_ERR_NONE) { return library_error; } /* The rest of the configuration needs the controller to be disabled */ while(!qspi_ip6514e_is_idle(dev->controller)); qspi_ip6514e_disable(dev->controller); /* Restore the default value of the QSPI controller registers. */ qspi_ip6514e_reset_regs(dev->controller); qspi_ip6514e_enable(dev->controller); dev->config_state = (struct mt25ql_config_state_t){ MT25QL_FUNC_STATE_NOT_INITED }; dev->config_state.func_state = MT25QL_FUNC_STATE_NOT_INITED; return MT25QL_ERR_NONE; } enum mt25ql_error_t mt25ql_direct_read(struct mt25ql_dev_t* dev, uint32_t addr, void *data, uint32_t len) { /* * The direct access window size is the size of the memory that can be * accessed with a direct access. */ uint32_t direct_access_window_size = dev->controller->cfg->addr_mask + 1; /* * The window number is the number of times it will be needed to remap the * address with the remap register. We move this Direct Access window first * window_number times starting at the beginning address to read full * windows of direct_access_window_size bytes. Then we read the remainder * bytes. */ uint32_t window_number = len / direct_access_window_size; if (data == NULL || len == 0) { return MT25QL_ERR_WRONG_ARGUMENT; } if ((addr + len) >= dev->size) { return MT25QL_ERR_ADDR_TOO_BIG; } /* * There is no limitation reading through a Flash page boundary hence we * do not add the same logic here than in the write function. */ /* Transfer the bytes for the window_number windows first. */ for (uint32_t window = 0; window < window_number; window++) { qspi_ip6514e_remap_addr(dev->controller, addr); /* * The AHB address to access the Flash memory does not change but it * will be translated differently thanks to the remap function. */ memcpy(data, (void *)dev->direct_access_start_addr, direct_access_window_size); len -= direct_access_window_size; data = (void *)((uint32_t)data + direct_access_window_size); addr += direct_access_window_size; } if (len) { /* Transfer the reminder bytes */ qspi_ip6514e_remap_addr(dev->controller, addr); memcpy(data, (void *)dev->direct_access_start_addr, len); } /* Disable remapping for direct accesses outside of this function. */ qspi_ip6514e_disable_remap(dev->controller); return MT25QL_ERR_NONE; } enum mt25ql_error_t mt25ql_direct_write(struct mt25ql_dev_t* dev, uint32_t addr, const void *data, uint32_t len) { enum mt25ql_error_t library_error; /* * The direct access window size is the size of the memory that can be * accessed with a direct access. */ uint32_t direct_access_window_size = dev->controller->cfg->addr_mask + 1; uint32_t window_number; /* Offset between address and the previous 32 bits aligned word */ uint32_t word_offset; if (data == NULL || len == 0) { return MT25QL_ERR_WRONG_ARGUMENT; } if ((addr + len) >= dev->size) { return MT25QL_ERR_ADDR_TOO_BIG; } /* * If the remapping address is not aligned on a 32 bits boundary, a direct * access of one word could cross a Flash page boundary. If that happens, * the bytes of that word that are over the page boundary will instead be * written at the beginning of the same page. * To counter this problem, we align the remapping address and add the word * offset to the address of the direct access for the first window only. */ word_offset = addr % BYTES_PER_WORD; /* Make address aligned on a 32 bits alignment. */ addr -= word_offset; /* * Only direct_access_window_size address locations are available by direct * access. We calculate the number of windows that we will need to transfer * len bytes. We have to add in the window the offset that we add in the * beginning. */ window_number = (len + word_offset) / direct_access_window_size; /* * This function assumes that the flash has already been erased. * Transfer the bytes for the window_number windows first. */ for (uint32_t window = 0; window < window_number; window++) { /* The controller needs to be disabled while remapping is done. */ qspi_ip6514e_remap_addr(dev->controller, addr); /* * The AHB address to access the Flash memory does not change but it * will be translated differently thanks to the remap function. */ memcpy((void *)(dev->direct_access_start_addr + word_offset), data, direct_access_window_size - word_offset); len -= (direct_access_window_size - word_offset); data = (void *)((uint32_t)data + (direct_access_window_size - word_offset)); addr += direct_access_window_size; /* * The address is now aligned, there is no need to add an offset for the * remaining windows. */ word_offset = 0; /* * Wait until the last program operation is complete before changing * the remap address. */ library_error = wait_program_or_erase_complete(dev); if (library_error != MT25QL_ERR_NONE) { return library_error; } } if (len) { /* Transfer the reminder bytes */ qspi_ip6514e_remap_addr(dev->controller, addr); memcpy((void *)(dev->direct_access_start_addr + word_offset), data, len); /* Wait until the last program operation is complete */ library_error = wait_program_or_erase_complete(dev); if (library_error != MT25QL_ERR_NONE) { return library_error; } } /* * Disable the default remap address for direct accesses outside of this * function. */ qspi_ip6514e_disable_remap(dev->controller); return MT25QL_ERR_NONE; } enum mt25ql_error_t mt25ql_command_read(struct mt25ql_dev_t* dev, uint32_t addr, void *data, uint32_t len) { /* With one single command only 8 bytes can be read. */ uint32_t cmd_number = len / CMD_DATA_MAX_SIZE; enum qspi_ip6514e_error_t controller_error; if (dev->config_state.func_state == MT25QL_FUNC_STATE_NOT_INITED) { return MT25QL_ERR_NOT_INITED; } for (uint32_t cmd_index = 0; cmd_index < cmd_number; cmd_index++) { controller_error = qspi_ip6514e_send_read_cmd( dev->controller, dev->config_state.opcode_read, data, CMD_DATA_MAX_SIZE, addr, ADDR_BYTES, dev->config_state.dummy_cycles_read); if (controller_error != QSPI_IP6514E_ERR_NONE) { return (enum mt25ql_error_t)controller_error; } data = (void *)((uint32_t)data + CMD_DATA_MAX_SIZE); addr += CMD_DATA_MAX_SIZE; len -= CMD_DATA_MAX_SIZE; } if (len) { /* Read the remainder. */ controller_error = qspi_ip6514e_send_read_cmd( dev->controller, dev->config_state.opcode_read, data, len, addr, ADDR_BYTES, dev->config_state.dummy_cycles_read); if (controller_error != QSPI_IP6514E_ERR_NONE) { return (enum mt25ql_error_t)controller_error; } } return MT25QL_ERR_NONE; } enum mt25ql_error_t mt25ql_command_write(struct mt25ql_dev_t* dev, uint32_t addr, const void *data, uint32_t len) { /* With one single command only 8 bytes can be written. */ uint32_t cmd_number = len / CMD_DATA_MAX_SIZE; enum qspi_ip6514e_error_t controller_error; enum mt25ql_error_t library_error; if (dev->config_state.func_state == MT25QL_FUNC_STATE_NOT_INITED) { return MT25QL_ERR_NOT_INITED; } for (uint32_t cmd_index = 0; cmd_index < cmd_number; cmd_index++) { send_write_enable(dev); /* * Check if this command is not writing over a page boundary: first and * last bytes are in the same page. */ if ((addr / FLASH_PAGE_SIZE) != ((addr + CMD_DATA_MAX_SIZE - 1) / FLASH_PAGE_SIZE)) { /* The CMD_DATA_MAX_SIZE bytes written are crossing the boundary. */ library_error = send_boundary_cross_write_cmd( dev, dev->config_state.opcode_write, data, CMD_DATA_MAX_SIZE, addr, ADDR_BYTES, dev->config_state.dummy_cycles_write); if (library_error != MT25QL_ERR_NONE) { return library_error; } } else { /* Normal case: not crossing the boundary. */ controller_error = qspi_ip6514e_send_write_cmd( dev->controller, dev->config_state.opcode_write, data, CMD_DATA_MAX_SIZE, addr, ADDR_BYTES, dev->config_state.dummy_cycles_write); if (controller_error != QSPI_IP6514E_ERR_NONE) { return (enum mt25ql_error_t)controller_error; } } /* Wait until the write operation is complete. */ library_error = wait_program_or_erase_complete(dev); if (library_error != MT25QL_ERR_NONE) { return library_error; } data = (void *)((uint32_t)data + CMD_DATA_MAX_SIZE); addr += CMD_DATA_MAX_SIZE; len -= CMD_DATA_MAX_SIZE; } if (len) { /* Write the remainder. */ send_write_enable(dev); /* * Check if this command is not writing over a page boundary: first and * last bytes are in the same page. */ if ((addr / FLASH_PAGE_SIZE) != ((addr + len - 1) / FLASH_PAGE_SIZE)) { /* The CMD_DATA_MAX_SIZE bytes written are crossing the boundary. */ library_error = send_boundary_cross_write_cmd( dev, dev->config_state.opcode_write, data, len, addr, ADDR_BYTES, dev->config_state.dummy_cycles_write); if (library_error != MT25QL_ERR_NONE) { return library_error; } } else { /* Normal case: not crossing the boundary. */ controller_error = qspi_ip6514e_send_write_cmd( dev->controller, dev->config_state.opcode_write, data, len, addr, ADDR_BYTES, dev->config_state.dummy_cycles_write); if (controller_error != QSPI_IP6514E_ERR_NONE) { return (enum mt25ql_error_t)controller_error; } } /* Wait until the write operation is complete. */ library_error = wait_program_or_erase_complete(dev); if (library_error != MT25QL_ERR_NONE) { return library_error; } } return MT25QL_ERR_NONE; } enum mt25ql_error_t mt25ql_erase(struct mt25ql_dev_t* dev, uint32_t addr, enum mt25ql_erase_t erase_type) { enum qspi_ip6514e_error_t controller_error; enum mt25ql_error_t library_error; uint8_t erase_cmd; uint32_t addr_bytes; if (dev->config_state.func_state == MT25QL_FUNC_STATE_NOT_INITED) { return MT25QL_ERR_NOT_INITED; } send_write_enable(dev); switch (erase_type) { case MT25QL_ERASE_ALL_FLASH: if (addr != 0) { return MT25QL_ERR_ADDR_NOT_ALIGNED; } erase_cmd = BULK_ERASE_CMD; addr_bytes = ARG_NOT_USED; break; case MT25QL_ERASE_SECTOR_64K: erase_cmd = SECTOR_ERASE_CMD; addr_bytes = ADDR_BYTES; if ((addr % SECTOR_64KB) != 0) { return MT25QL_ERR_ADDR_NOT_ALIGNED; } break; case MT25QL_ERASE_SUBSECTOR_32K: erase_cmd = SUBSECTOR_ERASE_32KB_CMD; addr_bytes = ADDR_BYTES; if ((addr % SUBSECTOR_32KB) != 0) { return MT25QL_ERR_ADDR_NOT_ALIGNED; } break; case MT25QL_ERASE_SUBSECTOR_4K: erase_cmd = SUBSECTOR_ERASE_4KB_CMD; addr_bytes = ADDR_BYTES; if ((addr % SUBSECTOR_4KB) != 0) { return MT25QL_ERR_ADDR_NOT_ALIGNED; } break; default: return MT25QL_ERR_WRONG_ARGUMENT; } if (addr >= dev->size) { return MT25QL_ERR_ADDR_TOO_BIG; } controller_error = qspi_ip6514e_send_cmd(dev->controller, erase_cmd, ARG_PTR_NOT_USED, ARG_NOT_USED, ARG_PTR_NOT_USED, ARG_NOT_USED, addr, addr_bytes, 0); /* No dummy cycles needed for any erase command. */ if (controller_error != QSPI_IP6514E_ERR_NONE) { return (enum mt25ql_error_t)controller_error; } /* Wait until the erase operation is complete */ library_error = wait_program_or_erase_complete(dev); if (library_error != MT25QL_ERR_NONE) { return (enum mt25ql_error_t)controller_error; } return MT25QL_ERR_NONE; }