This series adds significant and valuable work by Mikhail Kshevetskiy to
align spi-mem with Linux 6.16. It also includes contributions to the mtd
performance patches, a work started by Miquel Raynal and improved by
Mikhail Kshevetskiy. Additionally, two patches tighten dependencies on
the Atmel driver.

The patches pass the pipeline CI:
https://source.denx.de/u-boot/custodians/u-boot-nand-flash/-/pipelines/27873
This commit is contained in:
Tom Rini
2025-10-10 08:24:45 -06:00
24 changed files with 3696 additions and 545 deletions

View File

@@ -19,7 +19,7 @@
* @oobsize: OOB area size
* @pages_per_eraseblock: number of pages per eraseblock
* @eraseblocks_per_lun: number of eraseblocks per LUN (Logical Unit Number)
* @max_bad_eraseblocks_per_lun: maximum number of eraseblocks per LUN
* @max_bad_eraseblocks_per_lun: maximum number of bad eraseblocks per LUN
* @planes_per_lun: number of planes per LUN
* @luns_per_target: number of LUN per target (target is a synonym for die)
* @ntargets: total number of targets exposed by the NAND device
@@ -80,8 +80,19 @@ struct nand_pos {
unsigned int page;
};
/**
* enum nand_page_io_req_type - Direction of an I/O request
* @NAND_PAGE_READ: from the chip, to the controller
* @NAND_PAGE_WRITE: from the controller, to the chip
*/
enum nand_page_io_req_type {
NAND_PAGE_READ = 0,
NAND_PAGE_WRITE,
};
/**
* struct nand_page_io_req - NAND I/O request object
* @type: the type of page I/O: read or write
* @pos: the position this I/O request is targeting
* @dataoffs: the offset within the page
* @datalen: number of data bytes to read from/write to this page
@@ -90,6 +101,8 @@ struct nand_pos {
* @ooblen: the number of OOB bytes to read from/write to this page
* @oobbuf: buffer to store OOB data in or get OOB data from
* @mode: one of the %MTD_OPS_XXX mode
* @continuous: no need to start over the operation at the end of each page, the
* NAND device will automatically prepare the next one
*
* This object is used to pass per-page I/O requests to NAND sub-layers. This
* way all useful information are already formatted in a useful way and
@@ -97,6 +110,7 @@ struct nand_pos {
* specific commands/operations.
*/
struct nand_page_io_req {
enum nand_page_io_req_type type;
struct nand_pos pos;
unsigned int dataoffs;
unsigned int datalen;
@@ -111,6 +125,7 @@ struct nand_page_io_req {
void *in;
} oobbuf;
int mode;
bool continuous;
};
/**
@@ -271,6 +286,20 @@ nanddev_pages_per_eraseblock(const struct nand_device *nand)
return nand->memorg.pages_per_eraseblock;
}
/**
* nanddev_pages_per_target() - Get the number of pages per target
* @nand: NAND device
*
* Return: the number of pages per target.
*/
static inline unsigned int
nanddev_pages_per_target(const struct nand_device *nand)
{
return nand->memorg.pages_per_eraseblock *
nand->memorg.eraseblocks_per_lun *
nand->memorg.luns_per_target;
}
/**
* nanddev_per_page_oobsize() - Get NAND erase block size
* @nand: NAND device
@@ -294,6 +323,18 @@ nanddev_eraseblocks_per_lun(const struct nand_device *nand)
return nand->memorg.eraseblocks_per_lun;
}
/**
* nanddev_eraseblocks_per_target() - Get the number of eraseblocks per target
* @nand: NAND device
*
* Return: the number of eraseblocks per target.
*/
static inline unsigned int
nanddev_eraseblocks_per_target(const struct nand_device *nand)
{
return nand->memorg.eraseblocks_per_lun * nand->memorg.luns_per_target;
}
/**
* nanddev_target_size() - Get the total size provided by a single target/die
* @nand: NAND device
@@ -320,7 +361,7 @@ static inline unsigned int nanddev_ntargets(const struct nand_device *nand)
}
/**
* nanddev_neraseblocks() - Get the total number of erasablocks
* nanddev_neraseblocks() - Get the total number of eraseblocks
* @nand: NAND device
*
* Return: the total number of eraseblocks exposed by @nand.
@@ -358,6 +399,29 @@ nanddev_get_memorg(struct nand_device *nand)
return &nand->memorg;
}
/**
* nanddev_get_ecc_conf() - Extract the ECC configuration from a NAND device
* @nand: NAND device
*/
static inline const struct nand_ecc_req *
nanddev_get_ecc_conf(struct nand_device *nand)
{
return &nand->eccreq;
}
/**
* nanddev_set_ecc_requirements() - Assign the ECC requirements of a NAND
* device
* @nand: NAND device
* @reqs: Requirements
*/
static inline void
nanddev_set_ecc_requirements(struct nand_device *nand,
const struct nand_ecc_req *reqs)
{
nand->eccreq = *reqs;
}
int nanddev_init(struct nand_device *nand, const struct nand_ops *ops,
struct module *owner);
void nanddev_cleanup(struct nand_device *nand);
@@ -603,21 +667,23 @@ static inline void nanddev_pos_next_page(struct nand_device *nand,
}
/**
* nand_io_iter_init - Initialize a NAND I/O iterator
* nand_io_page_iter_init - Initialize a NAND I/O iterator
* @nand: NAND device
* @offs: absolute offset
* @req: MTD request
* @iter: NAND I/O iterator
*
* Initializes a NAND iterator based on the information passed by the MTD
* layer.
* layer for page jumps.
*/
static inline void nanddev_io_iter_init(struct nand_device *nand,
loff_t offs, struct mtd_oob_ops *req,
struct nand_io_iter *iter)
static inline void nanddev_io_page_iter_init(struct nand_device *nand,
enum nand_page_io_req_type reqtype,
loff_t offs, struct mtd_oob_ops *req,
struct nand_io_iter *iter)
{
struct mtd_info *mtd = nanddev_to_mtd(nand);
iter->req.type = reqtype;
iter->req.mode = req->mode;
iter->req.dataoffs = nanddev_offs_to_pos(nand, offs, &iter->req.pos);
iter->req.ooboffs = req->ooboffs;
@@ -632,6 +698,43 @@ static inline void nanddev_io_iter_init(struct nand_device *nand,
iter->req.ooblen = min_t(unsigned int,
iter->oobbytes_per_page - iter->req.ooboffs,
iter->oobleft);
iter->req.continuous = false;
}
/**
* nand_io_block_iter_init - Initialize a NAND I/O iterator
* @nand: NAND device
* @offs: absolute offset
* @req: MTD request
* @iter: NAND I/O iterator
*
* Initializes a NAND iterator based on the information passed by the MTD
* layer for block jumps (no OOB)
*
* In practice only reads may leverage this iterator.
*/
static inline void nanddev_io_block_iter_init(struct nand_device *nand,
enum nand_page_io_req_type reqtype,
loff_t offs, struct mtd_oob_ops *req,
struct nand_io_iter *iter)
{
unsigned int offs_in_eb;
iter->req.type = reqtype;
iter->req.mode = req->mode;
iter->req.dataoffs = nanddev_offs_to_pos(nand, offs, &iter->req.pos);
iter->req.ooboffs = 0;
iter->oobbytes_per_page = 0;
iter->dataleft = req->len;
iter->oobleft = 0;
iter->req.databuf.in = req->datbuf;
offs_in_eb = (nand->memorg.pagesize * iter->req.pos.page) + iter->req.dataoffs;
iter->req.datalen = min_t(unsigned int,
nanddev_eraseblock_size(nand) - offs_in_eb,
iter->dataleft);
iter->req.oobbuf.in = NULL;
iter->req.ooblen = 0;
iter->req.continuous = true;
}
/**
@@ -657,6 +760,25 @@ static inline void nanddev_io_iter_next_page(struct nand_device *nand,
iter->oobleft);
}
/**
* nand_io_iter_next_block - Move to the next block
* @nand: NAND device
* @iter: NAND I/O iterator
*
* Updates the @iter to point to the next block.
* No OOB handling available.
*/
static inline void nanddev_io_iter_next_block(struct nand_device *nand,
struct nand_io_iter *iter)
{
nanddev_pos_next_eraseblock(nand, &iter->req.pos);
iter->dataleft -= iter->req.datalen;
iter->req.databuf.in += iter->req.datalen;
iter->req.dataoffs = 0;
iter->req.datalen = min_t(unsigned int, nanddev_eraseblock_size(nand),
iter->dataleft);
}
/**
* nand_io_iter_end - Should end iteration or not
* @nand: NAND device
@@ -685,13 +807,28 @@ static inline bool nanddev_io_iter_end(struct nand_device *nand,
* @req: MTD I/O request
* @iter: NAND I/O iterator
*
* Should be used for iterate over pages that are contained in an MTD request.
* Should be used for iterating over pages that are contained in an MTD request.
*/
#define nanddev_io_for_each_page(nand, start, req, iter) \
for (nanddev_io_iter_init(nand, start, req, iter); \
#define nanddev_io_for_each_page(nand, type, start, req, iter) \
for (nanddev_io_page_iter_init(nand, type, start, req, iter); \
!nanddev_io_iter_end(nand, iter); \
nanddev_io_iter_next_page(nand, iter))
/**
* nand_io_for_each_block - Iterate over all NAND pages contained in an MTD I/O
* request, one block at a time
* @nand: NAND device
* @start: start address to read/write from
* @req: MTD I/O request
* @iter: NAND I/O iterator
*
* Should be used for iterating over blocks that are contained in an MTD request.
*/
#define nanddev_io_for_each_block(nand, type, start, req, iter) \
for (nanddev_io_block_iter_init(nand, type, start, req, iter); \
!nanddev_io_iter_end(nand, iter); \
nanddev_io_iter_next_block(nand, iter))
bool nanddev_isbad(struct nand_device *nand, const struct nand_pos *pos);
bool nanddev_isreserved(struct nand_device *nand, const struct nand_pos *pos);
int nanddev_markbad(struct nand_device *nand, const struct nand_pos *pos);

View File

@@ -26,126 +26,218 @@
* Standard SPI NAND flash operations
*/
#define SPINAND_RESET_OP \
#define SPINAND_RESET_1S_0_0_OP \
SPI_MEM_OP(SPI_MEM_OP_CMD(0xff, 1), \
SPI_MEM_OP_NO_ADDR, \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_NO_DATA)
#define SPINAND_WR_EN_DIS_OP(enable) \
#define SPINAND_WR_EN_DIS_1S_0_0_OP(enable) \
SPI_MEM_OP(SPI_MEM_OP_CMD((enable) ? 0x06 : 0x04, 1), \
SPI_MEM_OP_NO_ADDR, \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_NO_DATA)
#define SPINAND_READID_OP(naddr, ndummy, buf, len) \
#define SPINAND_READID_1S_1S_1S_OP(naddr, ndummy, buf, len) \
SPI_MEM_OP(SPI_MEM_OP_CMD(0x9f, 1), \
SPI_MEM_OP_ADDR(naddr, 0, 1), \
SPI_MEM_OP_DUMMY(ndummy, 1), \
SPI_MEM_OP_DATA_IN(len, buf, 1))
#define SPINAND_SET_FEATURE_OP(reg, valptr) \
#define SPINAND_SET_FEATURE_1S_1S_1S_OP(reg, valptr) \
SPI_MEM_OP(SPI_MEM_OP_CMD(0x1f, 1), \
SPI_MEM_OP_ADDR(1, reg, 1), \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_DATA_OUT(1, valptr, 1))
#define SPINAND_GET_FEATURE_OP(reg, valptr) \
#define SPINAND_GET_FEATURE_1S_1S_1S_OP(reg, valptr) \
SPI_MEM_OP(SPI_MEM_OP_CMD(0x0f, 1), \
SPI_MEM_OP_ADDR(1, reg, 1), \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_DATA_IN(1, valptr, 1))
#define SPINAND_BLK_ERASE_OP(addr) \
#define SPINAND_BLK_ERASE_1S_1S_0_OP(addr) \
SPI_MEM_OP(SPI_MEM_OP_CMD(0xd8, 1), \
SPI_MEM_OP_ADDR(3, addr, 1), \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_NO_DATA)
#define SPINAND_PAGE_READ_OP(addr) \
#define SPINAND_PAGE_READ_1S_1S_0_OP(addr) \
SPI_MEM_OP(SPI_MEM_OP_CMD(0x13, 1), \
SPI_MEM_OP_ADDR(3, addr, 1), \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_NO_DATA)
#define SPINAND_PAGE_READ_FROM_CACHE_OP(fast, addr, ndummy, buf, len) \
SPI_MEM_OP(SPI_MEM_OP_CMD(fast ? 0x0b : 0x03, 1), \
#define SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(addr, ndummy, buf, len, freq) \
SPI_MEM_OP(SPI_MEM_OP_CMD(0x03, 1), \
SPI_MEM_OP_ADDR(2, addr, 1), \
SPI_MEM_OP_DUMMY(ndummy, 1), \
SPI_MEM_OP_DATA_IN(len, buf, 1))
SPI_MEM_OP_DATA_IN(len, buf, 1), \
SPI_MEM_OP_MAX_FREQ(freq))
#define SPINAND_PAGE_READ_FROM_CACHE_OP_3A(fast, addr, ndummy, buf, len) \
SPI_MEM_OP(SPI_MEM_OP_CMD(fast ? 0x0b : 0x03, 1), \
#define SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(addr, ndummy, buf, len, freq) \
SPI_MEM_OP(SPI_MEM_OP_CMD(0x0b, 1), \
SPI_MEM_OP_ADDR(2, addr, 1), \
SPI_MEM_OP_DUMMY(ndummy, 1), \
SPI_MEM_OP_DATA_IN(len, buf, 1), \
SPI_MEM_OP_MAX_FREQ(freq))
#define SPINAND_PAGE_READ_FROM_CACHE_3A_1S_1S_1S_OP(addr, ndummy, buf, len, freq) \
SPI_MEM_OP(SPI_MEM_OP_CMD(0x03, 1), \
SPI_MEM_OP_ADDR(3, addr, 1), \
SPI_MEM_OP_DUMMY(ndummy, 1), \
SPI_MEM_OP_DATA_IN(len, buf, 1))
SPI_MEM_OP_DATA_IN(len, buf, 1), \
SPI_MEM_OP_MAX_FREQ(freq))
#define SPINAND_PAGE_READ_FROM_CACHE_X2_OP(addr, ndummy, buf, len) \
#define SPINAND_PAGE_READ_FROM_CACHE_FAST_3A_1S_1S_1S_OP(addr, ndummy, buf, len, freq) \
SPI_MEM_OP(SPI_MEM_OP_CMD(0x0b, 1), \
SPI_MEM_OP_ADDR(3, addr, 1), \
SPI_MEM_OP_DUMMY(ndummy, 1), \
SPI_MEM_OP_DATA_IN(len, buf, 1), \
SPI_MEM_OP_MAX_FREQ(freq))
#define SPINAND_PAGE_READ_FROM_CACHE_1S_1D_1D_OP(addr, ndummy, buf, len, freq) \
SPI_MEM_OP(SPI_MEM_OP_CMD(0x0d, 1), \
SPI_MEM_DTR_OP_ADDR(2, addr, 1), \
SPI_MEM_DTR_OP_DUMMY(ndummy, 1), \
SPI_MEM_DTR_OP_DATA_IN(len, buf, 1), \
SPI_MEM_OP_MAX_FREQ(freq))
#define SPINAND_PAGE_READ_FROM_CACHE_1S_1S_2S_OP(addr, ndummy, buf, len, freq) \
SPI_MEM_OP(SPI_MEM_OP_CMD(0x3b, 1), \
SPI_MEM_OP_ADDR(2, addr, 1), \
SPI_MEM_OP_DUMMY(ndummy, 1), \
SPI_MEM_OP_DATA_IN(len, buf, 2))
SPI_MEM_OP_DATA_IN(len, buf, 2), \
SPI_MEM_OP_MAX_FREQ(freq))
#define SPINAND_PAGE_READ_FROM_CACHE_X2_OP_3A(addr, ndummy, buf, len) \
#define SPINAND_PAGE_READ_FROM_CACHE_3A_1S_1S_2S_OP(addr, ndummy, buf, len, freq) \
SPI_MEM_OP(SPI_MEM_OP_CMD(0x3b, 1), \
SPI_MEM_OP_ADDR(3, addr, 1), \
SPI_MEM_OP_DUMMY(ndummy, 1), \
SPI_MEM_OP_DATA_IN(len, buf, 2))
SPI_MEM_OP_DATA_IN(len, buf, 2), \
SPI_MEM_OP_MAX_FREQ(freq))
#define SPINAND_PAGE_READ_FROM_CACHE_X4_OP(addr, ndummy, buf, len) \
SPI_MEM_OP(SPI_MEM_OP_CMD(0x6b, 1), \
SPI_MEM_OP_ADDR(2, addr, 1), \
SPI_MEM_OP_DUMMY(ndummy, 1), \
SPI_MEM_OP_DATA_IN(len, buf, 4))
#define SPINAND_PAGE_READ_FROM_CACHE_1S_1D_2D_OP(addr, ndummy, buf, len, freq) \
SPI_MEM_OP(SPI_MEM_OP_CMD(0x3d, 1), \
SPI_MEM_DTR_OP_ADDR(2, addr, 1), \
SPI_MEM_DTR_OP_DUMMY(ndummy, 1), \
SPI_MEM_DTR_OP_DATA_IN(len, buf, 2), \
SPI_MEM_OP_MAX_FREQ(freq))
#define SPINAND_PAGE_READ_FROM_CACHE_X4_OP_3A(addr, ndummy, buf, len) \
SPI_MEM_OP(SPI_MEM_OP_CMD(0x6b, 1), \
SPI_MEM_OP_ADDR(3, addr, 1), \
SPI_MEM_OP_DUMMY(ndummy, 1), \
SPI_MEM_OP_DATA_IN(len, buf, 4))
#define SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP(addr, ndummy, buf, len) \
#define SPINAND_PAGE_READ_FROM_CACHE_1S_2S_2S_OP(addr, ndummy, buf, len, freq) \
SPI_MEM_OP(SPI_MEM_OP_CMD(0xbb, 1), \
SPI_MEM_OP_ADDR(2, addr, 2), \
SPI_MEM_OP_DUMMY(ndummy, 2), \
SPI_MEM_OP_DATA_IN(len, buf, 2))
SPI_MEM_OP_DATA_IN(len, buf, 2), \
SPI_MEM_OP_MAX_FREQ(freq))
#define SPINAND_PAGE_READ_FROM_CACHE_DUALIO_OP_3A(addr, ndummy, buf, len) \
#define SPINAND_PAGE_READ_FROM_CACHE_3A_1S_2S_2S_OP(addr, ndummy, buf, len, freq) \
SPI_MEM_OP(SPI_MEM_OP_CMD(0xbb, 1), \
SPI_MEM_OP_ADDR(3, addr, 2), \
SPI_MEM_OP_DUMMY(ndummy, 2), \
SPI_MEM_OP_DATA_IN(len, buf, 2))
SPI_MEM_OP_DATA_IN(len, buf, 2), \
SPI_MEM_OP_MAX_FREQ(freq))
#define SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(addr, ndummy, buf, len) \
#define SPINAND_PAGE_READ_FROM_CACHE_1S_2D_2D_OP(addr, ndummy, buf, len, freq) \
SPI_MEM_OP(SPI_MEM_OP_CMD(0xbd, 1), \
SPI_MEM_DTR_OP_ADDR(2, addr, 2), \
SPI_MEM_DTR_OP_DUMMY(ndummy, 2), \
SPI_MEM_DTR_OP_DATA_IN(len, buf, 2), \
SPI_MEM_OP_MAX_FREQ(freq))
#define SPINAND_PAGE_READ_FROM_CACHE_1S_1S_4S_OP(addr, ndummy, buf, len, freq) \
SPI_MEM_OP(SPI_MEM_OP_CMD(0x6b, 1), \
SPI_MEM_OP_ADDR(2, addr, 1), \
SPI_MEM_OP_DUMMY(ndummy, 1), \
SPI_MEM_OP_DATA_IN(len, buf, 4), \
SPI_MEM_OP_MAX_FREQ(freq))
#define SPINAND_PAGE_READ_FROM_CACHE_3A_1S_1S_4S_OP(addr, ndummy, buf, len, freq) \
SPI_MEM_OP(SPI_MEM_OP_CMD(0x6b, 1), \
SPI_MEM_OP_ADDR(3, addr, 1), \
SPI_MEM_OP_DUMMY(ndummy, 1), \
SPI_MEM_OP_DATA_IN(len, buf, 4), \
SPI_MEM_OP_MAX_FREQ(freq))
#define SPINAND_PAGE_READ_FROM_CACHE_1S_1D_4D_OP(addr, ndummy, buf, len, freq) \
SPI_MEM_OP(SPI_MEM_OP_CMD(0x6d, 1), \
SPI_MEM_DTR_OP_ADDR(2, addr, 1), \
SPI_MEM_DTR_OP_DUMMY(ndummy, 1), \
SPI_MEM_DTR_OP_DATA_IN(len, buf, 4), \
SPI_MEM_OP_MAX_FREQ(freq))
#define SPINAND_PAGE_READ_FROM_CACHE_1S_4S_4S_OP(addr, ndummy, buf, len, freq) \
SPI_MEM_OP(SPI_MEM_OP_CMD(0xeb, 1), \
SPI_MEM_OP_ADDR(2, addr, 4), \
SPI_MEM_OP_DUMMY(ndummy, 4), \
SPI_MEM_OP_DATA_IN(len, buf, 4))
SPI_MEM_OP_DATA_IN(len, buf, 4), \
SPI_MEM_OP_MAX_FREQ(freq))
#define SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP_3A(addr, ndummy, buf, len) \
#define SPINAND_PAGE_READ_FROM_CACHE_3A_1S_4S_4S_OP(addr, ndummy, buf, len, freq) \
SPI_MEM_OP(SPI_MEM_OP_CMD(0xeb, 1), \
SPI_MEM_OP_ADDR(3, addr, 4), \
SPI_MEM_OP_DUMMY(ndummy, 4), \
SPI_MEM_OP_DATA_IN(len, buf, 4))
SPI_MEM_OP_DATA_IN(len, buf, 4), \
SPI_MEM_OP_MAX_FREQ(freq))
#define SPINAND_PROG_EXEC_OP(addr) \
#define SPINAND_PAGE_READ_FROM_CACHE_1S_4D_4D_OP(addr, ndummy, buf, len, freq) \
SPI_MEM_OP(SPI_MEM_OP_CMD(0xed, 1), \
SPI_MEM_DTR_OP_ADDR(2, addr, 4), \
SPI_MEM_DTR_OP_DUMMY(ndummy, 4), \
SPI_MEM_DTR_OP_DATA_IN(len, buf, 4), \
SPI_MEM_OP_MAX_FREQ(freq))
#define SPINAND_PAGE_READ_FROM_CACHE_1S_1S_8S_OP(addr, ndummy, buf, len, freq) \
SPI_MEM_OP(SPI_MEM_OP_CMD(0x8b, 1), \
SPI_MEM_OP_ADDR(2, addr, 1), \
SPI_MEM_OP_DUMMY(ndummy, 1), \
SPI_MEM_OP_DATA_IN(len, buf, 8), \
SPI_MEM_OP_MAX_FREQ(freq))
#define SPINAND_PAGE_READ_FROM_CACHE_1S_8S_8S_OP(addr, ndummy, buf, len, freq) \
SPI_MEM_OP(SPI_MEM_OP_CMD(0xcb, 1), \
SPI_MEM_OP_ADDR(2, addr, 8), \
SPI_MEM_OP_DUMMY(ndummy, 8), \
SPI_MEM_OP_DATA_IN(len, buf, 8), \
SPI_MEM_OP_MAX_FREQ(freq))
#define SPINAND_PAGE_READ_FROM_CACHE_1S_1D_8D_OP(addr, ndummy, buf, len, freq) \
SPI_MEM_OP(SPI_MEM_OP_CMD(0x9d, 1), \
SPI_MEM_DTR_OP_ADDR(2, addr, 1), \
SPI_MEM_DTR_OP_DUMMY(ndummy, 1), \
SPI_MEM_DTR_OP_DATA_IN(len, buf, 8), \
SPI_MEM_OP_MAX_FREQ(freq))
#define SPINAND_PROG_EXEC_1S_1S_0_OP(addr) \
SPI_MEM_OP(SPI_MEM_OP_CMD(0x10, 1), \
SPI_MEM_OP_ADDR(3, addr, 1), \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_NO_DATA)
#define SPINAND_PROG_LOAD(reset, addr, buf, len) \
#define SPINAND_PROG_LOAD_1S_1S_1S_OP(reset, addr, buf, len) \
SPI_MEM_OP(SPI_MEM_OP_CMD(reset ? 0x02 : 0x84, 1), \
SPI_MEM_OP_ADDR(2, addr, 1), \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_DATA_OUT(len, buf, 1))
#define SPINAND_PROG_LOAD_X4(reset, addr, buf, len) \
#define SPINAND_PROG_LOAD_1S_1S_4S_OP(reset, addr, buf, len) \
SPI_MEM_OP(SPI_MEM_OP_CMD(reset ? 0x32 : 0x34, 1), \
SPI_MEM_OP_ADDR(2, addr, 1), \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_DATA_OUT(len, buf, 4))
#define SPINAND_PROG_LOAD_1S_1S_8S_OP(addr, buf, len) \
SPI_MEM_OP(SPI_MEM_OP_CMD(0x82, 1), \
SPI_MEM_OP_ADDR(2, addr, 1), \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_DATA_OUT(len, buf, 8))
#define SPINAND_PROG_LOAD_1S_8S_8S_OP(reset, addr, buf, len) \
SPI_MEM_OP(SPI_MEM_OP_CMD(reset ? 0xc2 : 0xc4, 1), \
SPI_MEM_OP_ADDR(2, addr, 8), \
SPI_MEM_OP_NO_DUMMY, \
SPI_MEM_OP_DATA_OUT(len, buf, 8))
/**
* Standard SPI NAND flash commands
*/
@@ -175,7 +267,29 @@
struct spinand_op;
struct spinand_device;
#define SPINAND_MAX_ID_LEN 4
#define SPINAND_MAX_ID_LEN 5
/*
* For erase, write and read operation, we got the following timings :
* tBERS (erase) 1ms to 4ms
* tPROG 300us to 400us
* tREAD 25us to 100us
* In order to minimize latency, the min value is divided by 4 for the
* initial delay, and dividing by 20 for the poll delay.
* For reset, 5us/10us/500us if the device is respectively
* reading/programming/erasing when the RESET occurs. Since we always
* issue a RESET when the device is IDLE, 5us is selected for both initial
* and poll delay.
*/
#define SPINAND_READ_INITIAL_DELAY_US 6
#define SPINAND_READ_POLL_DELAY_US 5
#define SPINAND_RESET_INITIAL_DELAY_US 5
#define SPINAND_RESET_POLL_DELAY_US 5
#define SPINAND_WRITE_INITIAL_DELAY_US 75
#define SPINAND_WRITE_POLL_DELAY_US 15
#define SPINAND_ERASE_INITIAL_DELAY_US 250
#define SPINAND_ERASE_POLL_DELAY_US 50
#define SPINAND_WAITRDY_TIMEOUT_MS 400
/**
* struct spinand_id - SPI NAND id structure
@@ -244,13 +358,17 @@ struct spinand_manufacturer {
};
/* SPI NAND manufacturers */
extern const struct spinand_manufacturer alliancememory_spinand_manufacturer;
extern const struct spinand_manufacturer ato_spinand_manufacturer;
extern const struct spinand_manufacturer esmt_c8_spinand_manufacturer;
extern const struct spinand_manufacturer foresee_spinand_manufacturer;
extern const struct spinand_manufacturer gigadevice_spinand_manufacturer;
extern const struct spinand_manufacturer macronix_spinand_manufacturer;
extern const struct spinand_manufacturer micron_spinand_manufacturer;
extern const struct spinand_manufacturer paragon_spinand_manufacturer;
extern const struct spinand_manufacturer skyhigh_spinand_manufacturer;
extern const struct spinand_manufacturer toshiba_spinand_manufacturer;
extern const struct spinand_manufacturer winbond_spinand_manufacturer;
extern const struct spinand_manufacturer esmt_c8_spinand_manufacturer;
extern const struct spinand_manufacturer xtx_spinand_manufacturer;
/**
@@ -291,8 +409,73 @@ struct spinand_ecc_info {
const struct mtd_ooblayout_ops *ooblayout;
};
#define SPINAND_HAS_QE_BIT BIT(0)
#define SPINAND_HAS_CR_FEAT_BIT BIT(1)
#define SPINAND_HAS_QE_BIT BIT(0)
#define SPINAND_HAS_CR_FEAT_BIT BIT(1)
#define SPINAND_HAS_PROG_PLANE_SELECT_BIT BIT(2)
#define SPINAND_HAS_READ_PLANE_SELECT_BIT BIT(3)
#define SPINAND_NO_RAW_ACCESS BIT(4)
/**
* struct spinand_otp_layout - structure to describe the SPI NAND OTP area
* @npages: number of pages in the OTP
* @start_page: start page of the user/factory OTP area.
*/
struct spinand_otp_layout {
unsigned int npages;
unsigned int start_page;
};
/**
* struct spinand_fact_otp_ops - SPI NAND OTP methods for factory area
* @info: get the OTP area information
* @read: read from the SPI NAND OTP area
*/
struct spinand_fact_otp_ops {
int (*info)(struct spinand_device *spinand, size_t len,
struct otp_info *buf, size_t *retlen);
int (*read)(struct spinand_device *spinand, loff_t from, size_t len,
size_t *retlen, u8 *buf);
};
/**
* struct spinand_user_otp_ops - SPI NAND OTP methods for user area
* @info: get the OTP area information
* @lock: lock an OTP region
* @erase: erase an OTP region
* @read: read from the SPI NAND OTP area
* @write: write to the SPI NAND OTP area
*/
struct spinand_user_otp_ops {
int (*info)(struct spinand_device *spinand, size_t len,
struct otp_info *buf, size_t *retlen);
int (*lock)(struct spinand_device *spinand, loff_t from, size_t len);
int (*erase)(struct spinand_device *spinand, loff_t from, size_t len);
int (*read)(struct spinand_device *spinand, loff_t from, size_t len,
size_t *retlen, u8 *buf);
int (*write)(struct spinand_device *spinand, loff_t from, size_t len,
size_t *retlen, const u8 *buf);
};
/**
* struct spinand_fact_otp - SPI NAND OTP grouping structure for factory area
* @layout: OTP region layout
* @ops: OTP access ops
*/
struct spinand_fact_otp {
const struct spinand_otp_layout layout;
const struct spinand_fact_otp_ops *ops;
};
/**
* struct spinand_user_otp - SPI NAND OTP grouping structure for user area
* @layout: OTP region layout
* @ops: OTP access ops
*/
struct spinand_user_otp {
const struct spinand_otp_layout layout;
const struct spinand_user_otp_ops *ops;
};
/**
* struct spinand_info - Structure used to describe SPI NAND chips
@@ -308,6 +491,12 @@ struct spinand_ecc_info {
* @op_variants.update_cache: variants of the update-cache operation
* @select_target: function used to select a target/die. Required only for
* multi-die chips
* @configure_chip: Align the chip configuration with the core settings
* @set_cont_read: enable/disable continuous cached reads
* @fact_otp: SPI NAND factory OTP info.
* @user_otp: SPI NAND user OTP info.
* @read_retries: the number of read retry modes supported
* @set_read_retry: enable/disable read retry for data recovery
*
* Each SPI NAND manufacturer driver should have a spinand_info table
* describing all the chips supported by the driver.
@@ -326,6 +515,14 @@ struct spinand_info {
} op_variants;
int (*select_target)(struct spinand_device *spinand,
unsigned int target);
int (*configure_chip)(struct spinand_device *spinand);
int (*set_cont_read)(struct spinand_device *spinand,
bool enable);
struct spinand_fact_otp fact_otp;
struct spinand_user_otp user_otp;
unsigned int read_retries;
int (*set_read_retry)(struct spinand_device *spinand,
unsigned int read_retry);
};
#define SPINAND_ID(__method, ...) \
@@ -349,7 +546,35 @@ struct spinand_info {
}
#define SPINAND_SELECT_TARGET(__func) \
.select_target = __func,
.select_target = __func
#define SPINAND_CONFIGURE_CHIP(__configure_chip) \
.configure_chip = __configure_chip
#define SPINAND_CONT_READ(__set_cont_read) \
.set_cont_read = __set_cont_read
#define SPINAND_FACT_OTP_INFO(__npages, __start_page, __ops) \
.fact_otp = { \
.layout = { \
.npages = __npages, \
.start_page = __start_page, \
}, \
.ops = __ops, \
}
#define SPINAND_USER_OTP_INFO(__npages, __start_page, __ops) \
.user_otp = { \
.layout = { \
.npages = __npages, \
.start_page = __start_page, \
}, \
.ops = __ops, \
}
#define SPINAND_READ_RETRY(__read_retries, __set_read_retry) \
.read_retries = __read_retries, \
.set_read_retry = __set_read_retry
#define SPINAND_INFO(__model, __id, __memorg, __eccreq, __op_variants, \
__flags, ...) \
@@ -363,6 +588,13 @@ struct spinand_info {
__VA_ARGS__ \
}
struct spinand_dirmap {
struct spi_mem_dirmap_desc *wdesc;
struct spi_mem_dirmap_desc *rdesc;
struct spi_mem_dirmap_desc *wdesc_ecc;
struct spi_mem_dirmap_desc *rdesc_ecc;
};
/**
* struct spinand_device - SPI NAND device instance
* @base: NAND device instance
@@ -387,7 +619,20 @@ struct spinand_info {
* passed in spi_mem_op be DMA-able, so we can't based the bufs on
* the stack
* @manufacturer: SPI NAND manufacturer information
* @configure_chip: Align the chip configuration with the core settings
* @cont_read_possible: Field filled by the core once the whole system
* configuration is known to tell whether continuous reads are
* suitable to use or not in general with this chip/configuration.
* A per-transfer check must of course be done to ensure it is
* actually relevant to enable this feature.
* @set_cont_read: Enable/disable the continuous read feature
* @priv: manufacturer private data
* @fact_otp: SPI NAND factory OTP info.
* @user_otp: SPI NAND user OTP info.
* @read_retries: the number of read retry modes supported
* @set_read_retry: Enable/disable the read retry feature
* @last_wait_status: status of the last wait operation that will be used in case
* ->get_status() is not populated by the spinand device.
*/
struct spinand_device {
struct nand_device base;
@@ -406,6 +651,8 @@ struct spinand_device {
const struct spi_mem_op *update_cache;
} op_templates;
struct spinand_dirmap *dirmaps;
int (*select_target)(struct spinand_device *spinand,
unsigned int target);
unsigned int cur_target;
@@ -418,6 +665,20 @@ struct spinand_device {
u8 *scratchbuf;
const struct spinand_manufacturer *manufacturer;
void *priv;
u8 last_wait_status;
int (*configure_chip)(struct spinand_device *spinand);
bool cont_read_possible;
int (*set_cont_read)(struct spinand_device *spinand,
bool enable);
const struct spinand_fact_otp *fact_otp;
const struct spinand_user_otp *user_otp;
unsigned int read_retries;
int (*set_read_retry)(struct spinand_device *spinand,
unsigned int retry_mode);
};
/**
@@ -499,6 +760,31 @@ int spinand_match_and_init(struct spinand_device *spinand,
enum spinand_readid_method rdid_method);
int spinand_upd_cfg(struct spinand_device *spinand, u8 mask, u8 val);
int spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 *val);
int spinand_write_reg_op(struct spinand_device *spinand, u8 reg, u8 val);
int spinand_write_enable_op(struct spinand_device *spinand);
int spinand_select_target(struct spinand_device *spinand, unsigned int target);
int spinand_wait(struct spinand_device *spinand, unsigned long initial_delay_us,
unsigned long poll_delay_us, u8 *s);
int spinand_read_page(struct spinand_device *spinand,
const struct nand_page_io_req *req);
int spinand_write_page(struct spinand_device *spinand,
const struct nand_page_io_req *req);
size_t spinand_otp_page_size(struct spinand_device *spinand);
size_t spinand_fact_otp_size(struct spinand_device *spinand);
size_t spinand_user_otp_size(struct spinand_device *spinand);
int spinand_fact_otp_read(struct spinand_device *spinand, loff_t ofs,
size_t len, size_t *retlen, u8 *buf);
int spinand_user_otp_read(struct spinand_device *spinand, loff_t ofs,
size_t len, size_t *retlen, u8 *buf);
int spinand_user_otp_write(struct spinand_device *spinand, loff_t ofs,
size_t len, size_t *retlen, const u8 *buf);
int spinand_set_mtd_otp_ops(struct spinand_device *spinand);
#endif /* __LINUX_MTD_SPINAND_H */