1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

mtd: add module for remapping MTD sectors

This commit is contained in:
Koen Zandberg 2020-01-25 20:14:21 +01:00
parent cf5356df24
commit 9a430cd682
No known key found for this signature in database
GPG Key ID: 0E63411F8FCA8247
3 changed files with 243 additions and 0 deletions

View File

@ -0,0 +1,112 @@
/*
* Copyright (C) 2020 Koen Zandberg <koen@bergzand.net>
*
* 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 = &region.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 <koen@bergzand.net>
*/
#ifndef MTD_MAPPER_H
#define MTD_MAPPER_H
#include <stdint.h>
#include <stdbool.h>
#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 */
/** @} */

View File

@ -0,0 +1 @@
include $(RIOTBASE)/Makefile.base

View File

@ -0,0 +1,130 @@
/*
* Copyright (C) 2020 Koen Zandberg <koen@bergzand.net>
*
* 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 <koen@bergzand.net>
*
* @}
*/
#include <stdint.h>
#include <errno.h>
#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(&region->parent->lock);
}
static void _lock(mtd_mapper_region_t *region)
{
mutex_lock(&region->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,
};