From 9a430cd68245ada20e84c71271de8963ccc71e2f Mon Sep 17 00:00:00 2001 From: Koen Zandberg Date: Sat, 25 Jan 2020 20:14:21 +0100 Subject: [PATCH] mtd: add module for remapping MTD sectors --- drivers/include/mtd_mapper.h | 112 +++++++++++++++++++++++++++ drivers/mtd_mapper/Makefile | 1 + drivers/mtd_mapper/mtd_mapper.c | 130 ++++++++++++++++++++++++++++++++ 3 files changed, 243 insertions(+) create mode 100644 drivers/include/mtd_mapper.h create mode 100644 drivers/mtd_mapper/Makefile create mode 100644 drivers/mtd_mapper/mtd_mapper.c diff --git a/drivers/include/mtd_mapper.h b/drivers/include/mtd_mapper.h new file mode 100644 index 0000000000..e57d65fb31 --- /dev/null +++ b/drivers/include/mtd_mapper.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2020 Koen Zandberg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup drivers_mtd_mapper MTD address mapper + * @ingroup drivers_storage + * @brief Driver for address remap for flash devices + * + * This MTD module allows for remapping multiple different regions on a single + * MTD device and present them as separate MTD devices. This is similar to + * partitions on a hard drive, although this system only allows hardcoded + * partitions and lacks a partition table. + * + * The use case for this module is to be able to split a single MTD device, for + * example a SPI NOR flash chip into multiple separate regions which all can + * contain their own content or file systems. + * + * ## Usage + * + * To use this module include it in your makefile: + * + * ``` + * USEMODULE += mtd_mapper + * ``` + * + * To define new regions with an existing MTD device the following is required: + * + * ``` + * mtd_mapper_parent_t parent = MTD_PARENT_INIT(MTD_0); + * + * mtd_mapper_region_t region1 = { + * .mtd = { + * .driver = &mtd_mapper_driver, + * .sector_count = SECTOR_COUNT / 2, + * .pages_per_sector = PAGE_PER_SECTOR, + * .page_size = PAGE_SIZE, + * }, + * .parent = &parent, + * .offset = PAGE_PER_SECTOR * PAGE_SIZE * SECTOR_COUNT / 2 + * }; + * + * mtd_dev_t *dev = ®ion.mtd; + * ``` + * The snippet here defines a region within an existing `MTD_0` device of half + * the size of `MTD_0` and starting in the middle of the device. + * + * @warning Please ensure that the different configured regions do not overlap. + * + * @{ + * + * @brief Interface definitions for mtd mapper support + * + * @author Koen Zandberg + */ + +#ifndef MTD_MAPPER_H +#define MTD_MAPPER_H + +#include +#include +#include "mtd.h" +#include "mutex.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Shortcut macro for initializing the members of an + * @ref mtd_mapper_parent_t struct + */ +#define MTD_PARENT_INIT(_parent) \ +{ \ + .mtd = _parent, \ + .lock = MUTEX_INIT, \ + .init = false, \ +} + +/** + * @brief MTD mapper backing device context + */ +typedef struct { + mtd_dev_t *mtd; /**< Parent MTD device */ + mutex_t lock; /**< Mutex for guarding the backing device access */ + bool init; /**< Initialization flag */ +} mtd_mapper_parent_t; + +/** + * @brief MTD mapped region + */ +typedef struct { + mtd_dev_t mtd; /**< MTD context */ + mtd_mapper_parent_t *parent; /**< MTD mapper parent device */ + uint32_t offset; /**< Offset address to start this region */ +} mtd_mapper_region_t; + +/** + * @brief Mapper MTD device operations table + */ +extern const mtd_desc_t mtd_mapper_driver; + +#ifdef __cplusplus +} +#endif + +#endif /* MTD_MAPPER_H */ +/** @} */ diff --git a/drivers/mtd_mapper/Makefile b/drivers/mtd_mapper/Makefile new file mode 100644 index 0000000000..48422e909a --- /dev/null +++ b/drivers/mtd_mapper/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/drivers/mtd_mapper/mtd_mapper.c b/drivers/mtd_mapper/mtd_mapper.c new file mode 100644 index 0000000000..b8039d7a24 --- /dev/null +++ b/drivers/mtd_mapper/mtd_mapper.c @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2020 Koen Zandberg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup drivers_mtd_mapper + * @{ + * + * @file + * @brief Driver for flash memory remap support + * + * @author Koen Zandberg + * + * @} + */ + +#include +#include + +#include "kernel_defines.h" +#include "mtd.h" +#include "mtd_mapper.h" +#include "mutex.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +static void _unlock(mtd_mapper_region_t *region) +{ + mutex_unlock(®ion->parent->lock); +} + +static void _lock(mtd_mapper_region_t *region) +{ + mutex_lock(®ion->parent->lock); +} + +static uint32_t _region_size(mtd_mapper_region_t *region) +{ + return region->mtd.page_size * region->mtd.pages_per_sector * + region->mtd.sector_count; +} + +static int _init_target(mtd_mapper_region_t *region) +{ + mtd_mapper_parent_t *parent = region->parent; + + if (!parent->init) { + parent->init = true; + return mtd_init(parent->mtd); + } + return 0; +} + +static int _init(mtd_dev_t *mtd) +{ + + mtd_mapper_region_t *region = container_of(mtd, mtd_mapper_region_t, mtd); + mtd_dev_t *backing_mtd = region->parent->mtd; + + /* Configuration sanity checks */ + assert(backing_mtd->page_size == region->mtd.page_size); + assert(backing_mtd->pages_per_sector == region->mtd.pages_per_sector); + assert(backing_mtd->sector_count >= region->mtd.sector_count); + + /* offset + region size must not exceed the backing device */ + assert(region->offset + _region_size( + region) <= + backing_mtd->pages_per_sector * backing_mtd->sector_count * + backing_mtd->page_size); + + _lock(region); + int res = _init_target(region); + _unlock(region); + return res; +} + +static int _write(mtd_dev_t *mtd, const void *src, uint32_t addr, + uint32_t count) +{ + mtd_mapper_region_t *region = container_of(mtd, mtd_mapper_region_t, mtd); + + if (addr + count > _region_size(region)) { + return -EOVERFLOW; + } + + _lock(region); + int res = mtd_write(region->parent->mtd, src, addr + region->offset, count); + _unlock(region); + return res; +} + +static int _read(mtd_dev_t *mtd, void *dest, uint32_t addr, uint32_t count) +{ + mtd_mapper_region_t *region = container_of(mtd, mtd_mapper_region_t, mtd); + + if (addr + count > _region_size(region)) { + return -EOVERFLOW; + } + + _lock(region); + int res = mtd_read(region->parent->mtd, dest, addr + region->offset, count); + _unlock(region); + return res; +} + +static int _erase(mtd_dev_t *mtd, uint32_t addr, uint32_t count) +{ + mtd_mapper_region_t *region = container_of(mtd, mtd_mapper_region_t, mtd); + + if (addr + count > _region_size(region)) { + return -EOVERFLOW; + } + + _lock(region); + int res = mtd_erase(region->parent->mtd, addr + region->offset, count); + _unlock(region); + return res; +} + +const mtd_desc_t mtd_mapper_driver = { + .init = _init, + .read = _read, + .write = _write, + .erase = _erase, +};