diff --git a/openrtx/include/core/nvmem_access.h b/openrtx/include/core/nvmem_access.h index 006cf5a2..614ea949 100644 --- a/openrtx/include/core/nvmem_access.h +++ b/openrtx/include/core/nvmem_access.h @@ -25,16 +25,65 @@ #include #include +#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 address: start address for the read operation. + * @param dev: NVM device number. + * @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 len: number of bytes to read. * @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 @@ -42,98 +91,51 @@ int nvmArea_read(const struct nvmArea *area, uint32_t address, void *data, size_ * the write. * * @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 len: number of bytes to write. * @return zero on success, a negative error code otherwise. */ -int nvmArea_write(const struct nvmArea *area, uint32_t address, const void *data, - size_t len); +static inline int nvm_devWrite(const struct nvmDevice *dev, uint32_t offset, + 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 - * depend on the NVM device the area belongs to. + * Perform an erase operation on an NVM area. Acceptable offset and size depend + * on the NVM device the area belongs to. * * @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. * @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. - * @return pointer to the device parameters' data structure. + * @param dev: pointer to NVM device descriptor. + * @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); } -/** - * 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); +#ifdef __cplusplus } +#endif #endif diff --git a/openrtx/include/interfaces/nvmem.h b/openrtx/include/interfaces/nvmem.h index e37ea4ea..3ac23a9e 100644 --- a/openrtx/include/interfaces/nvmem.h +++ b/openrtx/include/interfaces/nvmem.h @@ -41,23 +41,33 @@ enum nvmType }; /** - * Nonvolatile memory device parameters. The content of this data structure is - * filled by the device driver and then kept constant. + * Enumeration field for nonvolatile memory properties. */ -struct nvmParams +enum nvmProperties { - 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 - uint8_t type; ///< Device type + NVM_WRITE = 0x100, ///< Device allows write access + NVM_BITWRITE = 0x200, ///< Device allows to change the value of single bits + NVM_ERASE = 0x400, ///< Device memory needs to be erased before writing +}; + +/** + * 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; /** - * Standard API for nonvolatile memory driver. + * Nonvolatile memory device driver. */ -struct nvmApi +struct nvmOps { /** * Read data from nonvolatile memory device. @@ -104,29 +114,19 @@ struct nvmApi * @return 0 on success, negative errno code on fail. */ 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 { - const struct nvmApi *api; ///< Driver API - const void *config; ///< Driver configuration data - void *const priv; ///< Driver runtime data + const void *priv; ///< Device driver private data + const struct nvmOps *ops; ///< Device operations + 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 - * partition is referred to the beginning of the area itself. + * Data structure representing a partition of a nonvolatile memory. The offset + * of the partition is referred to the beginning of the memory area. */ 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 * given device and with zero or more partition. */ -struct nvmArea +struct nvmDescriptor { - const char *name; ///< Area name - const struct nvmDevice *dev; ///< Device driver to manage the area - const size_t startAddr; ///< Start address of the area from the beginning of the device - const size_t size; ///< Size of the area, in bytes - const struct nvmPartition *partitions; ///< List of partitions + const char *name; ///< Name + const struct nvmDevice *dev; ///< Associated device driver + const size_t partNum; ///< Number of partitions + const struct nvmPartition *partitions; ///< Partion table }; @@ -160,12 +159,13 @@ void nvm_init(); 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. - * @return number of elements in the list. + * @param index: index of the nonvolatile memory. + * @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. diff --git a/openrtx/src/core/nvmem_access.c b/openrtx/src/core/nvmem_access.c index 9ede2435..c8b31f18 100644 --- a/openrtx/src/core/nvmem_access.c +++ b/openrtx/src/core/nvmem_access.c @@ -19,6 +19,7 @@ ***************************************************************************/ #include +#include #include #include #include @@ -26,60 +27,99 @@ /** * \internal - * Check if a read/write/erase operation is within the bounds of a given NVM - * area. + * Compute the absolute offset from the beginning of an NVM device, given the + * 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 addr: start address of the read/write/erase operation - * @param len: size of the read/write/erase operation - * @return true if the operation is within the NVM area bounds + * @param nvm: pointer to NVM descriptor. + * @param part: partition number. + * @param offset: pointer to the offset. + * @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) - && ((addr + len) < (area->startAddr + area->size)); -} + const struct nvmPartition *np; + // 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) -{ - const struct nvmDevice *dev = area->dev; + np = &nvm->partitions[part]; - if(checkBounds(area, address, len) == false) - return -EINVAL; + if((*offset + len) > np->size) + return -EINVAL; - return dev->api->read(dev, address, data, len); -} - -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); + *offset += np->offset; + } 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; - 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 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); }