mirror of
https://source.denx.de/u-boot/u-boot.git
synced 2026-06-13 15:03:58 +03:00
Merge branch 'uboot-05102025' of https://source.denx.de/u-boot/custodians/u-boot-nand-flash
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:
@@ -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);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user