Refactored NVM data structures and functions

This commit is contained in:
Silvano Seva 2024-04-03 21:11:12 +02:00
parent 031eda1d7e
commit 5b9cc789b9
3 changed files with 195 additions and 153 deletions

View File

@ -25,16 +25,65 @@
#include <stdint.h> #include <stdint.h>
#include <errno.h> #include <errno.h>
#ifdef __cplusplus
extern "C" {
#endif
/** /**
* Perform a byte-aligned read operation on an NVM area. * Perform a byte-aligned read operation on a nonvolatile memory.
* *
* @param area: pointer to the NVM are descriptor. * @param dev: NVM device number.
* @param address: start address for the read operation. * @param part: partition number, -1 for direct device access.
* @param address: offset for the read operation.
* @param data: pointer to a buffer where to store the data read. * @param data: pointer to a buffer where to store the data read.
* @param len: number of bytes to read. * @param len: number of bytes to read.
* @return zero on success, a negative error code otherwise. * @return zero on success, a negative error code otherwise.
*/ */
int nvmArea_read(const struct nvmArea *area, uint32_t address, void *data, size_t len); int nvm_read(const uint32_t dev, const int part, uint32_t offset, void *data,
size_t len);
/**
* Perform a write operation on a nonvolatile memory.
*
* @param dev: NVM device number.
* @param part: partition number, -1 for direct device access.
* @param offset: offset for the write operation.
* @param data: pointer to a buffer containing the data to write.
* @param len: number of bytes to write.
* @return zero on success, a negative error code otherwise.
*/
int nvm_write(const uint32_t dev, const int part, uint32_t offset, const void *data,
size_t len);
/**
* Perform an erase operation on a nonvolatile memory. Acceptable offset and
* size depend on characteristics of the underlying device.
*
* @param dev: NVM device number.
* @param part: partition number, -1 for direct device access.
* @param offset: offset for the erase operation.
* @param size: size of the area to be erased.
* @return zero on success, a negative error code otherwise.
*/
int nvm_erase(const uint32_t dev, const int part, uint32_t offset, size_t size);
/**
* Perform a byte-aligned read operation on an NVM area.
*
* @param area: pointer to the NVM are descriptor.
* @param offset: offset for the read operation.
* @param data: pointer to a buffer where to store the data read.
* @param len: number of bytes to read.
* @return zero on success, a negative error code otherwise.
*/
static inline int nvm_devRead(const struct nvmDevice *dev, uint32_t offset,
void *data, size_t len)
{
if((offset + len) > dev->size)
return -EINVAL;
return dev->ops->read(dev, offset, data, len);
}
/** /**
* Perform a byte-aligned write operation on an NVM area. If the underlying * Perform a byte-aligned write operation on an NVM area. If the underlying
@ -42,98 +91,51 @@ int nvmArea_read(const struct nvmArea *area, uint32_t address, void *data, size_
* the write. * the write.
* *
* @param area: pointer to the NVM are descriptor. * @param area: pointer to the NVM are descriptor.
* @param address: start address for the write operation. * @param offset: offset for the write operation.
* @param data: pointer to a buffer containing the data to write. * @param data: pointer to a buffer containing the data to write.
* @param len: number of bytes to write. * @param len: number of bytes to write.
* @return zero on success, a negative error code otherwise. * @return zero on success, a negative error code otherwise.
*/ */
int nvmArea_write(const struct nvmArea *area, uint32_t address, const void *data, static inline int nvm_devWrite(const struct nvmDevice *dev, uint32_t offset,
size_t len); const void *data, size_t len)
{
if(dev->ops->write == NULL)
return -ENOTSUP;
if((offset + len) > dev->size)
return -EINVAL;
return dev->ops->write(dev, offset, data, len);
}
/** /**
* Perform an erase operation on an NVM area. Acceptable start address and size * Perform an erase operation on an NVM area. Acceptable offset and size depend
* depend on the NVM device the area belongs to. * on the NVM device the area belongs to.
* *
* @param area: pointer to the NVM are descriptor. * @param area: pointer to the NVM are descriptor.
* @param address: start address for the erase operation. * @param offset: offset for the erase operation.
* @param size: size of the area to be erased. * @param size: size of the area to be erased.
* @return zero on success, a negative error code otherwise. * @return zero on success, a negative error code otherwise.
*/ */
int nvmArea_erase(const struct nvmArea *area, uint32_t address, size_t size); int nvm_devErase(const struct nvmDevice *dev, uint32_t offset, size_t size);
/** /**
* Get the parameters of the NVM device associated to a memory area. * Sync device cache and state to its underlying hardware.
* If the device does not support sync this function pointer is set to NULL.
* *
* @param area: pointer to the NVM are descriptor. * @param dev: pointer to NVM device descriptor.
* @return pointer to the device parameters' data structure. * @return 0 on success, negative errno code on fail.
*/ */
static inline const struct nvmParams *nvmArea_params(const struct nvmArea *area) static inline int nvm_devSync(const struct nvmDevice *dev)
{ {
const struct nvmDevice *dev = area->dev; if(dev->ops->sync == NULL)
return -ENOTSUP;
return dev->api->params(dev); return dev->ops->sync(dev);
} }
/** #ifdef __cplusplus
* Perform a byte-aligned read operation on an NVM area partition.
*
* @param area: pointer to the NVM are descriptor.
* @param pNum: partition number.
* @param address: start address for the read operation.
* @param data: pointer to a buffer where to store the data read.
* @param len: number of bytes to read.
* @return zero on success, a negative error code otherwise.
*/
static inline int nvmArea_readPartition(const struct nvmArea *area,
const uint32_t pNum, uint32_t offset,
void *data, size_t len)
{
const struct nvmPartition *partition = &(area->partitions[pNum]);
const size_t startAddr = area->startAddr + partition->offset + offset;
return nvmArea_read(area, startAddr, data, len);
}
/**
* Perform a byte-aligned write operation on an NVM area partition. If the
* underlying device requires state syncing, a sync operation is performed at
* the end of the write.
*
* @param area: pointer to the NVM are descriptor.
* @param pNum: partition number.
* @param address: start address for the write operation.
* @param data: pointer to a buffer containing the data to write.
* @param len: number of bytes to write.
* @return zero on success, a negative error code otherwise.
*/
static inline int nvmArea_writePartition(const struct nvmArea *area,
const uint32_t pNum, uint32_t offset,
const void *data, size_t len)
{
const struct nvmPartition *partition = &(area->partitions[pNum]);
const size_t startAddr = area->startAddr + partition->offset + offset;
return nvmArea_write(area, startAddr, data, len);
}
/**
* Perform an erase operation on an NVM area partition. Acceptable start address
* and size depend on the NVM device the area belongs to.
*
* @param area: pointer to the NVM are descriptor.
* @param pNum: partition number.
* @param address: start address for the erase operation.
* @param size: size of the area to be erased.
* @return zero on success, a negative error code otherwise.
*/
static inline int nvmArea_erasePartition(const struct nvmArea *area,
const uint32_t pNum, uint32_t offset,
size_t size)
{
const struct nvmPartition *partition = &(area->partitions[pNum]);
const size_t startAddr = area->startAddr + partition->offset + offset;
return nvmArea_erase(area, startAddr, size);
} }
#endif
#endif #endif

View File

@ -41,23 +41,33 @@ enum nvmType
}; };
/** /**
* Nonvolatile memory device parameters. The content of this data structure is * Enumeration field for nonvolatile memory properties.
* filled by the device driver and then kept constant.
*/ */
struct nvmParams enum nvmProperties
{ {
size_t write_size; ///< Minimum write size (write unit) NVM_WRITE = 0x100, ///< Device allows write access
size_t erase_size; ///< Minimum erase size (erase unit) NVM_BITWRITE = 0x200, ///< Device allows to change the value of single bits
size_t erase_cycles; ///< Maximum allowed erase cycles of a block NVM_ERASE = 0x400, ///< Device memory needs to be erased before writing
uint8_t type; ///< Device type };
/**
* Nonvolatile memory device information block. The content of this data structure
* is defined by the device driver and remains constant.
*/
struct nvmInfo
{
size_t write_size; ///< Minimum write size (write unit)
size_t erase_size; ///< Minimum erase size (erase unit)
size_t erase_cycles; ///< Maximum allowed erase cycles of a block
uint32_t device_info; ///< Device type and flags
}; };
struct nvmDevice; struct nvmDevice;
/** /**
* Standard API for nonvolatile memory driver. * Nonvolatile memory device driver.
*/ */
struct nvmApi struct nvmOps
{ {
/** /**
* Read data from nonvolatile memory device. * Read data from nonvolatile memory device.
@ -104,29 +114,19 @@ struct nvmApi
* @return 0 on success, negative errno code on fail. * @return 0 on success, negative errno code on fail.
*/ */
int (*sync)(const struct nvmDevice *dev); int (*sync)(const struct nvmDevice *dev);
/**
* Get device parameters.
*
* @param dev: pointer to NVM device descriptor.
* @return pointer to the device parameters' data structure.
*/
const struct nvmParams *(*params)(const struct nvmDevice *dev);
}; };
/**
* Nonvolatile memory device driver.
*/
struct nvmDevice struct nvmDevice
{ {
const struct nvmApi *api; ///< Driver API const void *priv; ///< Device driver private data
const void *config; ///< Driver configuration data const struct nvmOps *ops; ///< Device operations
void *const priv; ///< Driver runtime data const struct nvmInfo *info; ///< Device info
const size_t size; ///< Device size
}; };
/** /**
* Data structure representing a partition of a NVM area. The offset of the * Data structure representing a partition of a nonvolatile memory. The offset
* partition is referred to the beginning of the area itself. * of the partition is referred to the beginning of the memory area.
*/ */
struct nvmPartition struct nvmPartition
{ {
@ -135,17 +135,16 @@ struct nvmPartition
}; };
/** /**
* Nonvolatile memory area descriptor. This data structure contains all the data * Nonvolatile memory descriptor. This data structure contains all the data
* relative to an area of nonvolatile memory with a fixed size, managed by a * relative to an area of nonvolatile memory with a fixed size, managed by a
* given device and with zero or more partition. * given device and with zero or more partition.
*/ */
struct nvmArea struct nvmDescriptor
{ {
const char *name; ///< Area name const char *name; ///< Name
const struct nvmDevice *dev; ///< Device driver to manage the area const struct nvmDevice *dev; ///< Associated device driver
const size_t startAddr; ///< Start address of the area from the beginning of the device const size_t partNum; ///< Number of partitions
const size_t size; ///< Size of the area, in bytes const struct nvmPartition *partitions; ///< Partion table
const struct nvmPartition *partitions; ///< List of partitions
}; };
@ -160,12 +159,13 @@ void nvm_init();
void nvm_terminate(); void nvm_terminate();
/** /**
* Get a list of the available nonvolatile memory areas of the device. * Obtain the descriptor of a given nonvolatile memory.
* *
* @param list: pointer where to store the pointer to the list head. * @param index: index of the nonvolatile memory.
* @return number of elements in the list. * @return a pointer to the memory descriptor or NULL if the requested descriptor
* does not exist.
*/ */
size_t nvm_getMemoryAreas(const struct nvmArea **list); const struct nvmDescriptor *nvm_getDesc(const size_t index);
/** /**
* Load calibration data from nonvolatile memory. * Load calibration data from nonvolatile memory.

View File

@ -19,6 +19,7 @@
***************************************************************************/ ***************************************************************************/
#include <interfaces/nvmem.h> #include <interfaces/nvmem.h>
#include <nvmem_access.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <errno.h> #include <errno.h>
@ -26,60 +27,99 @@
/** /**
* \internal * \internal
* Check if a read/write/erase operation is within the bounds of a given NVM * Compute the absolute offset from the beginning of an NVM device, given the
* area. * device descriptor and the partition number. If the partition number is set
* to -1, the offset is considered from the beginning of the device.
* This function performs also a bound check to guarantee that the following
* operation stays within the limits of the partition (if any).
* *
* @param area: pointer to the NVM area descriptor * @param nvm: pointer to NVM descriptor.
* @param addr: start address of the read/write/erase operation * @param part: partition number.
* @param len: size of the read/write/erase operation * @param offset: pointer to the offset.
* @return true if the operation is within the NVM area bounds * @param len: lenght of the read/write/erase operation.
* @return a negative error code or zero.
*/ */
static inline bool checkBounds(const struct nvmArea *area, uint32_t addr, size_t len) static inline int getAbsOffset(const struct nvmDescriptor *nvm, const int part,
uint32_t *offset, size_t len)
{ {
return (addr >= area->startAddr) const struct nvmPartition *np;
&& ((addr + len) < (area->startAddr + area->size));
}
// Offset is relative to a partition
if(part >= 0)
{
if((size_t) part >= nvm->partNum)
return -EINVAL;
int nvmArea_read(const struct nvmArea *area, uint32_t address, void *data, size_t len) np = &nvm->partitions[part];
{
const struct nvmDevice *dev = area->dev;
if(checkBounds(area, address, len) == false) if((*offset + len) > np->size)
return -EINVAL; return -EINVAL;
return dev->api->read(dev, address, data, len); *offset += np->offset;
} }
int nvmArea_write(const struct nvmArea *area, uint32_t address, void *data, size_t len)
{
const struct nvmDevice *dev = area->dev;
if(checkBounds(area, address, len) == false)
return -EINVAL;
if(dev->api->write == NULL)
return -ENOTSUP;
int ret = dev->api->write(dev, address, data, len);
if(ret < 0)
return ret;
if(dev->api->sync != NULL)
dev->api->sync(dev);
return 0; return 0;
} }
int nvmArea_erase(const struct nvmArea *area, uint32_t address, size_t size)
{
const struct nvmDevice *dev = area->dev;
if(checkBounds(area, address, size) == false) int nvm_read(const uint32_t dev, const int part, uint32_t offset, void *data,
size_t len)
{
const struct nvmDescriptor *nvm = nvm_getDesc(dev);
if(nvm == NULL)
return -EINVAL; return -EINVAL;
if(dev->api->erase == NULL) int ret = getAbsOffset(nvm, part, &offset, len);
if(ret < 0)
return ret;
return nvm_devRead(nvm->dev, offset, data, len);
}
int nvm_write(const uint32_t dev, const int part, uint32_t offset, const void *data,
size_t len)
{
const struct nvmDescriptor *nvm = nvm_getDesc(dev);
if(nvm == NULL)
return -EINVAL;
int ret = getAbsOffset(nvm, part, &offset, len);
if(ret < 0)
return ret;
return nvm_devWrite(nvm->dev, offset, data, len);
}
int nvm_erase(const uint32_t dev, const int part, uint32_t offset, size_t size)
{
const struct nvmDescriptor *nvm = nvm_getDesc(dev);
if(nvm == NULL)
return -EINVAL;
int ret = getAbsOffset(nvm, part, &offset, size);
if(ret < 0)
return ret;
return nvm_devErase(nvm->dev, offset, size);
}
int nvm_devErase(const struct nvmDevice *dev, uint32_t offset, size_t size)
{
// Erase operation is allowed
if(dev->ops->erase == NULL)
return -ENOTSUP; return -ENOTSUP;
return dev->api->erase(dev, address, size); // Out-of-bounds check
if((offset + size) > dev->size)
return -EINVAL;
// Start offset must be aligned to the erase size
if((offset % dev->info->erase_size) != 0)
return -EINVAL;
// Total size must be aligned to the erase size
if((size % dev->info->erase_size) != 0)
return -EINVAL;
return dev->ops->erase(dev, offset, size);
} }