e64b0e81ad
This chip is apparently used in some Surfans F20 units, and has the same geometry as the ATO25D1GA. It has an on-die ECC engine. Change-Id: I4d37a2455620ce43cec0a9bcbb32c776d1a8eba1
242 lines
9 KiB
C
242 lines
9 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
* \/ \/ \/ \/ \/
|
|
* $Id$
|
|
*
|
|
* Copyright (C) 2021 Aidan MacDonald
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
* KIND, either express or implied.
|
|
*
|
|
****************************************************************************/
|
|
|
|
#ifndef __NAND_X1000_H__
|
|
#define __NAND_X1000_H__
|
|
|
|
#include <stdint.h>
|
|
#include <stddef.h>
|
|
#include <stdbool.h>
|
|
#include "kernel.h"
|
|
|
|
#define NAND_SUCCESS 0
|
|
#define NAND_ERR_UNKNOWN_CHIP (-1)
|
|
#define NAND_ERR_PROGRAM_FAIL (-2)
|
|
#define NAND_ERR_ERASE_FAIL (-3)
|
|
#define NAND_ERR_UNALIGNED (-4)
|
|
#define NAND_ERR_ECC_FAIL (-5)
|
|
|
|
/* keep max page size in sync with the NAND chip table in the .c file */
|
|
#define NAND_DRV_SCRATCHSIZE 32
|
|
#define NAND_DRV_MAXPAGESIZE 2112
|
|
|
|
/* Quad I/O support bit */
|
|
#define NAND_CHIPFLAG_QUAD 0x0001
|
|
/* Chip requires QE bit set to enable quad I/O mode */
|
|
#define NAND_CHIPFLAG_HAS_QE_BIT 0x0002
|
|
/* True if the chip has on-die ECC */
|
|
#define NAND_CHIPFLAG_ON_DIE_ECC 0x0004
|
|
|
|
/* cmd mode a d phase format has data */
|
|
#define NANDCMD_RESET SFC_CMD(0xff, SFC_TMODE_1_1_1, 0, 0, SFC_PFMT_ADDR_FIRST, 0)
|
|
#define NANDCMD_READID_OPCODE SFC_CMD(0x9f, SFC_TMODE_1_1_1, 0, 0, SFC_PFMT_ADDR_FIRST, 1)
|
|
#define NANDCMD_READID_ADDR SFC_CMD(0x9f, SFC_TMODE_1_1_1, 1, 0, SFC_PFMT_ADDR_FIRST, 1)
|
|
#define NANDCMD_READID_DUMMY SFC_CMD(0x9f, SFC_TMODE_1_1_1, 0, 8, SFC_PFMT_ADDR_FIRST, 1)
|
|
#define NANDCMD_WR_EN SFC_CMD(0x06, SFC_TMODE_1_1_1, 0, 0, SFC_PFMT_ADDR_FIRST, 0)
|
|
#define NANDCMD_GET_FEATURE SFC_CMD(0x0f, SFC_TMODE_1_1_1, 1, 0, SFC_PFMT_ADDR_FIRST, 1)
|
|
#define NANDCMD_SET_FEATURE SFC_CMD(0x1f, SFC_TMODE_1_1_1, 1, 0, SFC_PFMT_ADDR_FIRST, 1)
|
|
#define NANDCMD_PAGE_READ SFC_CMD(0x13, SFC_TMODE_1_1_1, 3, 0, SFC_PFMT_ADDR_FIRST, 0)
|
|
#define NANDCMD_READ_CACHE_SLOW SFC_CMD(0x03, SFC_TMODE_1_1_1, 2, 8, SFC_PFMT_ADDR_FIRST, 1)
|
|
#define NANDCMD_READ_CACHE SFC_CMD(0x0b, SFC_TMODE_1_1_1, 2, 8, SFC_PFMT_ADDR_FIRST, 1)
|
|
#define NANDCMD_READ_CACHE_x4 SFC_CMD(0x6b, SFC_TMODE_1_1_4, 2, 8, SFC_PFMT_ADDR_FIRST, 1)
|
|
#define NANDCMD_PROGRAM_EXECUTE SFC_CMD(0x10, SFC_TMODE_1_1_1, 3, 0, SFC_PFMT_ADDR_FIRST, 0)
|
|
#define NANDCMD_PROGRAM_LOAD SFC_CMD(0x02, SFC_TMODE_1_1_1, 2, 0, SFC_PFMT_ADDR_FIRST, 1)
|
|
#define NANDCMD_PROGRAM_LOAD_x4 SFC_CMD(0x32, SFC_TMODE_1_1_4, 2, 0, SFC_PFMT_ADDR_FIRST, 1)
|
|
#define NANDCMD_BLOCK_ERASE SFC_CMD(0xd8, SFC_TMODE_1_1_1, 3, 0, SFC_PFMT_ADDR_FIRST, 0)
|
|
|
|
/* Feature registers are found in linux/mtd/spinand.h,
|
|
* apparently these are pretty standardized */
|
|
#define FREG_PROT 0xa0
|
|
#define FREG_PROT_UNLOCK 0x00
|
|
|
|
#define FREG_CFG 0xb0
|
|
#define FREG_CFG_OTP_ENABLE (1 << 6)
|
|
#define FREG_CFG_ECC_ENABLE (1 << 4)
|
|
#define FREG_CFG_QUAD_ENABLE (1 << 0)
|
|
|
|
/* Winbond-specific bit used on the W25N01GVxx */
|
|
#define FREG_CFG_WINBOND_BUF (1 << 3)
|
|
|
|
#define FREG_STATUS 0xc0
|
|
#define FREG_STATUS_BUSY (1 << 0)
|
|
#define FREG_STATUS_EFAIL (1 << 2)
|
|
#define FREG_STATUS_PFAIL (1 << 3)
|
|
#define FREG_STATUS_ECC_MASK (3 << 4)
|
|
#define FREG_STATUS_ECC_NO_FLIPS (0 << 4)
|
|
#define FREG_STATUS_ECC_HAS_FLIPS (1 << 4)
|
|
#define FREG_STATUS_ECC_UNCOR_ERR (2 << 4)
|
|
|
|
/* Types to distinguish between block & page addresses in the API.
|
|
*
|
|
* BIT 31 log2_ppb bits
|
|
* +-------------------------------+---------------+
|
|
* nand_page_t = | block nr | page nr |
|
|
* +-------------------------------+---------------+
|
|
* BIT 0
|
|
*
|
|
* The page address is split into block and page numbers. Page numbers occupy
|
|
* the lower log2_ppb bits, and the block number occupies the upper bits.
|
|
*
|
|
* Block addresses are structured the same as page addresses, but with a page
|
|
* number of 0. So block number N has address N << log2_ppb.
|
|
*/
|
|
typedef uint32_t nand_block_t;
|
|
typedef uint32_t nand_page_t;
|
|
|
|
struct nand_drv;
|
|
|
|
struct nand_chip {
|
|
/* Base2 logarithm of the number of pages per block */
|
|
unsigned log2_ppb;
|
|
|
|
/* Size of a page's main / oob areas, in bytes. */
|
|
unsigned page_size;
|
|
unsigned oob_size;
|
|
|
|
/* Total number of blocks in the chip */
|
|
unsigned nr_blocks;
|
|
|
|
/* Bad block marker offset within the 1st page of a bad block */
|
|
unsigned bbm_pos;
|
|
|
|
/* Clock frequency to use */
|
|
uint32_t clock_freq;
|
|
|
|
/* Value of sfc_dev_conf */
|
|
uint32_t dev_conf;
|
|
|
|
/* Chip specific flags */
|
|
uint32_t flags;
|
|
|
|
/* SFC commands for issuing I/O ops */
|
|
uint32_t cmd_page_read;
|
|
uint32_t cmd_program_execute;
|
|
uint32_t cmd_block_erase;
|
|
uint32_t cmd_read_cache;
|
|
uint32_t cmd_program_load;
|
|
|
|
/* Chip-specific setup routine */
|
|
void(*setup_chip)(struct nand_drv* drv);
|
|
};
|
|
|
|
enum nand_readid_method {
|
|
NAND_READID_OPCODE,
|
|
NAND_READID_ADDR,
|
|
NAND_READID_DUMMY,
|
|
};
|
|
|
|
struct nand_chip_id {
|
|
uint8_t method;
|
|
uint8_t num_id_bytes;
|
|
uint8_t id_bytes[4];
|
|
const struct nand_chip* chip;
|
|
};
|
|
|
|
#define NAND_CHIP_ID(_chip, _method, ...) \
|
|
{ .method = _method, \
|
|
.num_id_bytes = ARRAYLEN(((uint8_t[]){__VA_ARGS__})), \
|
|
.id_bytes = {__VA_ARGS__}, \
|
|
.chip = _chip }
|
|
|
|
struct nand_drv {
|
|
/* NAND access lock. Needs to be held during any operations. */
|
|
struct mutex mutex;
|
|
|
|
/* Reference count for open/close operations */
|
|
unsigned refcount;
|
|
|
|
/* Scratch and page buffers. Both need to be cacheline-aligned and are
|
|
* provided externally by the caller prior to nand_open().
|
|
*
|
|
* - The scratch buffer is NAND_DRV_SCRATCHSIZE bytes long and is used
|
|
* for small data transfers associated with commands. It must not be
|
|
* disturbed while any NAND operation is in progress.
|
|
*
|
|
* - The page buffer is used by certain functions like nand_read_bytes(),
|
|
* but it's main purpose is to provide a common temporary buffer for
|
|
* driver users to perform I/O with. Must be fpage_size bytes long.
|
|
*/
|
|
uint8_t* scratch_buf;
|
|
uint8_t* page_buf;
|
|
|
|
/* Pointer to the chip data. */
|
|
const struct nand_chip* chip;
|
|
|
|
/* Pages per block = 1 << chip->log2_ppb */
|
|
unsigned ppb;
|
|
|
|
/* Full page size = chip->page_size + chip->oob_size */
|
|
unsigned fpage_size;
|
|
};
|
|
|
|
extern const struct nand_chip_id supported_nand_chips[];
|
|
extern const size_t nr_supported_nand_chips;
|
|
|
|
/* Return the static NAND driver instance.
|
|
*
|
|
* ALL normal Rockbox code should use this instance. The SPL does not
|
|
* use it, because it needs to manually place buffers in external RAM.
|
|
*/
|
|
extern struct nand_drv* nand_init(void);
|
|
|
|
static inline void nand_lock(struct nand_drv* drv)
|
|
{
|
|
mutex_lock(&drv->mutex);
|
|
}
|
|
|
|
static inline void nand_unlock(struct nand_drv* drv)
|
|
{
|
|
mutex_unlock(&drv->mutex);
|
|
}
|
|
|
|
/* Open or close the NAND driver
|
|
*
|
|
* The NAND driver is reference counted, and opening / closing it will
|
|
* increment and decrement the reference count. The hardware is only
|
|
* controlled when the reference count rises above or falls to 0, else
|
|
* these functions are no-ops which always succeed.
|
|
*
|
|
* These functions require the lock to be held.
|
|
*/
|
|
extern int nand_open(struct nand_drv* drv);
|
|
extern void nand_close(struct nand_drv* drv);
|
|
|
|
/* Enable/disable OTP access. OTP data pages are usually vendor-specific. */
|
|
void nand_enable_otp(struct nand_drv* drv, bool enable);
|
|
|
|
/* Read / program / erase operations. Buffer needs to be cache-aligned for DMA.
|
|
* Read and program operate on full page data, ie. including OOB data areas.
|
|
*
|
|
* NOTE: ECC is not implemented. If it ever needs to be, these functions will
|
|
* probably use ECC transparently. All code should be written to expect this.
|
|
*/
|
|
extern int nand_block_erase(struct nand_drv* drv, nand_block_t block);
|
|
extern int nand_page_program(struct nand_drv* drv, nand_page_t page, const void* buffer);
|
|
extern int nand_page_read(struct nand_drv* drv, nand_page_t page, void* buffer);
|
|
|
|
/* Wrappers to read/write bytes. For simple access to the main data area only.
|
|
* The write address / length must align to a block boundary. Reads do not have
|
|
* any alignment requirement. OOB data is never read, and is written as 0xff.
|
|
*/
|
|
extern int nand_read_bytes(struct nand_drv* drv, uint32_t byte_addr, uint32_t byte_len, void* buffer);
|
|
extern int nand_write_bytes(struct nand_drv* drv, uint32_t byte_addr, uint32_t byte_len, const void* buffer);
|
|
|
|
#endif /* __NAND_X1000_H__ */
|