kopia lustrzana https://github.com/OpenRTX/OpenRTX
NVM: deeply restructured W25Qx driver
Refactored the W25Qx driver in order to make it an SPI device conformant to the nvmDevice driver interface. Removed the read/write/erase functions.pull/278/merge
rodzic
565a056e82
commit
a4db8d2242
|
@ -37,14 +37,6 @@
|
|||
#define CMD_PDWN 0xB9 /* Power down */
|
||||
#define CMD_ECHIP 0xC7 /* Full chip erase */
|
||||
|
||||
/*
|
||||
* Target-specific SPI interface functions, their implementation can be found
|
||||
* in source files "spiFlash_xxx.c"
|
||||
*/
|
||||
extern uint8_t spiFlash_SendRecv(uint8_t val);
|
||||
extern void spiFlash_init();
|
||||
extern void spiFlash_terminate();
|
||||
|
||||
static const size_t PAGE_SIZE = 256;
|
||||
static const size_t SECT_SIZE = 4096;
|
||||
|
||||
|
@ -56,7 +48,7 @@ static const size_t SECT_SIZE = 4096;
|
|||
* @param timeout: wait timeout, in ms.
|
||||
* @return zero on success, -EIO if timeout expires.
|
||||
*/
|
||||
static int waitUntilReady(uint32_t timeout)
|
||||
static int waitUntilReady(const struct W25QxCfg *cfg, uint32_t timeout)
|
||||
{
|
||||
// Each wait tick is 500us
|
||||
timeout *= 2;
|
||||
|
@ -66,13 +58,15 @@ static int waitUntilReady(uint32_t timeout)
|
|||
delayUs(500);
|
||||
timeout--;
|
||||
|
||||
gpio_clearPin(FLASH_CS);
|
||||
spiFlash_SendRecv(CMD_RDSTA);
|
||||
uint8_t status = spiFlash_SendRecv(0x00);
|
||||
gpio_setPin(FLASH_CS);
|
||||
uint8_t cmd[] = {CMD_RDSTA, 0x00};
|
||||
uint8_t ret[2];
|
||||
|
||||
gpioPin_clear(&cfg->cs);
|
||||
spi_transfer(cfg->spi, cmd, ret, 2);
|
||||
gpioPin_set(&cfg->cs);
|
||||
|
||||
/* If busy flag is low, we're done */
|
||||
if((status & 0x01) == 0)
|
||||
if((ret[1] & 0x01) == 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -80,146 +74,162 @@ static int waitUntilReady(uint32_t timeout)
|
|||
}
|
||||
|
||||
|
||||
void W25Qx_init()
|
||||
void W25Qx_init(const struct nvmDevice *dev)
|
||||
{
|
||||
gpio_setMode(FLASH_CS, OUTPUT);
|
||||
gpio_setPin(FLASH_CS);
|
||||
const struct W25QxCfg *cfg = (const struct W25QxCfg *) dev->priv;
|
||||
|
||||
spiFlash_init();
|
||||
W25Qx_wakeup();
|
||||
gpioPin_setMode(&cfg->cs, OUTPUT);
|
||||
gpioPin_set(&cfg->cs);
|
||||
|
||||
W25Qx_wakeup(dev);
|
||||
// TODO: Implement sleep to increase power saving
|
||||
}
|
||||
|
||||
void W25Qx_terminate()
|
||||
void W25Qx_terminate(const struct nvmDevice *dev)
|
||||
{
|
||||
W25Qx_sleep();
|
||||
const struct W25QxCfg *cfg = (const struct W25QxCfg *) dev->priv;
|
||||
|
||||
gpio_setMode(FLASH_CS, INPUT);
|
||||
|
||||
spiFlash_terminate();
|
||||
W25Qx_sleep(dev);
|
||||
gpioPin_setMode(&cfg->cs, INPUT);
|
||||
}
|
||||
|
||||
void W25Qx_wakeup()
|
||||
void W25Qx_wakeup(const struct nvmDevice *dev)
|
||||
{
|
||||
gpio_clearPin(FLASH_CS);
|
||||
spiFlash_SendRecv(CMD_WKUP);
|
||||
gpio_setPin(FLASH_CS);
|
||||
const struct W25QxCfg *cfg = (const struct W25QxCfg *) dev->priv;
|
||||
const uint8_t cmd = CMD_WKUP;
|
||||
|
||||
spi_acquire(cfg->spi);
|
||||
|
||||
gpioPin_clear(&cfg->cs);
|
||||
spi_send(cfg->spi, &cmd, 1);
|
||||
gpioPin_set(&cfg->cs);
|
||||
|
||||
spi_release(cfg->spi);
|
||||
}
|
||||
|
||||
void W25Qx_sleep()
|
||||
void W25Qx_sleep(const struct nvmDevice *dev)
|
||||
{
|
||||
gpio_clearPin(FLASH_CS);
|
||||
spiFlash_SendRecv(CMD_PDWN);
|
||||
gpio_setPin(FLASH_CS);
|
||||
const struct W25QxCfg *cfg = (const struct W25QxCfg *) dev->priv;
|
||||
const uint8_t cmd = CMD_PDWN;
|
||||
|
||||
spi_acquire(cfg->spi);
|
||||
|
||||
gpioPin_clear(&cfg->cs);
|
||||
spi_send(cfg->spi, &cmd, 1);
|
||||
gpioPin_set(&cfg->cs);
|
||||
|
||||
spi_release(cfg->spi);
|
||||
}
|
||||
|
||||
ssize_t W25Qx_readSecurityRegister(uint32_t addr, void* buf, size_t len)
|
||||
static int nvm_api_readSecReg(const struct nvmDevice *dev, uint32_t offset, void *data, size_t len)
|
||||
{
|
||||
uint32_t addrBase = addr & 0x3000;
|
||||
uint32_t addrRange = addr & 0xCFFF;
|
||||
if((addrBase < 0x1000) || (addrBase > 0x3000)) return -1; /* Out of base */
|
||||
if(addrRange > 0xFF) return -1; /* Out of range */
|
||||
const struct W25QxSecRegDevice *pDev = (const struct W25QxSecRegDevice *) dev;
|
||||
const struct W25QxCfg *cfg = (const struct W25QxCfg *) dev->priv;
|
||||
|
||||
/* Keep 256-byte boundary to avoid wrap-around when reading */
|
||||
// Keep 256-byte boundary to avoid wrap-around when reading
|
||||
size_t readLen = len;
|
||||
if((addrRange + len) > 0xFF)
|
||||
if((offset + len) > 0xFF)
|
||||
{
|
||||
readLen = 0xFF - (addrRange & 0xFF);
|
||||
readLen = 0xFF - (offset & 0xFF);
|
||||
}
|
||||
|
||||
gpio_clearPin(FLASH_CS);
|
||||
spiFlash_SendRecv(CMD_RSECR); /* Command */
|
||||
spiFlash_SendRecv((addr >> 16) & 0xFF); /* Address high */
|
||||
spiFlash_SendRecv((addr >> 8) & 0xFF); /* Address middle */
|
||||
spiFlash_SendRecv(addr & 0xFF); /* Address low */
|
||||
spiFlash_SendRecv(0x00); /* Dummy byte */
|
||||
|
||||
for(size_t i = 0; i < readLen; i++)
|
||||
uint32_t address = pDev->baseAddr + offset;
|
||||
const uint8_t command[] =
|
||||
{
|
||||
((uint8_t *) buf)[i] = spiFlash_SendRecv(0x00);
|
||||
}
|
||||
CMD_RSECR, // Command
|
||||
(address >> 16) & 0xFF, // Address high
|
||||
(address >> 8) & 0xFF, // Address middle
|
||||
address & 0xFF, // Address low
|
||||
0x00 // Dummy byte
|
||||
};
|
||||
|
||||
gpio_setPin(FLASH_CS);
|
||||
spi_acquire(cfg->spi);
|
||||
gpioPin_clear(&cfg->cs);
|
||||
|
||||
return ((ssize_t) readLen);
|
||||
}
|
||||
spi_send(cfg->spi, command, sizeof(command));
|
||||
spi_receive(cfg->spi, data, readLen);
|
||||
|
||||
int W25Qx_readData(uint32_t addr, void* buf, size_t len)
|
||||
{
|
||||
gpio_clearPin(FLASH_CS);
|
||||
spiFlash_SendRecv(CMD_READ); /* Command */
|
||||
spiFlash_SendRecv((addr >> 16) & 0xFF); /* Address high */
|
||||
spiFlash_SendRecv((addr >> 8) & 0xFF); /* Address middle */
|
||||
spiFlash_SendRecv(addr & 0xFF); /* Address low */
|
||||
|
||||
for(size_t i = 0; i < len; i++)
|
||||
{
|
||||
((uint8_t *) buf)[i] = spiFlash_SendRecv(0x00);
|
||||
}
|
||||
|
||||
gpio_setPin(FLASH_CS);
|
||||
gpioPin_set(&cfg->cs);
|
||||
spi_release(cfg->spi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int W25Qx_erase(uint32_t addr, size_t size)
|
||||
static int nvm_api_read(const struct nvmDevice *dev, uint32_t offset, void *data, size_t len)
|
||||
{
|
||||
const struct W25QxCfg *cfg = (const struct W25QxCfg *) dev->priv;
|
||||
|
||||
const uint8_t command[] =
|
||||
{
|
||||
CMD_READ, // Command
|
||||
(offset >> 16) & 0xFF, // Address high
|
||||
(offset >> 8) & 0xFF, // Address middle
|
||||
offset & 0xFF, // Address low
|
||||
};
|
||||
|
||||
spi_acquire(cfg->spi);
|
||||
gpioPin_clear(&cfg->cs);
|
||||
|
||||
spi_send(cfg->spi, command, sizeof(command));
|
||||
spi_receive(cfg->spi, data, len);
|
||||
|
||||
gpioPin_set(&cfg->cs);
|
||||
spi_release(cfg->spi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nvm_api_erase(const struct nvmDevice *dev, uint32_t offset, size_t size)
|
||||
{
|
||||
const struct W25QxCfg *cfg = (const struct W25QxCfg *) dev->priv;
|
||||
uint8_t command[4];
|
||||
|
||||
// Addr or size not aligned to sector size
|
||||
if(((addr % SECT_SIZE) != 0) || ((size % SECT_SIZE) != 0))
|
||||
if(((offset % SECT_SIZE) != 0) || ((size % SECT_SIZE) != 0))
|
||||
return -EINVAL;
|
||||
|
||||
gpio_clearPin(FLASH_CS);
|
||||
spiFlash_SendRecv(CMD_WREN); /* Write enable */
|
||||
gpio_setPin(FLASH_CS);
|
||||
|
||||
delayUs(5);
|
||||
spi_acquire(cfg->spi);
|
||||
|
||||
int ret = 0;
|
||||
while(size > 0)
|
||||
{
|
||||
gpio_clearPin(FLASH_CS);
|
||||
spiFlash_SendRecv(CMD_ESECT); /* Command */
|
||||
spiFlash_SendRecv((addr >> 16) & 0xFF); /* Address high */
|
||||
spiFlash_SendRecv((addr >> 8) & 0xFF); /* Address middle */
|
||||
spiFlash_SendRecv(addr & 0xFF); /* Address low */
|
||||
gpio_setPin(FLASH_CS);
|
||||
// Write enable, has to be issued for each erase operation
|
||||
command[0] = CMD_WREN;
|
||||
gpioPin_clear(&cfg->cs);
|
||||
spi_send(cfg->spi, command, 1);
|
||||
gpioPin_set(&cfg->cs);
|
||||
|
||||
ret = waitUntilReady(500);
|
||||
delayUs(5);
|
||||
|
||||
// Sector erase
|
||||
command[0] = CMD_ESECT; // Command
|
||||
command[1] = (offset >> 16) & 0xFF; // Address high
|
||||
command[2] = (offset >> 8) & 0xFF; // Address middle
|
||||
command[3] = offset & 0xFF; // Address low
|
||||
|
||||
gpioPin_clear(&cfg->cs);
|
||||
spi_send(cfg->spi, command, 4);
|
||||
gpioPin_set(&cfg->cs);
|
||||
|
||||
ret = waitUntilReady(cfg, 500);
|
||||
if(ret < 0)
|
||||
break;
|
||||
|
||||
size -= SECT_SIZE;
|
||||
addr += SECT_SIZE;
|
||||
offset += SECT_SIZE;
|
||||
}
|
||||
|
||||
spi_release(cfg->spi);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool W25Qx_eraseChip()
|
||||
static ssize_t W25Qx_writePage(const struct nvmDevice *dev, uint32_t addr,
|
||||
const void* buf, size_t len)
|
||||
{
|
||||
gpio_clearPin(FLASH_CS);
|
||||
spiFlash_SendRecv(CMD_WREN);
|
||||
gpio_setPin(FLASH_CS);
|
||||
const struct W25QxCfg *cfg = (const struct W25QxCfg *) dev->priv;
|
||||
|
||||
delayUs(5);
|
||||
|
||||
gpio_clearPin(FLASH_CS);
|
||||
spiFlash_SendRecv(CMD_ECHIP);
|
||||
gpio_setPin(FLASH_CS);
|
||||
|
||||
/*
|
||||
* Wait until erase terminates, timeout after 200s.
|
||||
*/
|
||||
int ret = waitUntilReady(200000);
|
||||
if(ret == 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ssize_t W25Qx_writePage(uint32_t addr, const void* buf, size_t len)
|
||||
{
|
||||
/* Keep page boundary to avoid wrap-around when writing */
|
||||
// Keep page boundary to avoid wrap-around when writing
|
||||
size_t addrRange = addr & (PAGE_SIZE - 1);
|
||||
size_t writeLen = len;
|
||||
if((addrRange + len) > PAGE_SIZE)
|
||||
|
@ -227,84 +237,55 @@ ssize_t W25Qx_writePage(uint32_t addr, const void* buf, size_t len)
|
|||
writeLen = PAGE_SIZE - addrRange;
|
||||
}
|
||||
|
||||
gpio_clearPin(FLASH_CS);
|
||||
spiFlash_SendRecv(CMD_WREN); /* Write enable */
|
||||
gpio_setPin(FLASH_CS);
|
||||
// Write enable bit has to be set before each page program
|
||||
uint8_t command[4];
|
||||
command[0] = CMD_WREN;
|
||||
|
||||
delayUs(5);
|
||||
spi_acquire(cfg->spi);
|
||||
gpioPin_clear(&cfg->cs);
|
||||
spi_send(cfg->spi, command, 1);
|
||||
gpioPin_set(&cfg->cs);
|
||||
|
||||
gpio_clearPin(FLASH_CS);
|
||||
spiFlash_SendRecv(CMD_WRITE); /* Command */
|
||||
spiFlash_SendRecv((addr >> 16) & 0xFF); /* Address high */
|
||||
spiFlash_SendRecv((addr >> 8) & 0xFF); /* Address middle */
|
||||
spiFlash_SendRecv(addr & 0xFF); /* Address low */
|
||||
// Page program
|
||||
command[0] = CMD_WRITE; // Command
|
||||
command[1] = (addr >> 16) & 0xFF; // Address high
|
||||
command[2] = (addr >> 8) & 0xFF; // Address middle
|
||||
command[3] = addr & 0xFF; // Address low
|
||||
|
||||
for(size_t i = 0; i < writeLen; i++)
|
||||
{
|
||||
uint8_t value = ((uint8_t *) buf)[i];
|
||||
(void) spiFlash_SendRecv(value);
|
||||
}
|
||||
gpioPin_clear(&cfg->cs);
|
||||
spi_send(cfg->spi, command, 4);
|
||||
spi_send(cfg->spi, buf, writeLen);
|
||||
gpioPin_set(&cfg->cs);
|
||||
|
||||
gpio_setPin(FLASH_CS);
|
||||
// Wait until write terminates, timeout after 500ms.
|
||||
int ret = waitUntilReady(cfg, 500);
|
||||
spi_release(cfg->spi);
|
||||
|
||||
/*
|
||||
* Wait until erase terminates, timeout after 500ms.
|
||||
*/
|
||||
int ret = waitUntilReady(500);
|
||||
if(ret < 0)
|
||||
return (ssize_t) ret;
|
||||
|
||||
return writeLen;
|
||||
}
|
||||
|
||||
int W25Qx_writeData(uint32_t addr, const void *buf, size_t len)
|
||||
{
|
||||
while(len > 0)
|
||||
{
|
||||
size_t toWrite = len;
|
||||
|
||||
// Maximum single-shot write lenght is one page
|
||||
if(toWrite >= PAGE_SIZE)
|
||||
toWrite = PAGE_SIZE;
|
||||
|
||||
ssize_t written = W25Qx_writePage(addr, buf, toWrite);
|
||||
if(written < 0)
|
||||
return (int) written;
|
||||
|
||||
len -= (size_t) written;
|
||||
buf = ((const uint8_t *) buf) + (size_t) written;
|
||||
addr += (size_t) written;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nvm_api_readSecReg(const struct nvmDevice *dev, uint32_t offset, void *data, size_t len)
|
||||
{
|
||||
(void) dev;
|
||||
|
||||
return W25Qx_readSecurityRegister(offset, data, len);
|
||||
}
|
||||
|
||||
static int nvm_api_read(const struct nvmDevice *dev, uint32_t offset, void *data, size_t len)
|
||||
{
|
||||
(void) dev;
|
||||
|
||||
return W25Qx_readData(offset, data, len);
|
||||
else
|
||||
return writeLen;
|
||||
}
|
||||
|
||||
static int nvm_api_write(const struct nvmDevice *dev, uint32_t offset, const void *data, size_t len)
|
||||
{
|
||||
(void) dev;
|
||||
while(len > 0)
|
||||
{
|
||||
// Maximum single-shot write length is one page
|
||||
size_t toWrite = len;
|
||||
if(toWrite >= PAGE_SIZE)
|
||||
toWrite = PAGE_SIZE;
|
||||
|
||||
return W25Qx_writeData(offset, data, len);
|
||||
}
|
||||
ssize_t written = W25Qx_writePage(dev, offset, data, toWrite);
|
||||
if(written < 0)
|
||||
return (int) written;
|
||||
|
||||
static int nvm_api_erase(const struct nvmDevice *dev, uint32_t offset, size_t size)
|
||||
{
|
||||
(void) dev;
|
||||
len -= (size_t) written;
|
||||
data = ((const uint8_t *) data) + (size_t) written;
|
||||
offset += (size_t) written;
|
||||
}
|
||||
|
||||
return W25Qx_erase(offset, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct nvmOps W25Qx_ops =
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/types.h>
|
||||
#include <peripherals/spi.h>
|
||||
#include <peripherals/gpio.h>
|
||||
#include <interfaces/nvmem.h>
|
||||
|
||||
/**
|
||||
|
@ -31,10 +33,19 @@
|
|||
* volatile memory on various radios to store both calibration and contact data.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Configuration data structure for W25Qx memory device.
|
||||
*/
|
||||
struct W25QxCfg
|
||||
{
|
||||
const struct spiDevice *spi;
|
||||
const struct gpioPin cs;
|
||||
};
|
||||
|
||||
/**
|
||||
* Driver data structure for W25Qx security registers.
|
||||
*/
|
||||
struct w25qSecRegDevice
|
||||
struct W25QxSecRegDevice
|
||||
{
|
||||
const void *priv; ///< Device driver private data
|
||||
const struct nvmOps *ops; ///< Device operations
|
||||
|
@ -55,12 +66,13 @@ extern const struct nvmInfo W25Qx_info;
|
|||
* @param name: device name.
|
||||
* @param sz: memory size, in bytes.
|
||||
*/
|
||||
#define W25Qx_DEVICE_DEFINE(name, sz) \
|
||||
struct nvmDevice name = \
|
||||
{ \
|
||||
.ops = &W25Qx_ops, \
|
||||
.info = &W25Qx_info, \
|
||||
.size = sz \
|
||||
#define W25Qx_DEVICE_DEFINE(name, config, sz) \
|
||||
const struct nvmDevice name = \
|
||||
{ \
|
||||
.priv = &config, \
|
||||
.ops = &W25Qx_ops, \
|
||||
.info = &W25Qx_info, \
|
||||
.size = sz, \
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -76,24 +88,25 @@ extern const struct nvmInfo W25Qx_secReg_info;
|
|||
* @param base: security register base address.
|
||||
* @param sz: memory size, in bytes.
|
||||
*/
|
||||
#define W25Qx_SECREG_DEFINE(name, base, sz) \
|
||||
struct w25qSecRegDevice name = \
|
||||
{ \
|
||||
.ops = &W25Qx_secReg_ops, \
|
||||
.info = &W25Qx_secReg_info, \
|
||||
.size = sz, \
|
||||
.baseAddr = base \
|
||||
#define W25Qx_SECREG_DEFINE(name, config, base, sz) \
|
||||
const struct W25QxSecRegDevice name = \
|
||||
{ \
|
||||
.priv = &config, \
|
||||
.ops = &W25Qx_secReg_ops, \
|
||||
.info = &W25Qx_secReg_info, \
|
||||
.size = sz, \
|
||||
.baseAddr = base \
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialise driver for external flash.
|
||||
*/
|
||||
void W25Qx_init();
|
||||
void W25Qx_init(const struct nvmDevice *dev);
|
||||
|
||||
/**
|
||||
* Terminate driver for external flash.
|
||||
*/
|
||||
void W25Qx_terminate();
|
||||
void W25Qx_terminate(const struct nvmDevice *dev);
|
||||
|
||||
/**
|
||||
* Release flash chip from power down mode, this function should be called at
|
||||
|
@ -102,76 +115,11 @@ void W25Qx_terminate();
|
|||
* Application code must wait at least 3us before issuing any other command
|
||||
* after this one.
|
||||
*/
|
||||
void W25Qx_wakeup();
|
||||
void W25Qx_wakeup(const struct nvmDevice *dev);
|
||||
|
||||
/**
|
||||
* Put flash chip in low power mode.
|
||||
*/
|
||||
void W25Qx_sleep();
|
||||
|
||||
/**
|
||||
* Read data from one of the flash security registers, located at addresses
|
||||
* 0x1000, 0x2000 and 0x3000 and 256-byte wide.
|
||||
* NOTE: If a read operation goes beyond the 256 byte boundary, length will be
|
||||
* truncated to the one reaching the end of the register.
|
||||
*
|
||||
* @param addr: start address for read operation, must be the full register address.
|
||||
* @param buf: pointer to a buffer where data is written to.
|
||||
* @param len: number of bytes to read.
|
||||
* @return: -1 if address is not whithin security registers address range, the
|
||||
* number of bytes effectively read otherwise.
|
||||
*/
|
||||
ssize_t W25Qx_readSecurityRegister(uint32_t addr, void *buf, size_t len);
|
||||
|
||||
/**
|
||||
* Read data from flash memory.
|
||||
*
|
||||
* @param addr: start address for read operation.
|
||||
* @param buf: pointer to a buffer where data is written to.
|
||||
* @param len: number of bytes to read.
|
||||
* @return zero on success, negative errno code on fail.
|
||||
*/
|
||||
int W25Qx_readData(uint32_t addr, void *buf, size_t len);
|
||||
|
||||
/**
|
||||
* Erase a flash memory area.
|
||||
* The start address and erase size must be aligned to page size. The function
|
||||
* blocks until the erase process terminates.
|
||||
*
|
||||
* @param addr: start address.
|
||||
* @param size: size of the area to be erased.
|
||||
* @return zero on success, negative errno code on fail.
|
||||
*/
|
||||
int W25Qx_erase(uint32_t addr, size_t size);
|
||||
|
||||
/**
|
||||
* Full chip erase.
|
||||
* Function returns when erase process terminated.
|
||||
*
|
||||
* @return true on success, false on failure.
|
||||
*/
|
||||
bool W25Qx_eraseChip();
|
||||
|
||||
/**
|
||||
* Write data to a 256-byte flash memory page.
|
||||
* NOTE: if data size goes beyond the 256 byte boundary, length will be truncated
|
||||
* to the one reaching the end of the page.
|
||||
*
|
||||
* @param addr: start address for write operation.
|
||||
* @param buf: pointer to data buffer.
|
||||
* @param len: number of bytes to written.
|
||||
* @return: -1 on error, the number of bytes effectively written otherwise.
|
||||
*/
|
||||
ssize_t W25Qx_writePage(uint32_t addr, const void *buf, size_t len);
|
||||
|
||||
/**
|
||||
* Write data to flash memory.
|
||||
*
|
||||
* @param addr: start address for read operation.
|
||||
* @param buf: pointer to a buffer where data is written to.
|
||||
* @param len: number of bytes to read.
|
||||
* @return zero on success, negative errno code on fail.
|
||||
*/
|
||||
int W25Qx_writeData(uint32_t addr, const void *buf, size_t len);
|
||||
void W25Qx_sleep(const struct nvmDevice *dev);
|
||||
|
||||
#endif /* W25Qx_H */
|
||||
|
|
Ładowanie…
Reference in New Issue