/**************************************************************************** * drivers/mtd/cfi.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you 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. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include "cfi.h" /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ /* type of port */ #define CFI_PORT_8BIT 0x01 #define CFI_PORT_16BIT 0x02 #define CFI_PORT_32BIT 0X04 #define CFI_PORT_64BIT 0x08 /* Common CFI value */ #define CFI_CMD 0x98 #define CFI_QRY_OFFSET 0x10 #define CFI_ERASE_BLOCK_REGION_INFO_OFFSET 0x2D /* CFI command set number */ #define CFI_CMDSET_INTEL_EXTENDED 0x0001 #define CFI_CMDSET_AMD_STANDARD 0x0002 #define CFI_CMDSET_INTEL_STANDARD 0x0003 #define CFI_CMDSET_AMD_EXTENDED 0x0004 #define CFI_CMDSET_MITS_STANDARD 0x0100 #define CFI_CMDSET_MITS_EXTENDED 0x0101 /* Intel command value */ #define CFI_INTEL_CMD_RESET 0xFF #define CFI_INTEL_CMD_CLEAR_STATUS 0x50 #define CFI_INTEL_CMD_BLOCK_ERASE 0x20 #define CFI_INTEL_CMD_ERASE_CONFIRM 0xD0 #define CFI_INTEL_CMD_WRITE 0x40 #define CFI_INTEL_CMD_WRITE_BUFFER 0xE8 #define CFI_INTEL_CMD_WRITE_CONFIRM 0xD0 /* Intel status value */ #define CFI_INTEL_STATUS_DONE 0x80 #define CFI_INTEL_STATUS_ESS 0x40 /* erase suspended */ #define CFI_INTEL_STATUS_ECLBS 0x20 /* clear block lock bit * or erase error */ #define CFI_INTEL_STATUS_PSLBS 0X10 /* write or block lock bit * set error */ #define CFI_INTEL_STATUS_VPENS 0x08 /* voltage range error */ #define CFI_INTEL_STATUS_PSS 0x04 /* write is suspended */ #define CFI_INTEL_STATUS_DPS 0x02 /* device protected error */ /* Amd command value */ #define CFI_AMD_CMD_RESET 0xF0 #define CFI_AMD_CMD_UNLOCK1 0xAA #define CFI_AMD_CMD_UNLOCK2 0x55 #define CFI_AMD_CMD_ERASE_START 0x80 #define CFI_AMD_CMD_ERASE_SECTOR 0x30 #define CFI_AMD_CMD_WRITE 0xA0 #define CFI_AMD_CMD_WRITE_BUFFER 0x25 #define CFI_AMD_CMD_WRITE_CONFIRM 0x29 /* Amd status value */ #define CFI_AMD_STATUS_TOGGLE 0x40 /* DQ6 will toggle after a * reading cycle during * erasing */ /**************************************************************************** * Private Types ****************************************************************************/ /* Supports up to 64bits bank width */ typedef uint64_t cfiword_t; /**************************************************************************** * Private Data ****************************************************************************/ /* the offset of CFI query address is usually 0x55 * Spansion S29WS-N CFI query fix is to try 0x555 if 0x55 fails */ static const uint32_t g_cfi_query_address[2] = { 0x55, 0x555 }; /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: cfi_get_block_addr * * Description: * Get the start address of the block. * ****************************************************************************/ static uintptr_t cfi_get_block_addr(FAR struct cfi_dev_s *cfi, blkcnt_t block) { uint8_t i; uintptr_t base = cfi->base_addr; for (i = 0; i < cfi->info.erase_region_num; i++) { blkcnt_t block_num = cfi_get_blocknum(cfi, i); size_t block_size = cfi_get_blocksize(cfi, i); if (block >= block_num) { block -= block_num; base += block_num * block_size; continue; } base += block * block_size; break; } return base; } /**************************************************************************** * Name: cfi_addr_map * * Description: * Get the address according to block number and offset, and needs to * convert it according to bankwidth. * ****************************************************************************/ static uintptr_t cfi_addr_map(FAR struct cfi_dev_s *cfi, blkcnt_t block, off_t offset) { return cfi_get_block_addr(cfi, block) + cfi->bankwidth * offset; } /**************************************************************************** * Name: cfi_make_cmd * * Description: * Make command value according to bankwidth and device width. For example, * a command 0x98 should be 0x00980098 for a flash with bankwidth = 4 and * dev_width = 2. * ****************************************************************************/ static cfiword_t cfi_make_cmd(FAR struct cfi_dev_s *cfi, uint32_t cmd) { cfiword_t result = 0; uint8_t i = cfi->bankwidth / cfi->dev_width; while (i--) { result = (result << (8 * cfi->dev_width)) | cmd; } return result; } /**************************************************************************** * Name: cfi_add_byte * * Description: * Add a byte to cfiword, which will be written into cfi-flash later. * ****************************************************************************/ static void cfi_add_byte(FAR struct cfi_dev_s *cfi, FAR cfiword_t *word, uint8_t c) { switch (cfi->bankwidth) { case CFI_PORT_8BIT: *word = c; break; case CFI_PORT_16BIT: *word = (*word >> 8) | (uint16_t)c << 8; break; case CFI_PORT_32BIT: *word = (*word >> 8) | (uint32_t)c << 24; break; default: *word = (*word >> 8) | (uint64_t)c << 56; } } /**************************************************************************** * Name: cfi_write_data * * Description: * Write a value to specified address. * ****************************************************************************/ static inline void cfi_write_data(FAR struct cfi_dev_s *cfi, uintptr_t addr, FAR const void *buffer) { switch (cfi->bankwidth) { case CFI_PORT_8BIT: *(FAR volatile uint8_t *)addr = *(FAR const uint8_t *)buffer; break; case CFI_PORT_16BIT: *(FAR volatile uint16_t *)addr = *(FAR const uint16_t *)buffer; break; case CFI_PORT_32BIT: *(FAR volatile uint32_t *)addr = *(FAR const uint32_t *)buffer; break; default: *(FAR volatile uint64_t *)addr = *(FAR const uint64_t *)buffer; } } /**************************************************************************** * Name: cfi_write_block_cmd * * Description: * Write a command about specified block. * ****************************************************************************/ static void cfi_write_block_cmd(FAR struct cfi_dev_s *cfi, blkcnt_t block, uint32_t cmd) { cfiword_t val = cfi_make_cmd(cfi, cmd); cfi_write_data(cfi, cfi_addr_map(cfi, block, 0), &val); } /**************************************************************************** * Name: cfi_write_address_cmd * * Description: * Write a command to specified address. * ****************************************************************************/ static void cfi_write_address_cmd(FAR struct cfi_dev_s *cfi, uintptr_t addr, uint8_t cmd) { cfiword_t val = cfi_make_cmd(cfi, cmd); cfi_write_data(cfi, cfi_addr_map(cfi, 0, addr), &val); } /**************************************************************************** * Name: cfi_read_byte * * Description: * Read an 8-bit value from addr. * ****************************************************************************/ static inline uint8_t cfi_read_byte(uintptr_t addr) { return *(FAR volatile uint8_t *)addr; } /**************************************************************************** * Name: cfi_read_data * * Description: * Read a value to specified address. * ****************************************************************************/ static inline cfiword_t cfi_read_data(FAR struct cfi_dev_s *cfi, uintptr_t addr) { switch (cfi->bankwidth) { case CFI_PORT_8BIT: return cfi_read_byte(addr); case CFI_PORT_16BIT: return *(FAR const volatile uint16_t *)addr; case CFI_PORT_32BIT: return *(FAR const volatile uint32_t *)addr; default: return *(FAR const volatile uint64_t *)addr; } } /**************************************************************************** * Name: cfi_is_equal * * Description: * Check whether the value corresponding to addr is equal to cmd. * ****************************************************************************/ static bool cfi_is_equal(FAR struct cfi_dev_s *cfi, uintptr_t addr, uint8_t cmd) { cfiword_t val = cfi_make_cmd(cfi, cmd); addr = cfi_addr_map(cfi, 0, addr); return cfi_read_data(cfi, addr) == val; } /**************************************************************************** * Name: cfi_is_set * * Description: * Check the status bit, which is set by cmd. * ****************************************************************************/ static bool cfi_is_set(FAR struct cfi_dev_s *cfi, uint8_t cmd) { cfiword_t val = cfi_make_cmd(cfi, cmd); uintptr_t addr = cfi->base_addr; return (cfi_read_data(cfi, addr) & val) == val; } /**************************************************************************** * Name: cfi_get_time_us * * Description: * Check whether the value corresponding to offset is equal to cmd. * ****************************************************************************/ static inline uint32_t cfi_get_time_us(void) { struct timespec ts; clock_systime_timespec(&ts); return 1000000 * ts.tv_sec + ts.tv_nsec / 1000; } /**************************************************************************** * Name: cfi_is_timeout * * Description: * Check whether the operation has timed out. * ****************************************************************************/ static bool cfi_is_timeout(uint32_t start_us, uint32_t tout_us) { return (int32_t)(cfi_get_time_us() - start_us) >= tout_us; } /**************************************************************************** * Name: cfi_intel_is_busy * * Description: * Function of intel cfi command set. * Check whether the cfi-flash is busy. * ****************************************************************************/ static bool cfi_intel_is_busy(FAR struct cfi_dev_s *cfi, blkcnt_t block) { return !cfi_is_set(cfi, CFI_INTEL_STATUS_DONE); } /**************************************************************************** * Name: cfi_intel_status_check * * Description: * Function of intel cfi command set. * Check the status register of the cfi-flash. * ****************************************************************************/ static int cfi_intel_status_check(FAR struct cfi_dev_s *cfi, blkcnt_t block, uint32_t tout) { uint32_t start; /* wait for command completion */ start = cfi_get_time_us(); while (cfi_intel_is_busy(cfi, block)) { if (!cfi_is_timeout(start, tout)) { up_udelay(1); continue; } ferr("CFI flash operation has timed out.\n"); /* Check each status bit */ if (!cfi_is_equal(cfi, 0, CFI_INTEL_STATUS_DONE)) { ferr("CFI flash operation failed.\n"); if (cfi_is_set(cfi, CFI_INTEL_STATUS_ECLBS | CFI_INTEL_STATUS_PSLBS)) { ferr("CFI: command sequence error\n"); } else if (cfi_is_set(cfi, CFI_INTEL_STATUS_ECLBS)) { ferr("CFI: block erase error!\n"); } else if (cfi_is_set(cfi, CFI_INTEL_STATUS_PSLBS)) { ferr("CFI: locking error!\n"); } else if (cfi_is_set(cfi, CFI_INTEL_STATUS_DPS)) { ferr("CFI: block locked!\n"); } else if (cfi_is_set(cfi, CFI_INTEL_STATUS_VPENS)) { ferr("CFI: Vpp Low error!\n"); } } cfi_reset(cfi); return -EAGAIN; } return 0; } /**************************************************************************** * Name: cfi_intel_write_word * * Description: * Function of intel cfi command set. * Write data with the width of cfiword_t into cfi-flash. * ****************************************************************************/ static int cfi_intel_write_word(FAR struct cfi_dev_s *cfi, uintptr_t addr, cfiword_t word) { off_t offset = addr - cfi->base_addr; cfi_write_address_cmd(cfi, 0, CFI_INTEL_CMD_CLEAR_STATUS); cfi_write_address_cmd(cfi, offset / cfi->bankwidth, CFI_INTEL_CMD_WRITE); cfi_write_data(cfi, addr, &word); return cfi_intel_status_check(cfi, cfi_find_block(cfi, offset), (1 << cfi->info.single_write_timeout_typ) * (1 << cfi->info.single_write_timeout_max)); } /**************************************************************************** * Name: cfi_intel_write_buffer * * Description: * Function of intel cfi command set. * Write data from buffer into cfi-flash. * ****************************************************************************/ static int cfi_intel_write_buffer(FAR struct cfi_dev_s *cfi, uintptr_t addr, size_t nbytes, FAR const uint8_t *buffer) { int ret; blkcnt_t block; off_t offset = addr - cfi->base_addr; block = cfi_find_block(cfi, offset); cfi_write_address_cmd(cfi, 0, CFI_INTEL_CMD_CLEAR_STATUS); cfi_write_block_cmd(cfi, block, CFI_INTEL_CMD_WRITE_BUFFER); ret = cfi_intel_status_check(cfi, block, (1 << cfi->info.buffer_write_timeout_typ) * (1 << cfi->info.buffer_write_timeout_max)); if (ret < 0) { return ret; } addr = cfi->base_addr + offset; nbytes = nbytes / cfi->bankwidth; cfi_write_block_cmd(cfi, block, nbytes - 1); while (nbytes-- > 0) { cfi_write_data(cfi, addr, buffer); addr += cfi->bankwidth; buffer += cfi->bankwidth; } cfi_write_address_cmd(cfi, 0, CFI_INTEL_CMD_WRITE_CONFIRM); return cfi_intel_status_check(cfi, block, (1 << cfi->info.buffer_write_timeout_typ) * (1 << cfi->info.buffer_write_timeout_max)); } /**************************************************************************** * Name: cfi_intel_erase * * Description: * Function of intel cfi command set. * Erase one block. * ****************************************************************************/ static int cfi_intel_erase(FAR struct cfi_dev_s *cfi, blkcnt_t block) { cfi_write_address_cmd(cfi, 0, CFI_INTEL_CMD_CLEAR_STATUS); cfi_write_block_cmd(cfi, block, CFI_INTEL_CMD_BLOCK_ERASE); cfi_write_address_cmd(cfi, 0, CFI_INTEL_CMD_ERASE_CONFIRM); return cfi_intel_status_check(cfi, block, 1000 * (1 << cfi->info.block_erase_timeout_typ) * (1 << cfi->info.block_erase_timeout_max)); } /**************************************************************************** * Name: cfi_amd_fix * * Description: * Set the variables specific to AMD. * ****************************************************************************/ static void cfi_amd_fix(FAR struct cfi_dev_s *cfi) { /* x8/x16 in x8 mode or x16/x32 in x16 mode */ if ((cfi->dev_width == 1 && cfi->info.interface_desc == 0x02) || (cfi->dev_width == 2 && cfi->info.interface_desc == 0x04)) { cfi->unlock_addr1 = 0xaaa; cfi->unlock_addr2 = 0x555; } else { cfi->unlock_addr1 = 0x555; cfi->unlock_addr2 = 0x2aa; } } /**************************************************************************** * Name: cfi_amd_unlock * * Description: * Flash unlock command sequence specific to AMD. * ****************************************************************************/ static void cfi_amd_unlock(FAR struct cfi_dev_s *cfi) { cfi_write_address_cmd(cfi, cfi->unlock_addr1, CFI_AMD_CMD_UNLOCK1); cfi_write_address_cmd(cfi, cfi->unlock_addr2, CFI_AMD_CMD_UNLOCK2); } /**************************************************************************** * Name: cfi_amd_is_busy * * Description: * Function of amd cfi command set. * Check whether the cfi-flash is busy. * ****************************************************************************/ static bool cfi_amd_is_busy(FAR struct cfi_dev_s *cfi, blkcnt_t block) { uintptr_t addr; addr = cfi_addr_map(cfi, block, 0); /* status bit DQ6 will toggle after a reading cycle during erasing */ return ((cfi_read_byte(addr) ^ cfi_read_byte(addr)) & CFI_AMD_STATUS_TOGGLE) != 0; } /**************************************************************************** * Name: cfi_amd_status_check * * Description: * Function of amd cfi command set. * Check the status register of the cfi-flash. * ****************************************************************************/ static int cfi_amd_status_check(FAR struct cfi_dev_s *cfi, blkcnt_t block, uint32_t tout) { uint32_t start; /* wait for command completion */ start = cfi_get_time_us(); while (cfi_amd_is_busy(cfi, block)) { if (cfi_is_timeout(start, tout)) { ferr("CFI flash operation has timed out.\n"); cfi_reset(cfi); return -EAGAIN; } up_udelay(1); } return 0; } /**************************************************************************** * Name: cfi_amd_write_word * * Description: * Function of amd cfi command set. * Write data with the width of cfiword_t into cfi-flash. * ****************************************************************************/ static int cfi_amd_write_word(FAR struct cfi_dev_s *cfi, uintptr_t addr, cfiword_t word) { off_t offset = addr - cfi->base_addr; cfi_amd_unlock(cfi); cfi_write_address_cmd(cfi, cfi->unlock_addr1, CFI_AMD_CMD_WRITE); cfi_write_data(cfi, addr, &word); return cfi_amd_status_check(cfi, cfi_find_block(cfi, offset), (1 << cfi->info.single_write_timeout_typ) * (1 << cfi->info.single_write_timeout_max)); } /**************************************************************************** * Name: cfi_amd_write_buffer * * Description: * Function of amd cfi command set. * Write data from buffer into cfi-flash. * ****************************************************************************/ static int cfi_amd_write_buffer(FAR struct cfi_dev_s *cfi, uintptr_t addr, size_t nbytes, FAR const uint8_t *buffer) { blkcnt_t block; off_t offset = addr - cfi->base_addr; block = cfi_find_block(cfi, offset); cfi_amd_unlock(cfi); cfi_write_block_cmd(cfi, block, CFI_AMD_CMD_WRITE_BUFFER); nbytes = nbytes / cfi->bankwidth; cfi_write_block_cmd(cfi, block, nbytes - 1); while (nbytes-- > 0) { cfi_write_data(cfi, addr, buffer); addr += cfi->bankwidth; buffer += cfi->bankwidth; } cfi_write_address_cmd(cfi, 0, CFI_AMD_CMD_WRITE_CONFIRM); return cfi_amd_status_check(cfi, block, (1 << cfi->info.buffer_write_timeout_max) * (1 << cfi->info.buffer_write_timeout_max)); } /**************************************************************************** * Name: cfi_amd_erase * * Description: * Function of amd cfi command set. * Erase one block. * ****************************************************************************/ static int cfi_amd_erase(FAR struct cfi_dev_s *cfi, blkcnt_t block) { cfi_amd_unlock(cfi); cfi_write_address_cmd(cfi, cfi->unlock_addr1, CFI_AMD_CMD_ERASE_START); cfi_amd_unlock(cfi); cfi_write_block_cmd(cfi, block, CFI_AMD_CMD_ERASE_SECTOR); return cfi_amd_status_check(cfi, block, 1000 * (1 << cfi->info.block_erase_timeout_typ) * (1 << cfi->info.block_erase_timeout_max)); } /**************************************************************************** * Name: cfi_write_word * * Description: * Write data with the width of cfiword_t into cfi-flash. * ****************************************************************************/ static int cfi_write_word(FAR struct cfi_dev_s *cfi, uintptr_t addr, cfiword_t word) { switch (cfi->info.p_id) { case CFI_CMDSET_INTEL_EXTENDED: case CFI_CMDSET_INTEL_STANDARD: return cfi_intel_write_word(cfi, addr, word); case CFI_CMDSET_AMD_STANDARD: case CFI_CMDSET_AMD_EXTENDED: return cfi_amd_write_word(cfi, addr, word); case CFI_CMDSET_MITS_STANDARD: case CFI_CMDSET_MITS_EXTENDED: default: return -EINVAL; } } /**************************************************************************** * Name: cfi_write_buffer * * Description: * Write data from buffer into cfi-flash. * ****************************************************************************/ static int cfi_write_buffer(FAR struct cfi_dev_s *cfi, uintptr_t addr, size_t nbytes, FAR const uint8_t *buffer) { switch (cfi->info.p_id) { case CFI_CMDSET_INTEL_EXTENDED: case CFI_CMDSET_INTEL_STANDARD: return cfi_intel_write_buffer(cfi, addr, nbytes, buffer); case CFI_CMDSET_AMD_STANDARD: case CFI_CMDSET_AMD_EXTENDED: return cfi_amd_write_buffer(cfi, addr, nbytes, buffer); case CFI_CMDSET_MITS_STANDARD: case CFI_CMDSET_MITS_EXTENDED: default: return -EINVAL; } } /**************************************************************************** * Name: cfi_erase_one * * Description: * Erase one block. * ****************************************************************************/ static int cfi_erase_one(FAR struct cfi_dev_s *cfi, blkcnt_t block) { switch (cfi->info.p_id) { case CFI_CMDSET_INTEL_EXTENDED: case CFI_CMDSET_INTEL_STANDARD: return cfi_intel_erase(cfi, block); case CFI_CMDSET_AMD_STANDARD: case CFI_CMDSET_AMD_EXTENDED: return cfi_amd_erase(cfi, block); case CFI_CMDSET_MITS_STANDARD: case CFI_CMDSET_MITS_EXTENDED: default: return -EINVAL; } } static ssize_t cfi_write_unalign(FAR struct cfi_dev_s *cfi, off_t offset, size_t nbytes, FAR const uint8_t *buffer) { uint8_t i; int ret; size_t delta; uintptr_t wp; cfiword_t word = 0; /* get lower aligned address */ wp = offset & ~(cfi->bankwidth - 1); /* handle unaligned start */ if ((delta = offset - wp) == 0) { return 0; } for (i = 0; i < delta; i++) { cfi_add_byte(cfi, &word, cfi_read_byte(wp + i)); } delta = 0; for (; i < cfi->bankwidth && nbytes > 0; i++) { cfi_add_byte(cfi, &word, *buffer++); nbytes--; delta++; } for (; i < cfi->bankwidth; i++) { cfi_add_byte(cfi, &word, cfi_read_byte(wp + i)); } ret = cfi_write_word(cfi, wp, word); return ret < 0 ? ret : delta; } /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: cfi_get_blocksize * * Description: * Get the size of block in the specified region. * ****************************************************************************/ size_t cfi_get_blocksize(FAR struct cfi_dev_s *cfi, uint8_t region) { return cfi->dev_num * (cfi->info.erase_region_info[region] >> 16) * 256; } /**************************************************************************** * Name: cfi_get_blocknum * * Description: * Get the number of blocks in the specified region. * ****************************************************************************/ blkcnt_t cfi_get_blocknum(FAR struct cfi_dev_s *cfi, uint8_t region) { return ((uint16_t)(cfi->info.erase_region_info[region])) + 1; } /**************************************************************************** * Name: cfi_find_block * * Description: * Get the block number corresponding to offset. * ****************************************************************************/ blkcnt_t cfi_find_block(FAR struct cfi_dev_s *cfi, off_t offset) { uint8_t i; blkcnt_t ret = 0; for (i = 0; i < cfi->info.erase_region_num; i++) { blkcnt_t block_num = cfi_get_blocknum(cfi, i); size_t block_size = cfi_get_blocksize(cfi, i); if (block_num * block_size <= offset) { offset -= block_num * block_size; ret += block_num; } else { ret += offset / block_size; break; } } return ret; } /**************************************************************************** * Name: cfi_get_total_blocknum * * Description: * Get total number of erase blocks, assuming that all erase blocks are the * same size. * ****************************************************************************/ blkcnt_t cfi_get_total_blocknum(FAR struct cfi_dev_s *cfi) { uint8_t i; blkcnt_t blocknum = 0; for (i = 0; i < cfi->info.erase_region_num; i++) { blocknum += cfi_get_blocknum(cfi, i) * cfi_get_blocksize(cfi, i) / cfi_get_blocksize(cfi, 0); } return blocknum; } /**************************************************************************** * Name: cfi_check * * Description: * Check whether the device is a cfi device, if so, then get the cfi data. * ****************************************************************************/ int cfi_check(FAR struct cfi_dev_s *cfi) { uint8_t i; uint8_t j; /* try different single device width which we don't know now */ for (cfi->dev_width = 1; cfi->dev_width <= cfi->bankwidth; cfi->dev_width <<= 1) { cfi_reset(cfi); for (i = 0; i < nitems(g_cfi_query_address); i++) { cfi_write_address_cmd(cfi, g_cfi_query_address[i], CFI_CMD); /* try to get into cfi mode and read cfi data */ if (cfi_is_equal(cfi, CFI_QRY_OFFSET, 'Q') && cfi_is_equal(cfi, CFI_QRY_OFFSET + 1, 'R') && cfi_is_equal(cfi, CFI_QRY_OFFSET + 2, 'Y')) { FAR struct cfi_info_s *info = &(cfi->info); FAR uint8_t *ptr = (FAR uint8_t *)info; for (j = CFI_QRY_OFFSET; j < CFI_ERASE_BLOCK_REGION_INFO_OFFSET; j++) { *ptr++ = cfi_read_byte(cfi_addr_map(cfi, 0, j)); } while (j < CFI_ERASE_BLOCK_REGION_INFO_OFFSET + 4 * info->erase_region_num) { *ptr++ = cfi_read_byte(cfi_addr_map(cfi, 0, j++)); } cfi->cfi_offset = g_cfi_query_address[i]; cfi->dev_num = cfi->bankwidth / cfi->dev_width; cfi->page_size = (1 << info->max_write_bytes_num) * cfi->dev_num; /* fix amd feature */ if (info->p_id == CFI_CMDSET_AMD_STANDARD || info->p_id == CFI_CMDSET_AMD_EXTENDED) { cfi_amd_fix(cfi); } /* exit cfi mode, enter into read array mode */ cfi_reset(cfi); return 0; } } } /* cfi flash not found */ return -EINVAL; } /**************************************************************************** * Name: cfi_reset * * Description: * Get into read array mode. * ****************************************************************************/ void cfi_reset(FAR struct cfi_dev_s *cfi) { switch (cfi->info.p_id) { case CFI_CMDSET_INTEL_EXTENDED: case CFI_CMDSET_INTEL_STANDARD: cfi_write_address_cmd(cfi, 0, CFI_INTEL_CMD_CLEAR_STATUS); cfi_write_address_cmd(cfi, 0, CFI_INTEL_CMD_RESET); break; case CFI_CMDSET_AMD_STANDARD: case CFI_CMDSET_AMD_EXTENDED: cfi_write_address_cmd(cfi, 0, CFI_AMD_CMD_RESET); } } /**************************************************************************** * Name: cfi_erase * * Description: * Erase multi blocks. * ****************************************************************************/ int cfi_erase(FAR struct cfi_dev_s *cfi, blkcnt_t startblock, blkcnt_t blockcnt) { blkcnt_t i; int ret = 0; for (i = startblock; i < startblock + blockcnt; i++) { ret = cfi_erase_one(cfi, i); if (ret < 0) { break; } } cfi_reset(cfi); return ret; } /**************************************************************************** * Name: cfi_read * * Description: * Read data from cfi-flash to buffer. * ****************************************************************************/ int cfi_read(FAR struct cfi_dev_s *cfi, off_t offset, size_t nbytes, FAR uint8_t *buffer) { uintptr_t base = cfi->base_addr; memcpy(buffer, (FAR const uint8_t *)base + offset, nbytes); return 0; } /**************************************************************************** * Name: cfi_write * * Description: * Write data from buffer to cfi-flash. * ****************************************************************************/ int cfi_write(FAR struct cfi_dev_s *cfi, off_t offset, size_t nbytes, FAR const uint8_t *buffer) { ssize_t ret; offset = cfi->base_addr + offset; ret = cfi_write_unalign(cfi, offset, nbytes, buffer); if (ret < 0) { goto out; } offset = ALIGN_UP(offset, cfi->bankwidth); nbytes -= ret; buffer += ret; /* handle the aligned part */ /* if the device support buffer write */ if (cfi->info.max_write_bytes_num != 0) { while (nbytes >= cfi->bankwidth) { size_t size = cfi->page_size - offset % cfi->page_size; if (size > nbytes) { size = nbytes; } ret = cfi_write_buffer(cfi, offset, size, buffer); if (ret < 0) { goto out; } offset += size; buffer += size; nbytes -= size; } } else { while (nbytes >= cfi->bankwidth) { cfiword_t word = 0; uint8_t i; for (i = 0; i < cfi->bankwidth; i++) { cfi_add_byte(cfi, &word, *buffer++); } ret = cfi_write_word(cfi, offset, word); if (ret < 0) { goto out; } offset += cfi->bankwidth; nbytes -= cfi->bankwidth; } } if (nbytes == 0) { goto out; } /* handle unaligned tail bytes */ ret = cfi_write_unalign(cfi, offset, nbytes, buffer); out: cfi_reset(cfi); return ret < 0 ? ret : 0; }