diff options
author | Lionel Debieve <lionel.debieve@st.com> | 2019-09-09 20:13:34 +0200 |
---|---|---|
committer | Lionel Debieve <lionel.debieve@st.com> | 2020-01-20 11:32:59 +0100 |
commit | b114abb609d42a5e237a35f6e27852c9aa9ab963 (patch) | |
tree | 53856e8f52dede41db4301d30deb036fbcbc8f31 /drivers/io | |
parent | 45cc606ea7bbf8931be715a51c1819e7dd22e936 (diff) | |
download | platform_external_arm-trusted-firmware-b114abb609d42a5e237a35f6e27852c9aa9ab963.tar.gz platform_external_arm-trusted-firmware-b114abb609d42a5e237a35f6e27852c9aa9ab963.tar.bz2 platform_external_arm-trusted-firmware-b114abb609d42a5e237a35f6e27852c9aa9ab963.zip |
Add raw NAND framework
The raw NAND framework supports SLC NAND devices.
It introduces a new high level interface (io_mtd) that
defines operations a driver can register to the NAND framework.
This interface will fill in the io_mtd device specification:
- device_size
- erase_size
that could be used by the io_storage interface.
NAND core source file integrates the standard read loop that
performs NAND device read operations using a skip bad block strategy.
A platform buffer must be defined in case of unaligned
data. This buffer must fit to the maximum device page size
defined by PLATFORM_MTD_MAX_PAGE_SIZE.
The raw_nand.c source file embeds the specific NAND operations
to read data.
The read command is a raw page read without any ECC correction.
This can be overridden by a low level driver.
No generic support for write or erase command or software
ECC correction.
NAND ONFI detection is available and can be enabled using
NAND_ONFI_DETECT=1.
For non-ONFI NAND management, platform can define required
information.
Change-Id: Id80e9864456cf47f02b74938cf25d99261da8e82
Signed-off-by: Lionel Debieve <lionel.debieve@st.com>
Signed-off-by: Christophe Kerello <christophe.kerello@st.com>
Diffstat (limited to 'drivers/io')
-rw-r--r-- | drivers/io/io_mtd.c | 248 |
1 files changed, 248 insertions, 0 deletions
diff --git a/drivers/io/io_mtd.c b/drivers/io/io_mtd.c new file mode 100644 index 000000000..7575fa250 --- /dev/null +++ b/drivers/io/io_mtd.c @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2019, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <errno.h> +#include <string.h> + +#include <platform_def.h> + +#include <common/debug.h> +#include <drivers/io/io_driver.h> +#include <drivers/io/io_mtd.h> +#include <lib/utils.h> + +typedef struct { + io_mtd_dev_spec_t *dev_spec; + uintptr_t base; + unsigned long long offset; /* Offset in bytes */ + unsigned long long size; /* Size of device in bytes */ +} mtd_dev_state_t; + +io_type_t device_type_mtd(void); + +static int mtd_open(io_dev_info_t *dev_info, const uintptr_t spec, + io_entity_t *entity); +static int mtd_seek(io_entity_t *entity, int mode, signed long long offset); +static int mtd_read(io_entity_t *entity, uintptr_t buffer, size_t length, + size_t *length_read); +static int mtd_close(io_entity_t *entity); +static int mtd_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info); +static int mtd_dev_close(io_dev_info_t *dev_info); + +static const io_dev_connector_t mtd_dev_connector = { + .dev_open = mtd_dev_open +}; + +static const io_dev_funcs_t mtd_dev_funcs = { + .type = device_type_mtd, + .open = mtd_open, + .seek = mtd_seek, + .read = mtd_read, + .close = mtd_close, + .dev_close = mtd_dev_close, +}; + +static mtd_dev_state_t state_pool[MAX_IO_MTD_DEVICES]; +static io_dev_info_t dev_info_pool[MAX_IO_MTD_DEVICES]; + +io_type_t device_type_mtd(void) +{ + return IO_TYPE_MTD; +} + +/* Locate a MTD state in the pool, specified by address */ +static int find_first_mtd_state(const io_mtd_dev_spec_t *dev_spec, + unsigned int *index_out) +{ + unsigned int index; + int result = -ENOENT; + + for (index = 0U; index < MAX_IO_MTD_DEVICES; index++) { + /* dev_spec is used as identifier since it's unique */ + if (state_pool[index].dev_spec == dev_spec) { + result = 0; + *index_out = index; + break; + } + } + + return result; +} + +/* Allocate a device info from the pool */ +static int allocate_dev_info(io_dev_info_t **dev_info) +{ + unsigned int index = 0U; + int result; + + result = find_first_mtd_state(NULL, &index); + if (result != 0) { + return -ENOMEM; + } + + dev_info_pool[index].funcs = &mtd_dev_funcs; + dev_info_pool[index].info = (uintptr_t)&state_pool[index]; + *dev_info = &dev_info_pool[index]; + + return 0; +} + +/* Release a device info from the pool */ +static int free_dev_info(io_dev_info_t *dev_info) +{ + int result; + unsigned int index = 0U; + mtd_dev_state_t *state; + + state = (mtd_dev_state_t *)dev_info->info; + result = find_first_mtd_state(state->dev_spec, &index); + if (result != 0) { + return result; + } + + zeromem(state, sizeof(mtd_dev_state_t)); + zeromem(dev_info, sizeof(io_dev_info_t)); + + return 0; +} + +static int mtd_open(io_dev_info_t *dev_info, const uintptr_t spec, + io_entity_t *entity) +{ + mtd_dev_state_t *cur; + + assert((dev_info->info != 0UL) && (entity->info == 0UL)); + + cur = (mtd_dev_state_t *)dev_info->info; + entity->info = (uintptr_t)cur; + cur->offset = 0U; + + return 0; +} + +/* Seek to a specific position using offset */ +static int mtd_seek(io_entity_t *entity, int mode, signed long long offset) +{ + mtd_dev_state_t *cur; + + assert((entity->info != (uintptr_t)NULL) && (offset >= 0)); + + cur = (mtd_dev_state_t *)entity->info; + + switch (mode) { + case IO_SEEK_SET: + if ((offset >= 0) && + ((unsigned long long)offset >= cur->size)) { + return -EINVAL; + } + + cur->offset = offset; + break; + case IO_SEEK_CUR: + if (((cur->offset + (unsigned long long)offset) >= + cur->size) || + ((cur->offset + (unsigned long long)offset) < + cur->offset)) { + return -EINVAL; + } + + cur->offset += (unsigned long long)offset; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int mtd_read(io_entity_t *entity, uintptr_t buffer, size_t length, + size_t *out_length) +{ + mtd_dev_state_t *cur; + io_mtd_ops_t *ops; + int ret; + + assert(entity->info != (uintptr_t)NULL); + assert((length > 0U) && (buffer != (uintptr_t)NULL)); + + cur = (mtd_dev_state_t *)entity->info; + ops = &cur->dev_spec->ops; + assert(ops->read != NULL); + + VERBOSE("Read at %llx into %lx, length %zi\n", + cur->offset, buffer, length); + if ((cur->offset + length) > cur->dev_spec->device_size) { + return -EINVAL; + } + + ret = ops->read(cur->offset, buffer, length, out_length); + if (ret < 0) { + return ret; + } + + assert(*out_length == length); + cur->offset += *out_length; + + return 0; +} + +static int mtd_close(io_entity_t *entity) +{ + entity->info = (uintptr_t)NULL; + + return 0; +} + +static int mtd_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info) +{ + mtd_dev_state_t *cur; + io_dev_info_t *info; + io_mtd_ops_t *ops; + int result; + + result = allocate_dev_info(&info); + if (result != 0) { + return -ENOENT; + } + + cur = (mtd_dev_state_t *)info->info; + cur->dev_spec = (io_mtd_dev_spec_t *)dev_spec; + *dev_info = info; + ops = &(cur->dev_spec->ops); + if (ops->init != NULL) { + result = ops->init(&cur->dev_spec->device_size, + &cur->dev_spec->erase_size); + } + + if (result == 0) { + cur->size = cur->dev_spec->device_size; + } else { + cur->size = 0ULL; + } + + return result; +} + +static int mtd_dev_close(io_dev_info_t *dev_info) +{ + return free_dev_info(dev_info); +} + +/* Exported functions */ + +/* Register the MTD driver in the IO abstraction */ +int register_io_dev_mtd(const io_dev_connector_t **dev_con) +{ + int result; + + result = io_register_device(&dev_info_pool[0]); + if (result == 0) { + *dev_con = &mtd_dev_connector; + } + + return result; +} |