1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/notrynohigh-BabyOS

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
sfud.c 32 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
DHC Отправлено 4 месяцев назад 8d83e0c
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169
/*
* This file is part of the Serial Flash Universal Driver Library.
*
* Copyright (c) 2016-2018, Armink, <armink.ztl@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* 'Software'), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Function: serial flash operate functions by SFUD lib.
* Created on: 2016-04-23
*/
#include "inc/sfud.h"
#include <string.h>
/* send dummy data for read data */
#define DUMMY_DATA 0xFF
// #ifndef SFUD_FLASH_DEVICE_TABLE
// #error "Please configure the flash device information table in (in sfud_cfg.h)."
// #endif
/* supported manufacturer information table */
static const sfud_mf mf_table[] = SFUD_MF_TABLE;
#ifdef SFUD_USING_FLASH_INFO_TABLE
/* supported flash chip information table */
static const sfud_flash_chip flash_chip_table[] = SFUD_FLASH_CHIP_TABLE;
#endif
#ifdef SFUD_USING_QSPI
/**
* flash read data mode
*/
enum sfud_qspi_read_mode
{
NORMAL_SPI_READ = 1 << 0, /**< mormal spi read mode */
DUAL_OUTPUT = 1 << 1, /**< qspi fast read dual output */
DUAL_IO = 1 << 2, /**< qspi fast read dual input/output */
QUAD_OUTPUT = 1 << 3, /**< qspi fast read quad output */
QUAD_IO = 1 << 4, /**< qspi fast read quad input/output */
};
/* QSPI flash chip's extended information table */
static const sfud_qspi_flash_ext_info qspi_flash_ext_info_table[] = SFUD_FLASH_EXT_INFO_TABLE;
#endif /* SFUD_USING_QSPI */
static sfud_err software_init(const sfud_flash *flash);
static sfud_err hardware_init(sfud_flash *flash);
static sfud_err page256_or_1_byte_write(const sfud_flash *flash, uint32_t addr, size_t size,
uint16_t write_gran, const uint8_t *data);
static sfud_err aai_write(const sfud_flash *flash, uint32_t addr, size_t size, const uint8_t *data);
static sfud_err wait_busy(const sfud_flash *flash);
static sfud_err reset(const sfud_flash *flash);
static sfud_err read_jedec_id(sfud_flash *flash);
static sfud_err set_write_enabled(const sfud_flash *flash, SFUD_BOOL enabled);
static sfud_err set_4_byte_address_mode(sfud_flash *flash, SFUD_BOOL enabled);
static void make_address_byte_array(const sfud_flash *flash, uint32_t addr, uint8_t *array);
/* ../port/sfup_port.c */
extern void sfud_log_debug(const char *file, const long line, const char *format, ...);
extern void sfud_log_info(const char *format, ...);
/**
* SFUD initialize by flash device
*
* @param flash flash device
*
* @return result
*/
sfud_err sfud_device_init(sfud_flash *flash)
{
sfud_err result = SFUD_SUCCESS;
/* hardware initialize */
result = hardware_init(flash);
if (result == SFUD_SUCCESS)
{
result = software_init(flash);
}
if (result == SFUD_SUCCESS)
{
flash->init_ok = SFUD_TRUE;
SFUD_INFO("%s flash device is initialize success.", flash->name);
}
else
{
flash->init_ok = SFUD_FALSE;
SFUD_INFO("Error: %s flash device is initialize fail.", flash->name);
}
return result;
}
#ifdef SFUD_USING_QSPI
static void qspi_set_read_cmd_format(sfud_flash *flash, uint8_t ins, uint8_t ins_lines,
uint8_t addr_lines, uint8_t dummy_cycles, uint8_t data_lines)
{
/* if medium size greater than 16Mb, use 4-Byte address, instruction should be added one */
if (flash->chip.capacity <= 0x1000000)
{
flash->read_cmd_format.instruction = ins;
flash->read_cmd_format.address_size = 24;
}
else
{
if (ins == SFUD_CMD_READ_DATA) {
flash -> read_cmd_format.instruction = ins + 0x10;
} else {
flash->read_cmd_format.instruction = ins + 1;
}
flash->read_cmd_format.address_size = 32;
}
flash->read_cmd_format.instruction_lines = ins_lines;
flash->read_cmd_format.address_lines = addr_lines;
flash->read_cmd_format.alternate_bytes_lines = 0;
flash->read_cmd_format.dummy_cycles = dummy_cycles;
flash->read_cmd_format.data_lines = data_lines;
}
/**
* Enbale the fast read mode in QSPI flash mode. Default read mode is normal SPI mode.
*
* it will find the appropriate fast-read instruction to replace the read instruction(0x03)
* fast-read instruction @see SFUD_FLASH_EXT_INFO_TABLE
*
* @note When Flash is in QSPI mode, the method must be called after sfud_device_init().
*
* @param flash flash device
* @param data_line_width the data lines max width which QSPI bus supported, such as 1, 2, 4
*
* @return result
*/
sfud_err sfud_qspi_fast_read_enable(sfud_flash *flash, uint8_t data_line_width)
{
size_t i = 0;
uint8_t read_mode = NORMAL_SPI_READ;
sfud_err result = SFUD_SUCCESS;
SFUD_ASSERT(flash);
SFUD_ASSERT(data_line_width == 1 || data_line_width == 2 || data_line_width == 4);
/* get read_mode, If don't found, the default is SFUD_QSPI_NORMAL_SPI_READ */
for (i = 0; i < sizeof(qspi_flash_ext_info_table) / sizeof(sfud_qspi_flash_ext_info); i++)
{
if ((qspi_flash_ext_info_table[i].mf_id == flash->chip.mf_id) &&
(qspi_flash_ext_info_table[i].type_id == flash->chip.type_id) &&
(qspi_flash_ext_info_table[i].capacity_id == flash->chip.capacity_id))
{
read_mode = qspi_flash_ext_info_table[i].read_mode;
}
}
/* determine qspi supports which read mode and set read_cmd_format struct */
switch (data_line_width)
{
case 1:
qspi_set_read_cmd_format(flash, SFUD_CMD_READ_DATA, 1, 1, 0, 1);
break;
case 2:
if (read_mode & DUAL_IO)
{
qspi_set_read_cmd_format(flash, SFUD_CMD_DUAL_IO_READ_DATA, 1, 2, 4, 2);
}
else if (read_mode & DUAL_OUTPUT)
{
qspi_set_read_cmd_format(flash, SFUD_CMD_DUAL_OUTPUT_READ_DATA, 1, 1, 8, 2);
}
else
{
qspi_set_read_cmd_format(flash, SFUD_CMD_READ_DATA, 1, 1, 0, 1);
}
break;
case 4:
if (read_mode & QUAD_IO)
{
qspi_set_read_cmd_format(flash, SFUD_CMD_QUAD_IO_READ_DATA, 1, 4, 6, 4);
}
else if (read_mode & QUAD_OUTPUT)
{
qspi_set_read_cmd_format(flash, SFUD_CMD_QUAD_OUTPUT_READ_DATA, 1, 1, 8, 4);
}
else
{
qspi_set_read_cmd_format(flash, SFUD_CMD_READ_DATA, 1, 1, 0, 1);
}
break;
}
return result;
}
#endif /* SFUD_USING_QSPI */
/**
* hardware initialize
*/
static sfud_err hardware_init(sfud_flash *flash)
{
sfud_err result = SFUD_SUCCESS;
size_t i;
SFUD_ASSERT(flash);
#ifdef SFUD_USING_QSPI
/* set default read instruction */
flash->read_cmd_format.instruction = SFUD_CMD_READ_DATA;
#endif /* SFUD_USING_QSPI */
/* SPI write read function must be initialize */
SFUD_ASSERT(flash->spi.wr);
/* if the user don't configure flash chip information then using SFDP parameter or static flash
* parameter table */
if (flash->chip.capacity == 0 || flash->chip.write_mode == 0 || flash->chip.erase_gran == 0 ||
flash->chip.erase_gran_cmd == 0)
{
/* read JEDEC ID include manufacturer ID, memory type ID and flash capacity ID */
result = read_jedec_id(flash);
if (result != SFUD_SUCCESS)
{
return result;
}
#ifdef SFUD_USING_SFDP
extern SFUD_BOOL sfud_read_sfdp(sfud_flash * flash);
/* read SFDP parameters */
if (sfud_read_sfdp(flash))
{
flash->chip.name = NULL;
flash->chip.capacity = flash->sfdp.capacity;
/* only 1 byte or 256 bytes write mode for SFDP */
if (flash->sfdp.write_gran == 1)
{
flash->chip.write_mode = SFUD_WM_BYTE;
}
else
{
flash->chip.write_mode = SFUD_WM_PAGE_256B;
}
/* find the the smallest erase sector size for eraser. then will use this size for erase
* granularity */
flash->chip.erase_gran = flash->sfdp.eraser[0].size;
flash->chip.erase_gran_cmd = flash->sfdp.eraser[0].cmd;
for (i = 1; i < SFUD_SFDP_ERASE_TYPE_MAX_NUM; i++)
{
if (flash->sfdp.eraser[i].size != 0 &&
flash->chip.erase_gran > flash->sfdp.eraser[i].size)
{
flash->chip.erase_gran = flash->sfdp.eraser[i].size;
flash->chip.erase_gran_cmd = flash->sfdp.eraser[i].cmd;
}
}
}
else
{
#endif
#ifdef SFUD_USING_FLASH_INFO_TABLE
/* read SFDP parameters failed then using SFUD library provided static parameter */
for (i = 0; i < sizeof(flash_chip_table) / sizeof(sfud_flash_chip); i++)
{
if ((flash_chip_table[i].mf_id == flash->chip.mf_id) &&
(flash_chip_table[i].type_id == flash->chip.type_id) &&
(flash_chip_table[i].capacity_id == flash->chip.capacity_id))
{
flash->chip.name = flash_chip_table[i].name;
flash->chip.capacity = flash_chip_table[i].capacity;
flash->chip.write_mode = flash_chip_table[i].write_mode;
flash->chip.erase_gran = flash_chip_table[i].erase_gran;
flash->chip.erase_gran_cmd = flash_chip_table[i].erase_gran_cmd;
break;
}
}
#endif
#ifdef SFUD_USING_SFDP
}
#endif
}
if (flash->chip.capacity == 0 || flash->chip.write_mode == 0 || flash->chip.erase_gran == 0 ||
flash->chip.erase_gran_cmd == 0)
{
SFUD_INFO("Warning: This flash device is not found or not support.");
return SFUD_ERR_NOT_FOUND;
}
else
{
const char *flash_mf_name = NULL;
/* find the manufacturer information */
for (i = 0; i < sizeof(mf_table) / sizeof(sfud_mf); i++)
{
if (mf_table[i].id == flash->chip.mf_id)
{
flash_mf_name = mf_table[i].name;
break;
}
}
/* print manufacturer and flash chip name */
if (flash_mf_name && flash->chip.name)
{
SFUD_INFO("Find a %s %s flash chip. Size is %ld bytes.", flash_mf_name,
flash->chip.name, flash->chip.capacity);
}
else if (flash_mf_name)
{
SFUD_INFO("Find a %s flash chip. Size is %ld bytes.", flash_mf_name,
flash->chip.capacity);
}
else
{
SFUD_INFO("Find a flash chip. Size is %ld bytes.", flash->chip.capacity);
}
}
/* reset flash device */
result = reset(flash);
if (result != SFUD_SUCCESS)
{
return result;
}
/* I found when the flash write mode is supported AAI mode. The flash all blocks is protected,
* so need change the flash status to unprotected before write and erase operate. */
if (flash->chip.write_mode & SFUD_WM_AAI)
{
result = sfud_write_status(flash, SFUD_TRUE, 0x00);
}else {
/* MX25L3206E */
if ((0xC2 == flash -> chip.mf_id) && (0x20 == flash -> chip.type_id) && (0x16 == flash -> chip.capacity_id)) {
result = sfud_write_status(flash, SFUD_FALSE, 0x00);
}
}
if (result != SFUD_SUCCESS)
{
return result;
}
/* if the flash is large than 16MB (256Mb) then enter in 4-Byte addressing mode */
if (flash->chip.capacity > (1L << 24))
{
result = set_4_byte_address_mode(flash, SFUD_TRUE);
}
else
{
flash->addr_in_4_byte = SFUD_FALSE;
}
return result;
}
/**
* software initialize
*
* @param flash flash device
*
* @return result
*/
static sfud_err software_init(const sfud_flash *flash)
{
sfud_err result = SFUD_SUCCESS;
SFUD_ASSERT(flash);
return result;
}
/**
* read flash data
*
* @param flash flash device
* @param addr start address
* @param size read size
* @param data read data pointer
*
* @return result
*/
sfud_err sfud_read(const sfud_flash *flash, uint32_t addr, size_t size, uint8_t *data)
{
sfud_err result = SFUD_SUCCESS;
const sfud_spi *spi = &flash->spi;
uint8_t cmd_data[5 + SFUD_READ_DUMMY_BYTE_CNT], cmd_size;
uint8_t i;
SFUD_ASSERT(flash);
SFUD_ASSERT(data);
/* must be call this function after initialize OK */
SFUD_ASSERT(flash->init_ok);
/* check the flash address bound */
if (addr + size > flash->chip.capacity)
{
SFUD_INFO("Error: Flash address is out of bound.");
return SFUD_ERR_ADDR_OUT_OF_BOUND;
}
/* lock SPI */
if (spi->lock)
{
spi->lock(spi);
}
result = wait_busy(flash);
if (result == SFUD_SUCCESS)
{
#ifdef SFUD_USING_QSPI
if (flash->read_cmd_format.instruction != SFUD_CMD_READ_DATA)
{
result = spi->qspi_read(spi, addr, (sfud_qspi_read_cmd_format *)&flash->read_cmd_format,
data, size);
}
else
#endif
{
#ifdef SFUD_USING_FAST_READ
cmd_data[0] = SFUD_CMD_FAST_READ_DATA;
#else
cmd_data[0] = SFUD_CMD_READ_DATA;
#endif
make_address_byte_array(flash, addr, &cmd_data[1]);
cmd_size = flash->addr_in_4_byte ? 5 : 4;
for (i = 0; i < SFUD_READ_DUMMY_BYTE_CNT; i++) {
cmd_data[cmd_size] = SFUD_DUMMY_DATA;
cmd_size++;
}
result = spi->wr(spi, cmd_data, cmd_size, data, size);
}
}
/* unlock SPI */
if (spi->unlock)
{
spi->unlock(spi);
}
return result;
}
/**
* erase all flash data
*
* @param flash flash device
*
* @return result
*/
sfud_err sfud_chip_erase(const sfud_flash *flash)
{
sfud_err result = SFUD_SUCCESS;
const sfud_spi *spi = &flash->spi;
uint8_t cmd_data[4];
SFUD_ASSERT(flash);
/* must be call this function after initialize OK */
SFUD_ASSERT(flash->init_ok);
/* lock SPI */
if (spi->lock)
{
spi->lock(spi);
}
/* set the flash write enable */
result = set_write_enabled(flash, SFUD_TRUE);
if (result != SFUD_SUCCESS)
{
goto __exit;
}
cmd_data[0] = SFUD_CMD_ERASE_CHIP;
/* dual-buffer write, like AT45DB series flash chip erase operate is different for other flash
*/
if (flash->chip.write_mode & SFUD_WM_DUAL_BUFFER)
{
cmd_data[1] = 0x94;
cmd_data[2] = 0x80;
cmd_data[3] = 0x9A;
result = spi->wr(spi, cmd_data, 4, NULL, 0);
}
else
{
result = spi->wr(spi, cmd_data, 1, NULL, 0);
}
if (result != SFUD_SUCCESS)
{
SFUD_INFO("Error: Flash chip erase SPI communicate error.");
goto __exit;
}
result = wait_busy(flash);
__exit:
/* set the flash write disable */
set_write_enabled(flash, SFUD_FALSE);
/* unlock SPI */
if (spi->unlock)
{
spi->unlock(spi);
}
return result;
}
/**
* erase flash data
*
* @note It will erase align by erase granularity.
*
* @param flash flash device
* @param addr start address
* @param size erase size
*
* @return result
*/
sfud_err sfud_erase(const sfud_flash *flash, uint32_t addr, size_t size)
{
extern size_t sfud_sfdp_get_suitable_eraser(const sfud_flash *flash, uint32_t addr,
size_t erase_size);
sfud_err result = SFUD_SUCCESS;
const sfud_spi *spi = &flash->spi;
uint8_t cmd_data[5], cmd_size, cur_erase_cmd;
size_t cur_erase_size;
SFUD_ASSERT(flash);
/* must be call this function after initialize OK */
SFUD_ASSERT(flash->init_ok);
/* check the flash address bound */
if (addr + size > flash->chip.capacity)
{
SFUD_INFO("Error: Flash address is out of bound.");
return SFUD_ERR_ADDR_OUT_OF_BOUND;
}
if (addr == 0 && size == flash->chip.capacity)
{
return sfud_chip_erase(flash);
}
/* lock SPI */
if (spi->lock)
{
spi->lock(spi);
}
/* loop erase operate. erase unit is erase granularity */
while (size)
{
/* if this flash is support SFDP parameter, then used SFDP parameter supplies eraser */
#ifdef SFUD_USING_SFDP
size_t eraser_index;
if (flash->sfdp.available)
{
/* get the suitable eraser for erase process from SFDP parameter */
eraser_index = sfud_sfdp_get_suitable_eraser(flash, addr, size);
cur_erase_cmd = flash->sfdp.eraser[eraser_index].cmd;
cur_erase_size = flash->sfdp.eraser[eraser_index].size;
}
else
{
#else
{
#endif
cur_erase_cmd = flash->chip.erase_gran_cmd;
cur_erase_size = flash->chip.erase_gran;
}
/* set the flash write enable */
result = set_write_enabled(flash, SFUD_TRUE);
if (result != SFUD_SUCCESS)
{
goto __exit;
}
cmd_data[0] = cur_erase_cmd;
make_address_byte_array(flash, addr, &cmd_data[1]);
cmd_size = flash->addr_in_4_byte ? 5 : 4;
result = spi->wr(spi, cmd_data, cmd_size, NULL, 0);
if (result != SFUD_SUCCESS)
{
SFUD_INFO("Error: Flash erase SPI communicate error.");
goto __exit;
}
result = wait_busy(flash);
if (result != SFUD_SUCCESS)
{
goto __exit;
}
/* make erase align and calculate next erase address */
if (addr % cur_erase_size != 0)
{
if (size > cur_erase_size - (addr % cur_erase_size))
{
size -= cur_erase_size - (addr % cur_erase_size);
addr += cur_erase_size - (addr % cur_erase_size);
}
else
{
goto __exit;
}
}
else
{
if (size > cur_erase_size)
{
size -= cur_erase_size;
addr += cur_erase_size;
}
else
{
goto __exit;
}
}
}
__exit:
/* set the flash write disable */
set_write_enabled(flash, SFUD_FALSE);
/* unlock SPI */
if (spi->unlock)
{
spi->unlock(spi);
}
return result;
}
/**
* write flash data (no erase operate) for write 1 to 256 bytes per page mode or byte write mode
*
* @param flash flash device
* @param addr start address
* @param size write size
* @param write_gran write granularity bytes, only support 1 or 256
* @param data write data
*
* @return result
*/
static sfud_err page256_or_1_byte_write(const sfud_flash *flash, uint32_t addr, size_t size,
uint16_t write_gran, const uint8_t *data)
{
sfud_err result = SFUD_SUCCESS;
const sfud_spi *spi = &flash->spi;
static uint8_t cmd_data[5 + SFUD_WRITE_MAX_PAGE_SIZE];
uint8_t cmd_size;
size_t data_size;
SFUD_ASSERT(flash);
/* only support 1 or 256 */
SFUD_ASSERT(write_gran == 1 || write_gran == 256);
/* must be call this function after initialize OK */
SFUD_ASSERT(flash->init_ok);
/* check the flash address bound */
if (addr + size > flash->chip.capacity)
{
SFUD_INFO("Error: Flash address is out of bound.");
return SFUD_ERR_ADDR_OUT_OF_BOUND;
}
/* lock SPI */
if (spi->lock)
{
spi->lock(spi);
}
/* loop write operate. write unit is write granularity */
while (size)
{
/* set the flash write enable */
result = set_write_enabled(flash, SFUD_TRUE);
if (result != SFUD_SUCCESS)
{
goto __exit;
}
cmd_data[0] = SFUD_CMD_PAGE_PROGRAM;
make_address_byte_array(flash, addr, &cmd_data[1]);
cmd_size = flash->addr_in_4_byte ? 5 : 4;
/* make write align and calculate next write address */
if (addr % write_gran != 0)
{
if (size > write_gran - (addr % write_gran))
{
data_size = write_gran - (addr % write_gran);
}
else
{
data_size = size;
}
}
else
{
if (size > write_gran)
{
data_size = write_gran;
}
else
{
data_size = size;
}
}
size -= data_size;
addr += data_size;
memcpy(&cmd_data[cmd_size], data, data_size);
result = spi->wr(spi, cmd_data, cmd_size + data_size, NULL, 0);
if (result != SFUD_SUCCESS)
{
SFUD_INFO("Error: Flash write SPI communicate error.");
goto __exit;
}
result = wait_busy(flash);
if (result != SFUD_SUCCESS)
{
goto __exit;
}
data += data_size;
}
__exit:
/* set the flash write disable */
set_write_enabled(flash, SFUD_FALSE);
/* unlock SPI */
if (spi->unlock)
{
spi->unlock(spi);
}
return result;
}
/**
* write flash data (no erase operate) for auto address increment mode
*
* If the address is odd number, it will place one 0xFF before the start of data for protect the old
* data. If the latest remain size is 1, it will append one 0xFF at the end of data for protect the
* old data.
*
* @param flash flash device
* @param addr start address
* @param size write size
* @param data write data
*
* @return result
*/
static sfud_err aai_write(const sfud_flash *flash, uint32_t addr, size_t size, const uint8_t *data)
{
sfud_err result = SFUD_SUCCESS;
const sfud_spi *spi = &flash->spi;
uint8_t cmd_data[8], cmd_size;
SFUD_BOOL first_write = SFUD_TRUE;
SFUD_ASSERT(flash);
SFUD_ASSERT(flash->init_ok);
/* check the flash address bound */
if (addr + size > flash->chip.capacity)
{
SFUD_INFO("Error: Flash address is out of bound.");
return SFUD_ERR_ADDR_OUT_OF_BOUND;
}
/* lock SPI */
if (spi->lock)
{
spi->lock(spi);
}
/* The address must be even for AAI write mode. So it must write one byte first when address is
* odd. */
if (addr % 2 != 0)
{
result = page256_or_1_byte_write(flash, addr++, 1, 1, data++);
if (result != SFUD_SUCCESS)
{
goto __exit;
}
size--;
}
/* set the flash write enable */
result = set_write_enabled(flash, SFUD_TRUE);
if (result != SFUD_SUCCESS)
{
goto __exit;
}
/* loop write operate. */
cmd_data[0] = SFUD_CMD_AAI_WORD_PROGRAM;
while (size >= 2)
{
if (first_write)
{
make_address_byte_array(flash, addr, &cmd_data[1]);
cmd_size = flash->addr_in_4_byte ? 5 : 4;
cmd_data[cmd_size] = *data;
cmd_data[cmd_size + 1] = *(data + 1);
first_write = SFUD_FALSE;
}
else
{
cmd_size = 1;
cmd_data[1] = *data;
cmd_data[2] = *(data + 1);
}
result = spi->wr(spi, cmd_data, cmd_size + 2, NULL, 0);
if (result != SFUD_SUCCESS)
{
SFUD_INFO("Error: Flash write SPI communicate error.");
goto __exit;
}
result = wait_busy(flash);
if (result != SFUD_SUCCESS)
{
goto __exit;
}
size -= 2;
addr += 2;
data += 2;
}
/* set the flash write disable for exit AAI mode */
result = set_write_enabled(flash, SFUD_FALSE);
/* write last one byte data when origin write size is odd */
if (result == SFUD_SUCCESS && size == 1)
{
result = page256_or_1_byte_write(flash, addr, 1, 1, data);
}
__exit:
if (result != SFUD_SUCCESS)
{
set_write_enabled(flash, SFUD_FALSE);
}
/* unlock SPI */
if (spi->unlock)
{
spi->unlock(spi);
}
return result;
}
/**
* write flash data (no erase operate)
*
* @param flash flash device
* @param addr start address
* @param size write size
* @param data write data
*
* @return result
*/
sfud_err sfud_write(const sfud_flash *flash, uint32_t addr, size_t size, const uint8_t *data)
{
sfud_err result = SFUD_SUCCESS;
if (flash->chip.write_mode & SFUD_WM_PAGE_256B)
{
result = page256_or_1_byte_write(flash, addr, size, 256, data);
}
else if (flash->chip.write_mode & SFUD_WM_AAI)
{
result = aai_write(flash, addr, size, data);
}
else if (flash->chip.write_mode & SFUD_WM_DUAL_BUFFER)
{
// TODO dual-buffer write mode
}
return result;
}
/**
* erase and write flash data
*
* @param flash flash device
* @param addr start address
* @param size write size
* @param data write data
*
* @return result
*/
sfud_err sfud_erase_write(const sfud_flash *flash, uint32_t addr, size_t size, const uint8_t *data)
{
sfud_err result = SFUD_SUCCESS;
result = sfud_erase(flash, addr, size);
if (result == SFUD_SUCCESS)
{
result = sfud_write(flash, addr, size, data);
}
return result;
}
static sfud_err reset(const sfud_flash *flash)
{
sfud_err result = SFUD_SUCCESS;
const sfud_spi *spi = &flash->spi;
uint8_t cmd_data[2];
SFUD_ASSERT(flash);
cmd_data[0] = SFUD_CMD_ENABLE_RESET;
result = spi->wr(spi, cmd_data, 1, NULL, 0);
if (result == SFUD_SUCCESS)
{
result = wait_busy(flash);
}
else
{
SFUD_INFO("Error: Flash device reset failed.");
return result;
}
cmd_data[1] = SFUD_CMD_RESET;
result = spi->wr(spi, &cmd_data[1], 1, NULL, 0);
if (result == SFUD_SUCCESS)
{
result = wait_busy(flash);
}
if (result == SFUD_SUCCESS)
{
SFUD_DEBUG("Flash device reset success.");
}
else
{
SFUD_INFO("Error: Flash device reset failed.");
}
return result;
}
static sfud_err read_jedec_id(sfud_flash *flash)
{
sfud_err result = SFUD_SUCCESS;
const sfud_spi *spi = &flash->spi;
uint8_t cmd_data[1], recv_data[3];
SFUD_ASSERT(flash);
cmd_data[0] = SFUD_CMD_JEDEC_ID;
result = spi->wr(spi, cmd_data, sizeof(cmd_data), recv_data, sizeof(recv_data));
if (result == SFUD_SUCCESS)
{
flash->chip.mf_id = recv_data[0];
flash->chip.type_id = recv_data[1];
flash->chip.capacity_id = recv_data[2];
SFUD_DEBUG(
"The flash device manufacturer ID is 0x%02X, memory type ID is 0x%02X, capacity ID is "
"0x%02X.",
flash->chip.mf_id, flash->chip.type_id, flash->chip.capacity_id);
}
else
{
SFUD_INFO("Error: Read flash device JEDEC ID error.");
}
return result;
}
/**
* set the flash write enable or write disable
*
* @param flash flash device
* @param enabled SFUD_TRUE: enable SFUD_FALSE: disable
*
* @return result
*/
static sfud_err set_write_enabled(const sfud_flash *flash, SFUD_BOOL enabled)
{
sfud_err result = SFUD_SUCCESS;
uint8_t cmd, register_status;
SFUD_ASSERT(flash);
if (enabled)
{
cmd = SFUD_CMD_WRITE_ENABLE;
}
else
{
cmd = SFUD_CMD_WRITE_DISABLE;
}
result = flash->spi.wr(&flash->spi, &cmd, 1, NULL, 0);
if (result == SFUD_SUCCESS)
{
result = sfud_read_status(flash, &register_status);
}
if (result == SFUD_SUCCESS)
{
if (enabled && (register_status & SFUD_STATUS_REGISTER_WEL) == 0)
{
SFUD_INFO("Error: Can't enable write status.");
return SFUD_ERR_WRITE;
}
else if (!enabled && (register_status & SFUD_STATUS_REGISTER_WEL) != 0)
{
SFUD_INFO("Error: Can't disable write status.");
return SFUD_ERR_WRITE;
}
}
return result;
}
/**
* enable or disable 4-Byte addressing for flash
*
* @note The 4-Byte addressing just supported for the flash capacity which is large then 16MB
* (256Mb).
*
* @param flash flash device
* @param enabled SFUD_TRUE: enable SFUD_FALSE: disable
*
* @return result
*/
static sfud_err set_4_byte_address_mode(sfud_flash *flash, SFUD_BOOL enabled)
{
sfud_err result = SFUD_SUCCESS;
uint8_t cmd;
SFUD_ASSERT(flash);
/* set the flash write enable */
result = set_write_enabled(flash, SFUD_TRUE);
if (result != SFUD_SUCCESS)
{
return result;
}
if (enabled)
{
cmd = SFUD_CMD_ENTER_4B_ADDRESS_MODE;
}
else
{
cmd = SFUD_CMD_EXIT_4B_ADDRESS_MODE;
}
result = flash->spi.wr(&flash->spi, &cmd, 1, NULL, 0);
if (result == SFUD_SUCCESS)
{
flash->addr_in_4_byte = enabled ? SFUD_TRUE : SFUD_FALSE;
SFUD_DEBUG("%s 4-Byte addressing mode success.", enabled ? "Enter" : "Exit");
}
else
{
SFUD_INFO("Error: %s 4-Byte addressing mode failed.", enabled ? "Enter" : "Exit");
}
return result;
}
/**
* read flash register status
*
* @param flash flash device
* @param status register status
*
* @return result
*/
sfud_err sfud_read_status(const sfud_flash *flash, uint8_t *status)
{
uint8_t cmd = SFUD_CMD_READ_STATUS_REGISTER;
SFUD_ASSERT(flash);
SFUD_ASSERT(status);
return flash->spi.wr(&flash->spi, &cmd, 1, status, 1);
}
static sfud_err wait_busy(const sfud_flash *flash)
{
sfud_err result = SFUD_SUCCESS;
uint8_t status;
size_t retry_times = flash->retry.times;
SFUD_ASSERT(flash);
while (SFUD_TRUE)
{
result = sfud_read_status(flash, &status);
if (result == SFUD_SUCCESS && ((status & SFUD_STATUS_REGISTER_BUSY)) == 0)
{
break;
}
/* retry counts */
SFUD_RETRY_PROCESS(flash->retry.delay, retry_times, result);
}
if (result != SFUD_SUCCESS || ((status & SFUD_STATUS_REGISTER_BUSY)) != 0)
{
SFUD_INFO("Error: Flash wait busy has an error. %d %d", result, status);
}
return result;
}
static void make_address_byte_array(const sfud_flash *flash, uint32_t addr, uint8_t *array)
{
uint8_t len, i;
SFUD_ASSERT(flash);
SFUD_ASSERT(array);
len = flash->addr_in_4_byte ? 4 : 3;
for (i = 0; i < len; i++)
{
array[i] = (addr >> ((len - (i + 1)) * 8)) & 0xFF;
}
}
/**
* write status register
*
* @param flash flash device
* @param is_volatile SFUD_TRUE: volatile mode, SFUD_FALSE: non-volatile mode
* @param status register status
*
* @return result
*/
sfud_err sfud_write_status(const sfud_flash *flash, SFUD_BOOL is_volatile, uint8_t status)
{
sfud_err result = SFUD_SUCCESS;
const sfud_spi *spi = &flash->spi;
uint8_t cmd_data[2];
SFUD_ASSERT(flash);
if (is_volatile)
{
cmd_data[0] = SFUD_VOLATILE_SR_WRITE_ENABLE;
result = spi->wr(spi, cmd_data, 1, NULL, 0);
}
else
{
result = set_write_enabled(flash, SFUD_TRUE);
}
if (result == SFUD_SUCCESS)
{
cmd_data[0] = SFUD_CMD_WRITE_STATUS_REGISTER;
cmd_data[1] = status;
result = spi->wr(spi, cmd_data, 2, NULL, 0);
}
if (result != SFUD_SUCCESS)
{
SFUD_INFO("Error: Write_status register failed.");
}
return result;
}

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://gitlife.ru/oschina-mirror/notrynohigh-BabyOS.git
git@gitlife.ru:oschina-mirror/notrynohigh-BabyOS.git
oschina-mirror
notrynohigh-BabyOS
notrynohigh-BabyOS
master