Browse Source

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>
pull/1937/head
Lionel Debieve 5 years ago
parent
commit
b114abb609
  1. 248
      drivers/io/io_mtd.c
  2. 118
      drivers/mtd/nand/core.c
  3. 446
      drivers/mtd/nand/raw_nand.c
  4. 59
      include/drivers/io/io_mtd.h
  5. 3
      include/drivers/io/io_storage.h
  6. 55
      include/drivers/nand.h
  7. 187
      include/drivers/raw_nand.h

248
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;
}

118
drivers/mtd/nand/core.c

@ -0,0 +1,118 @@
/*
* Copyright (c) 2019, STMicroelectronics - All Rights Reserved
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <errno.h>
#include <stddef.h>
#include <platform_def.h>
#include <common/debug.h>
#include <drivers/delay_timer.h>
#include <drivers/nand.h>
#include <lib/utils.h>
/*
* Define a single nand_device used by specific NAND frameworks.
*/
static struct nand_device nand_dev;
static uint8_t scratch_buff[PLATFORM_MTD_MAX_PAGE_SIZE];
int nand_read(unsigned int offset, uintptr_t buffer, size_t length,
size_t *length_read)
{
unsigned int block = offset / nand_dev.block_size;
unsigned int end_block = (offset + length - 1U) / nand_dev.block_size;
unsigned int page_start =
(offset % nand_dev.block_size) / nand_dev.page_size;
unsigned int nb_pages = nand_dev.block_size / nand_dev.page_size;
unsigned int start_offset = offset % nand_dev.page_size;
unsigned int page;
unsigned int bytes_read;
int is_bad;
int ret;
VERBOSE("Block %u - %u, page_start %u, nb %u, length %zu, offset %u\n",
block, end_block, page_start, nb_pages, length, offset);
*length_read = 0UL;
if (((start_offset != 0U) || (length % nand_dev.page_size) != 0U) &&
(sizeof(scratch_buff) < nand_dev.page_size)) {
return -EINVAL;
}
while (block <= end_block) {
is_bad = nand_dev.mtd_block_is_bad(block);
if (is_bad < 0) {
return is_bad;
}
if (is_bad == 1) {
/* Skip the block */
uint32_t max_block =
nand_dev.size / nand_dev.block_size;
block++;
end_block++;
if ((block < max_block) && (end_block < max_block)) {
continue;
}
return -EIO;
}
for (page = page_start; page < nb_pages; page++) {
if ((start_offset != 0U) ||
(length < nand_dev.page_size)) {
ret = nand_dev.mtd_read_page(
&nand_dev,
(block * nb_pages) + page,
(uintptr_t)scratch_buff);
if (ret != 0) {
return ret;
}
bytes_read = MIN((size_t)(nand_dev.page_size -
start_offset),
length);
memcpy((uint8_t *)buffer,
scratch_buff + start_offset,
bytes_read);
start_offset = 0U;
} else {
ret = nand_dev.mtd_read_page(&nand_dev,
(block * nb_pages) + page,
buffer);
if (ret != 0) {
return ret;
}
bytes_read = nand_dev.page_size;
}
length -= bytes_read;
buffer += bytes_read;
*length_read += bytes_read;
if (length == 0U) {
break;
}
}
page_start = 0U;
block++;
}
return 0;
}
struct nand_device *get_nand_device(void)
{
return &nand_dev;
}

446
drivers/mtd/nand/raw_nand.c

@ -0,0 +1,446 @@
/*
* Copyright (c) 2019, STMicroelectronics - All Rights Reserved
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <errno.h>
#include <stddef.h>
#include <platform_def.h>
#include <common/debug.h>
#include <drivers/delay_timer.h>
#include <drivers/raw_nand.h>
#include <lib/utils.h>
#define ONFI_SIGNATURE_ADDR 0x20U
/* CRC calculation */
#define CRC_POLYNOM 0x8005U
#define CRC_INIT_VALUE 0x4F4EU
/* Status register */
#define NAND_STATUS_READY BIT(6)
#define SZ_128M 0x08000000U
#define SZ_512 0x200U
static struct rawnand_device rawnand_dev;
#pragma weak plat_get_raw_nand_data
int plat_get_raw_nand_data(struct rawnand_device *device)
{
return 0;
}
static int nand_send_cmd(uint8_t cmd, unsigned int tim)
{
struct nand_req req;
zeromem(&req, sizeof(struct nand_req));
req.nand = rawnand_dev.nand_dev;
req.type = NAND_REQ_CMD | cmd;
req.inst_delay = tim;
return rawnand_dev.ops->exec(&req);
}
static int nand_send_addr(uint8_t addr, unsigned int tim)
{
struct nand_req req;
zeromem(&req, sizeof(struct nand_req));
req.nand = rawnand_dev.nand_dev;
req.type = NAND_REQ_ADDR;
req.addr = &addr;
req.inst_delay = tim;
return rawnand_dev.ops->exec(&req);
}
static int nand_send_wait(unsigned int delay, unsigned int tim)
{
struct nand_req req;
zeromem(&req, sizeof(struct nand_req));
req.nand = rawnand_dev.nand_dev;
req.type = NAND_REQ_WAIT;
req.inst_delay = tim;
req.delay_ms = delay;
return rawnand_dev.ops->exec(&req);
}
static int nand_read_data(uint8_t *data, unsigned int length, bool use_8bit)
{
struct nand_req req;
zeromem(&req, sizeof(struct nand_req));
req.nand = rawnand_dev.nand_dev;
req.type = NAND_REQ_DATAIN | (use_8bit ? NAND_REQ_BUS_WIDTH_8 : 0U);
req.addr = data;
req.length = length;
return rawnand_dev.ops->exec(&req);
}
int nand_change_read_column_cmd(unsigned int offset, uintptr_t buffer,
unsigned int len)
{
int ret;
uint8_t addr[2];
unsigned int i;
ret = nand_send_cmd(NAND_CMD_CHANGE_1ST, 0U);
if (ret != 0) {
return ret;
}
if (rawnand_dev.nand_dev->buswidth == NAND_BUS_WIDTH_16) {
offset /= 2U;
}
addr[0] = offset;
addr[1] = offset >> 8;
for (i = 0; i < 2U; i++) {
ret = nand_send_addr(addr[i], 0U);
if (ret != 0) {
return ret;
}
}
ret = nand_send_cmd(NAND_CMD_CHANGE_2ND, NAND_TCCS_MIN);
if (ret != 0) {
return ret;
}
return nand_read_data((uint8_t *)buffer, len, false);
}
int nand_read_page_cmd(unsigned int page, unsigned int offset,
uintptr_t buffer, unsigned int len)
{
uint8_t addr[5];
uint8_t i = 0U;
uint8_t j;
int ret;
VERBOSE(">%s page %u offset %u buffer 0x%lx\n", __func__, page, offset,
buffer);
if (rawnand_dev.nand_dev->buswidth == NAND_BUS_WIDTH_16) {
offset /= 2U;
}
addr[i++] = offset;
addr[i++] = offset >> 8;
addr[i++] = page;
addr[i++] = page >> 8;
if (rawnand_dev.nand_dev->size > SZ_128M) {
addr[i++] = page >> 16;
}
ret = nand_send_cmd(NAND_CMD_READ_1ST, 0U);
if (ret != 0) {
return ret;
}
for (j = 0U; j < i; j++) {
ret = nand_send_addr(addr[j], 0U);
if (ret != 0) {
return ret;
}
}
ret = nand_send_cmd(NAND_CMD_READ_2ND, NAND_TWB_MAX);
if (ret != 0) {
return ret;
}
ret = nand_send_wait(PSEC_TO_MSEC(NAND_TR_MAX), NAND_TRR_MIN);
if (ret != 0) {
return ret;
}
if (buffer != 0U) {
ret = nand_read_data((uint8_t *)buffer, len, false);
}
return ret;
}
static int nand_status(uint8_t *status)
{
int ret;
ret = nand_send_cmd(NAND_CMD_STATUS, NAND_TWHR_MIN);
if (ret != 0) {
return ret;
}
if (status != NULL) {
ret = nand_read_data(status, 1U, true);
}
return ret;
}
int nand_wait_ready(unsigned long delay)
{
uint8_t status;
int ret;
uint64_t timeout;
/* Wait before reading status */
udelay(1);
ret = nand_status(NULL);
if (ret != 0) {
return ret;
}
timeout = timeout_init_us(delay);
while (!timeout_elapsed(timeout)) {
ret = nand_read_data(&status, 1U, true);
if (ret != 0) {
return ret;
}
if ((status & NAND_STATUS_READY) != 0U) {
return nand_send_cmd(NAND_CMD_READ_1ST, 0U);
}
udelay(10);
}
return -ETIMEDOUT;
}
#if NAND_ONFI_DETECT
static uint16_t nand_check_crc(uint16_t crc, uint8_t *data_in,
unsigned int data_len)
{
uint32_t i;
uint32_t j;
uint32_t bit;
for (i = 0U; i < data_len; i++) {
uint8_t cur_param = *data_in++;
for (j = BIT(7); j != 0U; j >>= 1) {
bit = crc & BIT(15);
crc <<= 1;
if ((cur_param & j) != 0U) {
bit ^= BIT(15);
}
if (bit != 0U) {
crc ^= CRC_POLYNOM;
}
}
crc &= GENMASK(15, 0);
}
return crc;
}
static int nand_read_id(uint8_t addr, uint8_t *id, unsigned int size)
{
int ret;
ret = nand_send_cmd(NAND_CMD_READID, 0U);
if (ret != 0) {
return ret;
}
ret = nand_send_addr(addr, NAND_TWHR_MIN);
if (ret != 0) {
return ret;
}
return nand_read_data(id, size, true);
}
static int nand_reset(void)
{
int ret;
ret = nand_send_cmd(NAND_CMD_RESET, NAND_TWB_MAX);
if (ret != 0) {
return ret;
}
return nand_send_wait(PSEC_TO_MSEC(NAND_TRST_MAX), 0U);
}
static int nand_read_param_page(void)
{
struct nand_param_page page;
uint8_t addr = 0U;
int ret;
ret = nand_send_cmd(NAND_CMD_READ_PARAM_PAGE, 0U);
if (ret != 0) {
return ret;
}
ret = nand_send_addr(addr, NAND_TWB_MAX);
if (ret != 0) {
return ret;
}
ret = nand_send_wait(PSEC_TO_MSEC(NAND_TR_MAX), NAND_TRR_MIN);
if (ret != 0) {
return ret;
}
ret = nand_read_data((uint8_t *)&page, sizeof(page), true);
if (ret != 0) {
return ret;
}
if (strncmp((char *)&page.page_sig, "ONFI", 4) != 0) {
WARN("Error ONFI detection\n");
return -EINVAL;
}
if (nand_check_crc(CRC_INIT_VALUE, (uint8_t *)&page, 254U) !=
page.crc16) {
WARN("Error reading param\n");
return -EINVAL;
}
if ((page.features & ONFI_FEAT_BUS_WIDTH_16) != 0U) {
rawnand_dev.nand_dev->buswidth = NAND_BUS_WIDTH_16;
} else {
rawnand_dev.nand_dev->buswidth = NAND_BUS_WIDTH_8;
}
rawnand_dev.nand_dev->block_size = page.num_pages_per_blk *
page.bytes_per_page;
rawnand_dev.nand_dev->page_size = page.bytes_per_page;
rawnand_dev.nand_dev->size = page.num_pages_per_blk *
page.bytes_per_page *
page.num_blk_in_lun * page.num_lun;
if (page.nb_ecc_bits != GENMASK_32(7, 0)) {
rawnand_dev.nand_dev->ecc.max_bit_corr = page.nb_ecc_bits;
rawnand_dev.nand_dev->ecc.size = SZ_512;
}
VERBOSE("Page size %u, block_size %u, Size %llu, ecc %u, buswidth %u\n",
rawnand_dev.nand_dev->page_size,
rawnand_dev.nand_dev->block_size, rawnand_dev.nand_dev->size,
rawnand_dev.nand_dev->ecc.max_bit_corr,
rawnand_dev.nand_dev->buswidth);
return 0;
}
static int detect_onfi(void)
{
int ret;
char id[4];
ret = nand_reset();
if (ret != 0) {
return ret;
}
ret = nand_read_id(ONFI_SIGNATURE_ADDR, (uint8_t *)id, sizeof(id));
if (ret != 0) {
return ret;
}
if (strncmp(id, "ONFI", sizeof(id)) != 0) {
WARN("NAND Non ONFI detected\n");
return -ENODEV;
}
return nand_read_param_page();
}
#endif
static int nand_mtd_block_is_bad(unsigned int block)
{
unsigned int nbpages_per_block = rawnand_dev.nand_dev->block_size /
rawnand_dev.nand_dev->page_size;
uint8_t bbm_marker[2];
uint8_t page;
int ret;
for (page = 0U; page < 2U; page++) {
ret = nand_read_page_cmd(block * nbpages_per_block,
rawnand_dev.nand_dev->page_size,
(uintptr_t)bbm_marker,
sizeof(bbm_marker));
if (ret != 0) {
return ret;
}
if ((bbm_marker[0] != GENMASK_32(7, 0)) ||
(bbm_marker[1] != GENMASK_32(7, 0))) {
WARN("Block %u is bad\n", block);
return 1;
}
}
return 0;
}
static int nand_mtd_read_page_raw(struct nand_device *nand, unsigned int page,
uintptr_t buffer)
{
return nand_read_page_cmd(page, 0U, buffer,
rawnand_dev.nand_dev->page_size);
}
void nand_raw_ctrl_init(const struct nand_ctrl_ops *ops)
{
rawnand_dev.ops = ops;
}
int nand_raw_init(unsigned long long *size, unsigned int *erase_size)
{
rawnand_dev.nand_dev = get_nand_device();
if (rawnand_dev.nand_dev == NULL) {
return -EINVAL;
}
rawnand_dev.nand_dev->mtd_block_is_bad = nand_mtd_block_is_bad;
rawnand_dev.nand_dev->mtd_read_page = nand_mtd_read_page_raw;
rawnand_dev.nand_dev->ecc.mode = NAND_ECC_NONE;
if ((rawnand_dev.ops->setup == NULL) ||
(rawnand_dev.ops->exec == NULL)) {
return -ENODEV;
}
#if NAND_ONFI_DETECT
if (detect_onfi() != 0) {
WARN("Detect ONFI failed\n");
}
#endif
if (plat_get_raw_nand_data(&rawnand_dev) != 0) {
return -EINVAL;
}
assert((rawnand_dev.nand_dev->page_size != 0U) &&
(rawnand_dev.nand_dev->block_size != 0U) &&
(rawnand_dev.nand_dev->size != 0U));
*size = rawnand_dev.nand_dev->size;
*erase_size = rawnand_dev.nand_dev->block_size;
rawnand_dev.ops->setup(rawnand_dev.nand_dev);
return 0;
}

59
include/drivers/io/io_mtd.h

@ -0,0 +1,59 @@
/*
* Copyright (c) 2019, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef IO_MTD_H
#define IO_MTD_H
#include <stdint.h>
#include <stdio.h>
#include <drivers/io/io_storage.h>
/* MTD devices ops */
typedef struct io_mtd_ops {
/*
* Initialize MTD framework and retrieve device information.
*
* @size: [out] MTD device size in bytes.
* @erase_size: [out] MTD erase size in bytes.
* Return 0 on success, a negative error code otherwise.
*/
int (*init)(unsigned long long *size, unsigned int *erase_size);
/*
* Execute a read memory operation.
*
* @offset: Offset in bytes to start read operation.
* @buffer: [out] Buffer to store read data.
* @length: Required length to be read in bytes.
* @out_length: [out] Length read in bytes.
* Return 0 on success, a negative error code otherwise.
*/
int (*read)(unsigned int offset, uintptr_t buffer, size_t length,
size_t *out_length);
/*
* Execute a write memory operation.
*
* @offset: Offset in bytes to start write operation.
* @buffer: Buffer to be written in device.
* @length: Required length to be written in bytes.
* Return 0 on success, a negative error code otherwise.
*/
int (*write)(unsigned int offset, uintptr_t buffer, size_t length);
} io_mtd_ops_t;
typedef struct io_mtd_dev_spec {
unsigned long long device_size;
unsigned int erase_size;
io_mtd_ops_t ops;
} io_mtd_dev_spec_t;
struct io_dev_connector;
int register_io_dev_mtd(const struct io_dev_connector **dev_con);
#endif /* IO_MTD_H */

3
include/drivers/io/io_storage.h

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, ARM Limited and Contributors. All rights reserved.
* Copyright (c) 2014-2019, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@ -22,6 +22,7 @@ typedef enum {
IO_TYPE_DUMMY,
IO_TYPE_FIRMWARE_IMAGE_PACKAGE,
IO_TYPE_BLOCK,
IO_TYPE_MTD,
IO_TYPE_MMC,
IO_TYPE_STM32IMAGE,
IO_TYPE_MAX

55
include/drivers/nand.h

@ -0,0 +1,55 @@
/*
* Copyright (c) 2019, STMicroelectronics - All Rights Reserved
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef DRIVERS_NAND_H
#define DRIVERS_NAND_H
#include <stddef.h>
#include <stdint.h>
#include <lib/utils_def.h>
#define PSEC_TO_MSEC(x) div_round_up((x), 1000000000ULL)
struct ecc {
unsigned int mode; /* ECC mode NAND_ECC_MODE_{NONE|HW|ONDIE} */
unsigned int size; /* Data byte per ECC step */
unsigned int bytes; /* ECC bytes per step */
unsigned int max_bit_corr; /* Max correctible bits per ECC steps */
};
struct nand_device {
unsigned int block_size;
unsigned int page_size;
unsigned long long size;
unsigned int nb_planes;
unsigned int buswidth;
struct ecc ecc;
int (*mtd_block_is_bad)(unsigned int block);
int (*mtd_read_page)(struct nand_device *nand, unsigned int page,
uintptr_t buffer);
};
/*
* Read bytes from NAND device
*
* @offset: Byte offset to read from in device
* @buffer: [out] Bytes read from device
* @length: Number of bytes to read
* @length_read: [out] Number of bytes read from device
* Return: 0 on success, a negative errno on failure
*/
int nand_read(unsigned int offset, uintptr_t buffer, size_t length,
size_t *length_read);
/*
* Get NAND device instance
*
* Return: NAND device instance reference
*/
struct nand_device *get_nand_device(void);
#endif /* DRIVERS_NAND_H */

187
include/drivers/raw_nand.h

@ -0,0 +1,187 @@
/*
* Copyright (c) 2019, STMicroelectronics - All Rights Reserved
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef DRIVERS_RAW_NAND_H
#define DRIVERS_RAW_NAND_H
#include <stdint.h>
#include <drivers/nand.h>
/* NAND ONFI default value mode 0 in picosecond */
#define NAND_TADL_MIN 400000UL
#define NAND_TALH_MIN 20000UL
#define NAND_TALS_MIN 50000UL
#define NAND_TAR_MIN 25000UL
#define NAND_TCCS_MIN 500000UL
#define NAND_TCEA_MIN 100000UL
#define NAND_TCEH_MIN 20000UL
#define NAND_TCH_MIN 20000UL
#define NAND_TCHZ_MAX 100000UL
#define NAND_TCLH_MIN 20000UL
#define NAND_TCLR_MIN 20000UL
#define NAND_TCLS_MIN 50000UL
#define NAND_TCOH_MIN 0UL
#define NAND_TCS_MIN 70000UL
#define NAND_TDH_MIN 20000UL
#define NAND_TDS_MIN 40000UL
#define NAND_TFEAT_MAX 1000000UL
#define NAND_TIR_MIN 10000UL
#define NAND_TITC_MIN 1000000UL
#define NAND_TR_MAX 200000000UL
#define NAND_TRC_MIN 100000UL
#define NAND_TREA_MAX 40000UL
#define NAND_TREH_MIN 30000UL
#define NAND_TRHOH_MIN 0UL
#define NAND_TRHW_MIN 200000UL
#define NAND_TRHZ_MAX 200000UL
#define NAND_TRLOH_MIN 0UL
#define NAND_TRP_MIN 50000UL
#define NAND_TRR_MIN 40000UL
#define NAND_TRST_MAX 250000000000ULL
#define NAND_TWB_MAX 200000UL
#define NAND_TWC_MIN 100000UL
#define NAND_TWH_MIN 30000UL
#define NAND_TWHR_MIN 120000UL
#define NAND_TWP_MIN 50000UL
#define NAND_TWW_MIN 100000UL
/* NAND request types */
#define NAND_REQ_CMD 0x0000U
#define NAND_REQ_ADDR 0x1000U
#define NAND_REQ_DATAIN 0x2000U
#define NAND_REQ_DATAOUT 0x3000U
#define NAND_REQ_WAIT 0x4000U
#define NAND_REQ_MASK GENMASK(14, 12)
#define NAND_REQ_BUS_WIDTH_8 BIT(15)
#define PARAM_PAGE_SIZE 256
/* NAND ONFI commands */
#define NAND_CMD_READ_1ST 0x00U
#define NAND_CMD_CHANGE_1ST 0x05U
#define NAND_CMD_READID_SIG_ADDR 0x20U
#define NAND_CMD_READ_2ND 0x30U
#define NAND_CMD_STATUS 0x70U
#define NAND_CMD_READID 0x90U
#define NAND_CMD_CHANGE_2ND 0xE0U
#define NAND_CMD_READ_PARAM_PAGE 0xECU
#define NAND_CMD_RESET 0xFFU
#define ONFI_REV_21 BIT(3)
#define ONFI_FEAT_BUS_WIDTH_16 BIT(0)
#define ONFI_FEAT_EXTENDED_PARAM BIT(7)
/* NAND ECC type */
#define NAND_ECC_NONE U(0)
#define NAND_ECC_HW U(1)
#define NAND_ECC_ONDIE U(2)
/* NAND bus width */
#define NAND_BUS_WIDTH_8 U(0)
#define NAND_BUS_WIDTH_16 U(1)
struct nand_req {
struct nand_device *nand;
uint16_t type;
uint8_t *addr;
unsigned int length;
unsigned int delay_ms;
unsigned int inst_delay;
};
struct nand_param_page {
/* Rev information and feature block */
uint32_t page_sig;
uint16_t rev;
uint16_t features;
uint16_t opt_cmd;
uint8_t jtg;
uint8_t train_cmd;
uint16_t ext_param_length;
uint8_t nb_param_pages;
uint8_t reserved1[17];
/* Manufacturer information */
uint8_t manufacturer[12];
uint8_t model[20];
uint8_t manufacturer_id;
uint16_t data_code;
uint8_t reserved2[13];
/* Memory organization */
uint32_t bytes_per_page;
uint16_t spare_per_page;
uint32_t bytes_per_partial;
uint16_t spare_per_partial;
uint32_t num_pages_per_blk;
uint32_t num_blk_in_lun;
uint8_t num_lun;
uint8_t num_addr_cycles;
uint8_t bit_per_cell;
uint16_t max_bb_per_lun;
uint16_t blk_endur;
uint8_t valid_blk_begin;
uint16_t blk_enbur_valid;
uint8_t nb_prog_page;
uint8_t partial_prog_attr;
uint8_t nb_ecc_bits;
uint8_t plane_addr;
uint8_t mplanes_ops;
uint8_t ez_nand;
uint8_t reserved3[12];
/* Electrical parameters */
uint8_t io_pin_cap_max;
uint16_t sdr_timing_mode;
uint16_t sdr_prog_cache_timing;
uint16_t tprog;
uint16_t tbers;
uint16_t tr;
uint16_t tccs;
uint8_t nvddr_timing_mode;
uint8_t nvddr2_timing_mode;
uint8_t nvddr_features;
uint16_t clk_input_cap_typ;
uint16_t io_pin_cap_typ;
uint16_t input_pin_cap_typ;
uint8_t input_pin_cap_max;
uint8_t drv_strength_support;
uint16_t tr_max;
uint16_t tadl;
uint16_t tr_typ;
uint8_t reserved4[6];
/* Vendor block */
uint16_t vendor_revision;
uint8_t vendor[88];
uint16_t crc16;
} __packed;
struct nand_ctrl_ops {
int (*exec)(struct nand_req *req);
void (*setup)(struct nand_device *nand);
};
struct rawnand_device {
struct nand_device *nand_dev;
const struct nand_ctrl_ops *ops;
};
int nand_raw_init(unsigned long long *size, unsigned int *erase_size);
int nand_wait_ready(unsigned long delay);
int nand_read_page_cmd(unsigned int page, unsigned int offset,
uintptr_t buffer, unsigned int len);
int nand_change_read_column_cmd(unsigned int offset, uintptr_t buffer,
unsigned int len);
void nand_raw_ctrl_init(const struct nand_ctrl_ops *ops);
/*
* Platform can implement this to override default raw NAND instance
* configuration.
*
* @device: target raw NAND instance.
* Return 0 on success, negative value otherwise.
*/
int plat_get_raw_nand_data(struct rawnand_device *device);
#endif /* DRIVERS_RAW_NAND_H */
Loading…
Cancel
Save