From 5f50d82d8918b711717b4bbd96c6f348eb6e2a2c Mon Sep 17 00:00:00 2001 From: Ezequiel Garcia Date: Thu, 16 Aug 2018 17:30:00 +0200 Subject: mtd: Uninline mtd_write_oob and move it to mtdcore.c There's no reason for having mtd_write_oob inlined in mtd.h header. Move it to mtdcore.c where it belongs. Signed-off-by: Ezequiel Garcia Acked-by: Boris Brezillon Signed-off-by: Jacek Anaszewski Signed-off-by: Miquel Raynal --- include/linux/mtd/mtd.h | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) (limited to 'include') diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 823e535b82..eb39f38887 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -351,17 +351,7 @@ int mtd_panic_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops); - -static inline int mtd_write_oob(struct mtd_info *mtd, loff_t to, - struct mtd_oob_ops *ops) -{ - ops->retlen = ops->oobretlen = 0; - if (!mtd->_write_oob) - return -EOPNOTSUPP; - if (!(mtd->flags & MTD_WRITEABLE)) - return -EROFS; - return mtd->_write_oob(mtd, to, ops); -} +int mtd_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops); int mtd_get_fact_prot_info(struct mtd_info *mtd, size_t len, size_t *retlen, struct otp_info *buf); -- cgit From 9bfc3fde9c64320f7165560a64888fad09bc0fee Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Thu, 16 Aug 2018 17:30:03 +0200 Subject: mtd: add get/set of_node/flash_node helpers We are going to begin using the mtd->dev.of_node field for MTD device nodes, so let's add helpers for it. Also, we'll be making some conversions on spi_nor (and nand_chip eventually) too, so get that ready with their own helpers. Signed-off-by: Brian Norris Reviewed-by: Boris Brezillon Signed-off-by: Miquel Raynal Reviewed-by: Jagan Teki --- include/linux/mtd/mtd.h | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'include') diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index eb39f38887..272c646f9d 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -21,6 +21,9 @@ #include #include #include +#if IS_ENABLED(CONFIG_DM) +#include +#endif #define MAX_MTD_DEVICES 32 #endif @@ -306,6 +309,31 @@ struct mtd_info { int usecount; }; +#if IS_ENABLED(CONFIG_DM) +static inline void mtd_set_of_node(struct mtd_info *mtd, + const struct device_node *np) +{ + mtd->dev->node.np = np; +} + +static inline const struct device_node *mtd_get_of_node(struct mtd_info *mtd) +{ + return mtd->dev->node.np; +} +#else +struct device_node; + +static inline void mtd_set_of_node(struct mtd_info *mtd, + const struct device_node *np) +{ +} + +static inline const struct device_node *mtd_get_of_node(struct mtd_info *mtd) +{ + return NULL; +} +#endif + int mtd_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *oobecc); int mtd_ooblayout_find_eccregion(struct mtd_info *mtd, int eccbyte, -- cgit From d02f1d36ec6fe6bfadd77fa71b1df228010ddaa8 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 16 Aug 2018 17:30:05 +0200 Subject: mtd: move definitions to enlarge their range Some helpers might be useful in a future 'mtd' U-Boot command to parse MTD device list. Signed-off-by: Miquel Raynal --- include/linux/mtd/mtd.h | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'include') diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 272c646f9d..b8c2c3fd59 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -533,6 +533,12 @@ int del_mtd_device(struct mtd_info *mtd); int add_mtd_partitions(struct mtd_info *, const struct mtd_partition *, int); int del_mtd_partitions(struct mtd_info *); +struct mtd_info *__mtd_next_device(int i); +#define mtd_for_each_device(mtd) \ + for ((mtd) = __mtd_next_device(0); \ + (mtd) != NULL; \ + (mtd) = __mtd_next_device(mtd->index + 1)) + int mtd_arg_off(const char *arg, int *idx, loff_t *off, loff_t *size, loff_t *maxsize, int devtype, uint64_t chipsize); int mtd_arg_off_size(int argc, char *const argv[], int *idx, loff_t *off, -- cgit From a430fa06a4ac50e785fdbfb7f43c3cb14b35619c Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 16 Aug 2018 17:30:07 +0200 Subject: mtd: move NAND files into a raw/ subdirectory NAND flavors, like serial and parallel, have a lot in common and would benefit to share code. Let's move raw (parallel) NAND specific code in a raw/ subdirectory, to ease the addition of a core file in nand/ and the introduction of a spi/ subdirectory specific to SPI NANDs. Signed-off-by: Miquel Raynal --- include/configs/MPC8313ERDB.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/configs/MPC8313ERDB.h b/include/configs/MPC8313ERDB.h index 884dd4125e..5b5c38f2c9 100644 --- a/include/configs/MPC8313ERDB.h +++ b/include/configs/MPC8313ERDB.h @@ -240,7 +240,7 @@ /* LB refresh timer prescal, 266MHz/32 */ #define CONFIG_SYS_LBC_MRTPR 0x20000000 /*TODO */ -/* drivers/mtd/nand/nand.c */ +/* drivers/mtd/nand/raw/nand.c */ #if defined(CONFIG_NAND) && defined(CONFIG_SPL_BUILD) #define CONFIG_SYS_NAND_BASE 0xFFF00000 #else -- cgit From b95db8d33a1e920801816e47ffc5c6f18acce024 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 16 Aug 2018 17:30:09 +0200 Subject: mtd: nand: Add core infrastructure to deal with NAND devices Add an intermediate layer to abstract NAND device interface so that some logic can be shared between SPI NANDs, parallel/raw NANDs, OneNANDs, ... Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal Acked-by: Jagan Teki --- include/linux/mtd/nand.h | 731 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 731 insertions(+) create mode 100644 include/linux/mtd/nand.h (limited to 'include') diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h new file mode 100644 index 0000000000..ada7af4a41 --- /dev/null +++ b/include/linux/mtd/nand.h @@ -0,0 +1,731 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2017 - Free Electrons + * + * Authors: + * Boris Brezillon + * Peter Pan + */ + +#ifndef __LINUX_MTD_NAND_H +#define __LINUX_MTD_NAND_H + +#include + +/** + * struct nand_memory_organization - Memory organization structure + * @bits_per_cell: number of bits per NAND cell + * @pagesize: page size + * @oobsize: OOB area size + * @pages_per_eraseblock: number of pages per eraseblock + * @eraseblocks_per_lun: number of eraseblocks per LUN (Logical Unit Number) + * @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 + */ +struct nand_memory_organization { + unsigned int bits_per_cell; + unsigned int pagesize; + unsigned int oobsize; + unsigned int pages_per_eraseblock; + unsigned int eraseblocks_per_lun; + unsigned int planes_per_lun; + unsigned int luns_per_target; + unsigned int ntargets; +}; + +#define NAND_MEMORG(bpc, ps, os, ppe, epl, ppl, lpt, nt) \ + { \ + .bits_per_cell = (bpc), \ + .pagesize = (ps), \ + .oobsize = (os), \ + .pages_per_eraseblock = (ppe), \ + .eraseblocks_per_lun = (epl), \ + .planes_per_lun = (ppl), \ + .luns_per_target = (lpt), \ + .ntargets = (nt), \ + } + +/** + * struct nand_row_converter - Information needed to convert an absolute offset + * into a row address + * @lun_addr_shift: position of the LUN identifier in the row address + * @eraseblock_addr_shift: position of the eraseblock identifier in the row + * address + */ +struct nand_row_converter { + unsigned int lun_addr_shift; + unsigned int eraseblock_addr_shift; +}; + +/** + * struct nand_pos - NAND position object + * @target: the NAND target/die + * @lun: the LUN identifier + * @plane: the plane within the LUN + * @eraseblock: the eraseblock within the LUN + * @page: the page within the LUN + * + * These information are usually used by specific sub-layers to select the + * appropriate target/die and generate a row address to pass to the device. + */ +struct nand_pos { + unsigned int target; + unsigned int lun; + unsigned int plane; + unsigned int eraseblock; + unsigned int page; +}; + +/** + * struct nand_page_io_req - NAND I/O request object + * @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 + * @databuf: buffer to store data in or get data from + * @ooboffs: the OOB offset within the page + * @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 + * + * 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 + * specific NAND layers can focus on translating these information into + * specific commands/operations. + */ +struct nand_page_io_req { + struct nand_pos pos; + unsigned int dataoffs; + unsigned int datalen; + union { + const void *out; + void *in; + } databuf; + unsigned int ooboffs; + unsigned int ooblen; + union { + const void *out; + void *in; + } oobbuf; +}; + +/** + * struct nand_ecc_req - NAND ECC requirements + * @strength: ECC strength + * @step_size: ECC step/block size + */ +struct nand_ecc_req { + unsigned int strength; + unsigned int step_size; +}; + +#define NAND_ECCREQ(str, stp) { .strength = (str), .step_size = (stp) } + +/** + * struct nand_bbt - bad block table object + * @cache: in memory BBT cache + */ +struct nand_bbt { + unsigned long *cache; +}; + +struct nand_device; + +/** + * struct nand_ops - NAND operations + * @erase: erase a specific block. No need to check if the block is bad before + * erasing, this has been taken care of by the generic NAND layer + * @markbad: mark a specific block bad. No need to check if the block is + * already marked bad, this has been taken care of by the generic + * NAND layer. This method should just write the BBM (Bad Block + * Marker) so that future call to struct_nand_ops->isbad() return + * true + * @isbad: check whether a block is bad or not. This method should just read + * the BBM and return whether the block is bad or not based on what it + * reads + * + * These are all low level operations that should be implemented by specialized + * NAND layers (SPI NAND, raw NAND, ...). + */ +struct nand_ops { + int (*erase)(struct nand_device *nand, const struct nand_pos *pos); + int (*markbad)(struct nand_device *nand, const struct nand_pos *pos); + bool (*isbad)(struct nand_device *nand, const struct nand_pos *pos); +}; + +/** + * struct nand_device - NAND device + * @mtd: MTD instance attached to the NAND device + * @memorg: memory layout + * @eccreq: ECC requirements + * @rowconv: position to row address converter + * @bbt: bad block table info + * @ops: NAND operations attached to the NAND device + * + * Generic NAND object. Specialized NAND layers (raw NAND, SPI NAND, OneNAND) + * should declare their own NAND object embedding a nand_device struct (that's + * how inheritance is done). + * struct_nand_device->memorg and struct_nand_device->eccreq should be filled + * at device detection time to reflect the NAND device + * capabilities/requirements. Once this is done nanddev_init() can be called. + * It will take care of converting NAND information into MTD ones, which means + * the specialized NAND layers should never manually tweak + * struct_nand_device->mtd except for the ->_read/write() hooks. + */ +struct nand_device { + struct mtd_info *mtd; + struct nand_memory_organization memorg; + struct nand_ecc_req eccreq; + struct nand_row_converter rowconv; + struct nand_bbt bbt; + const struct nand_ops *ops; +}; + +/** + * struct nand_io_iter - NAND I/O iterator + * @req: current I/O request + * @oobbytes_per_page: maximum number of OOB bytes per page + * @dataleft: remaining number of data bytes to read/write + * @oobleft: remaining number of OOB bytes to read/write + * + * Can be used by specialized NAND layers to iterate over all pages covered + * by an MTD I/O request, which should greatly simplifies the boiler-plate + * code needed to read/write data from/to a NAND device. + */ +struct nand_io_iter { + struct nand_page_io_req req; + unsigned int oobbytes_per_page; + unsigned int dataleft; + unsigned int oobleft; +}; + +/** + * mtd_to_nanddev() - Get the NAND device attached to the MTD instance + * @mtd: MTD instance + * + * Return: the NAND device embedding @mtd. + */ +static inline struct nand_device *mtd_to_nanddev(struct mtd_info *mtd) +{ + return mtd->priv; +} + +/** + * nanddev_to_mtd() - Get the MTD device attached to a NAND device + * @nand: NAND device + * + * Return: the MTD device embedded in @nand. + */ +static inline struct mtd_info *nanddev_to_mtd(struct nand_device *nand) +{ + return nand->mtd; +} + +/* + * nanddev_bits_per_cell() - Get the number of bits per cell + * @nand: NAND device + * + * Return: the number of bits per cell. + */ +static inline unsigned int nanddev_bits_per_cell(const struct nand_device *nand) +{ + return nand->memorg.bits_per_cell; +} + +/** + * nanddev_page_size() - Get NAND page size + * @nand: NAND device + * + * Return: the page size. + */ +static inline size_t nanddev_page_size(const struct nand_device *nand) +{ + return nand->memorg.pagesize; +} + +/** + * nanddev_per_page_oobsize() - Get NAND OOB size + * @nand: NAND device + * + * Return: the OOB size. + */ +static inline unsigned int +nanddev_per_page_oobsize(const struct nand_device *nand) +{ + return nand->memorg.oobsize; +} + +/** + * nanddev_pages_per_eraseblock() - Get the number of pages per eraseblock + * @nand: NAND device + * + * Return: the number of pages per eraseblock. + */ +static inline unsigned int +nanddev_pages_per_eraseblock(const struct nand_device *nand) +{ + return nand->memorg.pages_per_eraseblock; +} + +/** + * nanddev_per_page_oobsize() - Get NAND erase block size + * @nand: NAND device + * + * Return: the eraseblock size. + */ +static inline size_t nanddev_eraseblock_size(const struct nand_device *nand) +{ + return nand->memorg.pagesize * nand->memorg.pages_per_eraseblock; +} + +/** + * nanddev_eraseblocks_per_lun() - Get the number of eraseblocks per LUN + * @nand: NAND device + * + * Return: the number of eraseblocks per LUN. + */ +static inline unsigned int +nanddev_eraseblocks_per_lun(const struct nand_device *nand) +{ + return nand->memorg.eraseblocks_per_lun; +} + +/** + * nanddev_target_size() - Get the total size provided by a single target/die + * @nand: NAND device + * + * Return: the total size exposed by a single target/die in bytes. + */ +static inline u64 nanddev_target_size(const struct nand_device *nand) +{ + return (u64)nand->memorg.luns_per_target * + nand->memorg.eraseblocks_per_lun * + nand->memorg.pages_per_eraseblock * + nand->memorg.pagesize; +} + +/** + * nanddev_ntarget() - Get the total of targets + * @nand: NAND device + * + * Return: the number of targets/dies exposed by @nand. + */ +static inline unsigned int nanddev_ntargets(const struct nand_device *nand) +{ + return nand->memorg.ntargets; +} + +/** + * nanddev_neraseblocks() - Get the total number of erasablocks + * @nand: NAND device + * + * Return: the total number of eraseblocks exposed by @nand. + */ +static inline unsigned int nanddev_neraseblocks(const struct nand_device *nand) +{ + return (u64)nand->memorg.luns_per_target * + nand->memorg.eraseblocks_per_lun * + nand->memorg.pages_per_eraseblock; +} + +/** + * nanddev_size() - Get NAND size + * @nand: NAND device + * + * Return: the total size (in bytes) exposed by @nand. + */ +static inline u64 nanddev_size(const struct nand_device *nand) +{ + return nanddev_target_size(nand) * nanddev_ntargets(nand); +} + +/** + * nanddev_get_memorg() - Extract memory organization info from a NAND device + * @nand: NAND device + * + * This can be used by the upper layer to fill the memorg info before calling + * nanddev_init(). + * + * Return: the memorg object embedded in the NAND device. + */ +static inline struct nand_memory_organization * +nanddev_get_memorg(struct nand_device *nand) +{ + return &nand->memorg; +} + +int nanddev_init(struct nand_device *nand, const struct nand_ops *ops, + struct module *owner); +void nanddev_cleanup(struct nand_device *nand); + +/** + * nanddev_register() - Register a NAND device + * @nand: NAND device + * + * Register a NAND device. + * This function is just a wrapper around mtd_device_register() + * registering the MTD device embedded in @nand. + * + * Return: 0 in case of success, a negative error code otherwise. + */ +static inline int nanddev_register(struct nand_device *nand) +{ + return mtd_device_register(nand->mtd, NULL, 0); +} + +/** + * nanddev_unregister() - Unregister a NAND device + * @nand: NAND device + * + * Unregister a NAND device. + * This function is just a wrapper around mtd_device_unregister() + * unregistering the MTD device embedded in @nand. + * + * Return: 0 in case of success, a negative error code otherwise. + */ +static inline int nanddev_unregister(struct nand_device *nand) +{ + return mtd_device_unregister(nand->mtd); +} + +/** + * nanddev_set_of_node() - Attach a DT node to a NAND device + * @nand: NAND device + * @np: DT node + * + * Attach a DT node to a NAND device. + */ +static inline void nanddev_set_of_node(struct nand_device *nand, + const struct device_node *np) +{ + mtd_set_of_node(nand->mtd, np); +} + +/** + * nanddev_get_of_node() - Retrieve the DT node attached to a NAND device + * @nand: NAND device + * + * Return: the DT node attached to @nand. + */ +static inline const struct device_node *nanddev_get_of_node(struct nand_device *nand) +{ + return mtd_get_of_node(nand->mtd); +} + +/** + * nanddev_offs_to_pos() - Convert an absolute NAND offset into a NAND position + * @nand: NAND device + * @offs: absolute NAND offset (usually passed by the MTD layer) + * @pos: a NAND position object to fill in + * + * Converts @offs into a nand_pos representation. + * + * Return: the offset within the NAND page pointed by @pos. + */ +static inline unsigned int nanddev_offs_to_pos(struct nand_device *nand, + loff_t offs, + struct nand_pos *pos) +{ + unsigned int pageoffs; + u64 tmp = offs; + + pageoffs = do_div(tmp, nand->memorg.pagesize); + pos->page = do_div(tmp, nand->memorg.pages_per_eraseblock); + pos->eraseblock = do_div(tmp, nand->memorg.eraseblocks_per_lun); + pos->plane = pos->eraseblock % nand->memorg.planes_per_lun; + pos->lun = do_div(tmp, nand->memorg.luns_per_target); + pos->target = tmp; + + return pageoffs; +} + +/** + * nanddev_pos_cmp() - Compare two NAND positions + * @a: First NAND position + * @b: Second NAND position + * + * Compares two NAND positions. + * + * Return: -1 if @a < @b, 0 if @a == @b and 1 if @a > @b. + */ +static inline int nanddev_pos_cmp(const struct nand_pos *a, + const struct nand_pos *b) +{ + if (a->target != b->target) + return a->target < b->target ? -1 : 1; + + if (a->lun != b->lun) + return a->lun < b->lun ? -1 : 1; + + if (a->eraseblock != b->eraseblock) + return a->eraseblock < b->eraseblock ? -1 : 1; + + if (a->page != b->page) + return a->page < b->page ? -1 : 1; + + return 0; +} + +/** + * nanddev_pos_to_offs() - Convert a NAND position into an absolute offset + * @nand: NAND device + * @pos: the NAND position to convert + * + * Converts @pos NAND position into an absolute offset. + * + * Return: the absolute offset. Note that @pos points to the beginning of a + * page, if one wants to point to a specific offset within this page + * the returned offset has to be adjusted manually. + */ +static inline loff_t nanddev_pos_to_offs(struct nand_device *nand, + const struct nand_pos *pos) +{ + unsigned int npages; + + npages = pos->page + + ((pos->eraseblock + + (pos->lun + + (pos->target * nand->memorg.luns_per_target)) * + nand->memorg.eraseblocks_per_lun) * + nand->memorg.pages_per_eraseblock); + + return (loff_t)npages * nand->memorg.pagesize; +} + +/** + * nanddev_pos_to_row() - Extract a row address from a NAND position + * @nand: NAND device + * @pos: the position to convert + * + * Converts a NAND position into a row address that can then be passed to the + * device. + * + * Return: the row address extracted from @pos. + */ +static inline unsigned int nanddev_pos_to_row(struct nand_device *nand, + const struct nand_pos *pos) +{ + return (pos->lun << nand->rowconv.lun_addr_shift) | + (pos->eraseblock << nand->rowconv.eraseblock_addr_shift) | + pos->page; +} + +/** + * nanddev_pos_next_target() - Move a position to the next target/die + * @nand: NAND device + * @pos: the position to update + * + * Updates @pos to point to the start of the next target/die. Useful when you + * want to iterate over all targets/dies of a NAND device. + */ +static inline void nanddev_pos_next_target(struct nand_device *nand, + struct nand_pos *pos) +{ + pos->page = 0; + pos->plane = 0; + pos->eraseblock = 0; + pos->lun = 0; + pos->target++; +} + +/** + * nanddev_pos_next_lun() - Move a position to the next LUN + * @nand: NAND device + * @pos: the position to update + * + * Updates @pos to point to the start of the next LUN. Useful when you want to + * iterate over all LUNs of a NAND device. + */ +static inline void nanddev_pos_next_lun(struct nand_device *nand, + struct nand_pos *pos) +{ + if (pos->lun >= nand->memorg.luns_per_target - 1) + return nanddev_pos_next_target(nand, pos); + + pos->lun++; + pos->page = 0; + pos->plane = 0; + pos->eraseblock = 0; +} + +/** + * nanddev_pos_next_eraseblock() - Move a position to the next eraseblock + * @nand: NAND device + * @pos: the position to update + * + * Updates @pos to point to the start of the next eraseblock. Useful when you + * want to iterate over all eraseblocks of a NAND device. + */ +static inline void nanddev_pos_next_eraseblock(struct nand_device *nand, + struct nand_pos *pos) +{ + if (pos->eraseblock >= nand->memorg.eraseblocks_per_lun - 1) + return nanddev_pos_next_lun(nand, pos); + + pos->eraseblock++; + pos->page = 0; + pos->plane = pos->eraseblock % nand->memorg.planes_per_lun; +} + +/** + * nanddev_pos_next_eraseblock() - Move a position to the next page + * @nand: NAND device + * @pos: the position to update + * + * Updates @pos to point to the start of the next page. Useful when you want to + * iterate over all pages of a NAND device. + */ +static inline void nanddev_pos_next_page(struct nand_device *nand, + struct nand_pos *pos) +{ + if (pos->page >= nand->memorg.pages_per_eraseblock - 1) + return nanddev_pos_next_eraseblock(nand, pos); + + pos->page++; +} + +/** + * nand_io_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. + */ +static inline void nanddev_io_iter_init(struct nand_device *nand, + loff_t offs, struct mtd_oob_ops *req, + struct nand_io_iter *iter) +{ + struct mtd_info *mtd = nanddev_to_mtd(nand); + + iter->req.dataoffs = nanddev_offs_to_pos(nand, offs, &iter->req.pos); + iter->req.ooboffs = req->ooboffs; + iter->oobbytes_per_page = mtd_oobavail(mtd, req); + iter->dataleft = req->len; + iter->oobleft = req->ooblen; + iter->req.databuf.in = req->datbuf; + iter->req.datalen = min_t(unsigned int, + nand->memorg.pagesize - iter->req.dataoffs, + iter->dataleft); + iter->req.oobbuf.in = req->oobbuf; + iter->req.ooblen = min_t(unsigned int, + iter->oobbytes_per_page - iter->req.ooboffs, + iter->oobleft); +} + +/** + * nand_io_iter_next_page - Move to the next page + * @nand: NAND device + * @iter: NAND I/O iterator + * + * Updates the @iter to point to the next page. + */ +static inline void nanddev_io_iter_next_page(struct nand_device *nand, + struct nand_io_iter *iter) +{ + nanddev_pos_next_page(nand, &iter->req.pos); + iter->dataleft -= iter->req.datalen; + iter->req.databuf.in += iter->req.datalen; + iter->oobleft -= iter->req.ooblen; + iter->req.oobbuf.in += iter->req.ooblen; + iter->req.dataoffs = 0; + iter->req.ooboffs = 0; + iter->req.datalen = min_t(unsigned int, nand->memorg.pagesize, + iter->dataleft); + iter->req.ooblen = min_t(unsigned int, iter->oobbytes_per_page, + iter->oobleft); +} + +/** + * nand_io_iter_end - Should end iteration or not + * @nand: NAND device + * @iter: NAND I/O iterator + * + * Check whether @iter has reached the end of the NAND portion it was asked to + * iterate on or not. + * + * Return: true if @iter has reached the end of the iteration request, false + * otherwise. + */ +static inline bool nanddev_io_iter_end(struct nand_device *nand, + const struct nand_io_iter *iter) +{ + if (iter->dataleft || iter->oobleft) + return false; + + return true; +} + +/** + * nand_io_for_each_page - Iterate over all NAND pages contained in an MTD I/O + * request + * @nand: NAND device + * @start: start address to read/write from + * @req: MTD I/O request + * @iter: NAND I/O iterator + * + * Should be used for iterate 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); \ + !nanddev_io_iter_end(nand, iter); \ + nanddev_io_iter_next_page(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_erase(struct nand_device *nand, const struct nand_pos *pos); +int nanddev_markbad(struct nand_device *nand, const struct nand_pos *pos); + +/* BBT related functions */ +enum nand_bbt_block_status { + NAND_BBT_BLOCK_STATUS_UNKNOWN, + NAND_BBT_BLOCK_GOOD, + NAND_BBT_BLOCK_WORN, + NAND_BBT_BLOCK_RESERVED, + NAND_BBT_BLOCK_FACTORY_BAD, + NAND_BBT_BLOCK_NUM_STATUS, +}; + +int nanddev_bbt_init(struct nand_device *nand); +void nanddev_bbt_cleanup(struct nand_device *nand); +int nanddev_bbt_update(struct nand_device *nand); +int nanddev_bbt_get_block_status(const struct nand_device *nand, + unsigned int entry); +int nanddev_bbt_set_block_status(struct nand_device *nand, unsigned int entry, + enum nand_bbt_block_status status); +int nanddev_bbt_markbad(struct nand_device *nand, unsigned int block); + +/** + * nanddev_bbt_pos_to_entry() - Convert a NAND position into a BBT entry + * @nand: NAND device + * @pos: the NAND position we want to get BBT entry for + * + * Return the BBT entry used to store information about the eraseblock pointed + * by @pos. + * + * Return: the BBT entry storing information about eraseblock pointed by @pos. + */ +static inline unsigned int nanddev_bbt_pos_to_entry(struct nand_device *nand, + const struct nand_pos *pos) +{ + return pos->eraseblock + + ((pos->lun + (pos->target * nand->memorg.luns_per_target)) * + nand->memorg.eraseblocks_per_lun); +} + +/** + * nanddev_bbt_is_initialized() - Check if the BBT has been initialized + * @nand: NAND device + * + * Return: true if the BBT has been initialized, false otherwise. + */ +static inline bool nanddev_bbt_is_initialized(struct nand_device *nand) +{ + return !!nand->bbt.cache; +} + +/* MTD -> NAND helper functions. */ +int nanddev_mtd_erase(struct mtd_info *mtd, struct erase_info *einfo); + +#endif /* __LINUX_MTD_NAND_H */ -- cgit From f86787280b37e381f8d82f48583434d62dd16e27 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 16 Aug 2018 17:30:10 +0200 Subject: mtd: nand: Pass mode information to nand_page_io_req The NAND sub-layers are likely to need the MTD_OPS_XXX mode information in order to decide if they should enable/disable ECC or how they should place the OOB bytes in the provided OOB buffer. Add a field to nand_page_io_req to pass this information. Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal --- include/linux/mtd/nand.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include') diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index ada7af4a41..13e8dd1103 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -86,6 +86,7 @@ struct nand_pos { * @ooboffs: the OOB offset within the page * @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 * * 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 @@ -106,6 +107,7 @@ struct nand_page_io_req { const void *out; void *in; } oobbuf; + int mode; }; /** @@ -599,6 +601,7 @@ static inline void nanddev_io_iter_init(struct nand_device *nand, { struct mtd_info *mtd = nanddev_to_mtd(nand); + iter->req.mode = req->mode; iter->req.dataoffs = nanddev_offs_to_pos(nand, offs, &iter->req.pos); iter->req.ooboffs = req->ooboffs; iter->oobbytes_per_page = mtd_oobavail(mtd, req); -- cgit From d13f5b254a43e292814a618f60a2696ba01267a7 Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 16 Aug 2018 17:30:11 +0200 Subject: spi: Extend the core to ease integration of SPI memory controllers Some controllers are exposing high-level interfaces to access various kind of SPI memories. Unfortunately they do not fit in the current spi_controller model and usually have drivers placed in drivers/mtd/spi-nor which are only supporting SPI NORs and not SPI memories in general. This is an attempt at defining a SPI memory interface which works for all kinds of SPI memories (NORs, NANDs, SRAMs). Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal Acked-by: Jagan Teki --- include/spi-mem.h | 258 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ include/spi.h | 11 +++ 2 files changed, 269 insertions(+) create mode 100644 include/spi-mem.h (limited to 'include') diff --git a/include/spi-mem.h b/include/spi-mem.h new file mode 100644 index 0000000000..36814efa86 --- /dev/null +++ b/include/spi-mem.h @@ -0,0 +1,258 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2018 Exceet Electronics GmbH + * Copyright (C) 2018 Bootlin + * + * Author: + * Peter Pan + * Boris Brezillon + */ + +#ifndef __UBOOT_SPI_MEM_H +#define __UBOOT_SPI_MEM_H + +#include +#include +#include +#include + +#define SPI_MEM_OP_CMD(__opcode, __buswidth) \ + { \ + .buswidth = __buswidth, \ + .opcode = __opcode, \ + } + +#define SPI_MEM_OP_ADDR(__nbytes, __val, __buswidth) \ + { \ + .nbytes = __nbytes, \ + .val = __val, \ + .buswidth = __buswidth, \ + } + +#define SPI_MEM_OP_NO_ADDR { } + +#define SPI_MEM_OP_DUMMY(__nbytes, __buswidth) \ + { \ + .nbytes = __nbytes, \ + .buswidth = __buswidth, \ + } + +#define SPI_MEM_OP_NO_DUMMY { } + +#define SPI_MEM_OP_DATA_IN(__nbytes, __buf, __buswidth) \ + { \ + .dir = SPI_MEM_DATA_IN, \ + .nbytes = __nbytes, \ + .buf.in = __buf, \ + .buswidth = __buswidth, \ + } + +#define SPI_MEM_OP_DATA_OUT(__nbytes, __buf, __buswidth) \ + { \ + .dir = SPI_MEM_DATA_OUT, \ + .nbytes = __nbytes, \ + .buf.out = __buf, \ + .buswidth = __buswidth, \ + } + +#define SPI_MEM_OP_NO_DATA { } + +/** + * enum spi_mem_data_dir - describes the direction of a SPI memory data + * transfer from the controller perspective + * @SPI_MEM_DATA_IN: data coming from the SPI memory + * @SPI_MEM_DATA_OUT: data sent the SPI memory + */ +enum spi_mem_data_dir { + SPI_MEM_DATA_IN, + SPI_MEM_DATA_OUT, +}; + +/** + * struct spi_mem_op - describes a SPI memory operation + * @cmd.buswidth: number of IO lines used to transmit the command + * @cmd.opcode: operation opcode + * @addr.nbytes: number of address bytes to send. Can be zero if the operation + * does not need to send an address + * @addr.buswidth: number of IO lines used to transmit the address cycles + * @addr.val: address value. This value is always sent MSB first on the bus. + * Note that only @addr.nbytes are taken into account in this + * address value, so users should make sure the value fits in the + * assigned number of bytes. + * @dummy.nbytes: number of dummy bytes to send after an opcode or address. Can + * be zero if the operation does not require dummy bytes + * @dummy.buswidth: number of IO lanes used to transmit the dummy bytes + * @data.buswidth: number of IO lanes used to send/receive the data + * @data.dir: direction of the transfer + * @data.buf.in: input buffer + * @data.buf.out: output buffer + */ +struct spi_mem_op { + struct { + u8 buswidth; + u8 opcode; + } cmd; + + struct { + u8 nbytes; + u8 buswidth; + u64 val; + } addr; + + struct { + u8 nbytes; + u8 buswidth; + } dummy; + + struct { + u8 buswidth; + enum spi_mem_data_dir dir; + unsigned int nbytes; + /* buf.{in,out} must be DMA-able. */ + union { + void *in; + const void *out; + } buf; + } data; +}; + +#define SPI_MEM_OP(__cmd, __addr, __dummy, __data) \ + { \ + .cmd = __cmd, \ + .addr = __addr, \ + .dummy = __dummy, \ + .data = __data, \ + } + +#ifndef __UBOOT__ +/** + * struct spi_mem - describes a SPI memory device + * @spi: the underlying SPI device + * @drvpriv: spi_mem_driver private data + * + * Extra information that describe the SPI memory device and may be needed by + * the controller to properly handle this device should be placed here. + * + * One example would be the device size since some controller expose their SPI + * mem devices through a io-mapped region. + */ +struct spi_mem { + struct udevice *dev; + void *drvpriv; +}; + +/** + * struct spi_mem_set_drvdata() - attach driver private data to a SPI mem + * device + * @mem: memory device + * @data: data to attach to the memory device + */ +static inline void spi_mem_set_drvdata(struct spi_mem *mem, void *data) +{ + mem->drvpriv = data; +} + +/** + * struct spi_mem_get_drvdata() - get driver private data attached to a SPI mem + * device + * @mem: memory device + * + * Return: the data attached to the mem device. + */ +static inline void *spi_mem_get_drvdata(struct spi_mem *mem) +{ + return mem->drvpriv; +} +#endif /* __UBOOT__ */ + +/** + * struct spi_controller_mem_ops - SPI memory operations + * @adjust_op_size: shrink the data xfer of an operation to match controller's + * limitations (can be alignment of max RX/TX size + * limitations) + * @supports_op: check if an operation is supported by the controller + * @exec_op: execute a SPI memory operation + * + * This interface should be implemented by SPI controllers providing an + * high-level interface to execute SPI memory operation, which is usually the + * case for QSPI controllers. + */ +struct spi_controller_mem_ops { + int (*adjust_op_size)(struct spi_slave *slave, struct spi_mem_op *op); + bool (*supports_op)(struct spi_slave *slave, + const struct spi_mem_op *op); + int (*exec_op)(struct spi_slave *slave, + const struct spi_mem_op *op); +}; + +#ifndef __UBOOT__ +/** + * struct spi_mem_driver - SPI memory driver + * @spidrv: inherit from a SPI driver + * @probe: probe a SPI memory. Usually where detection/initialization takes + * place + * @remove: remove a SPI memory + * @shutdown: take appropriate action when the system is shutdown + * + * This is just a thin wrapper around a spi_driver. The core takes care of + * allocating the spi_mem object and forwarding the probe/remove/shutdown + * request to the spi_mem_driver. The reason we use this wrapper is because + * we might have to stuff more information into the spi_mem struct to let + * SPI controllers know more about the SPI memory they interact with, and + * having this intermediate layer allows us to do that without adding more + * useless fields to the spi_device object. + */ +struct spi_mem_driver { + struct spi_driver spidrv; + int (*probe)(struct spi_mem *mem); + int (*remove)(struct spi_mem *mem); + void (*shutdown)(struct spi_mem *mem); +}; + +#if IS_ENABLED(CONFIG_SPI_MEM) +int spi_controller_dma_map_mem_op_data(struct spi_controller *ctlr, + const struct spi_mem_op *op, + struct sg_table *sg); + +void spi_controller_dma_unmap_mem_op_data(struct spi_controller *ctlr, + const struct spi_mem_op *op, + struct sg_table *sg); +#else +static inline int +spi_controller_dma_map_mem_op_data(struct spi_controller *ctlr, + const struct spi_mem_op *op, + struct sg_table *sg) +{ + return -ENOTSUPP; +} + +static inline void +spi_controller_dma_unmap_mem_op_data(struct spi_controller *ctlr, + const struct spi_mem_op *op, + struct sg_table *sg) +{ +} +#endif /* CONFIG_SPI_MEM */ +#endif /* __UBOOT__ */ + +int spi_mem_adjust_op_size(struct spi_slave *slave, struct spi_mem_op *op); + +bool spi_mem_supports_op(struct spi_slave *slave, const struct spi_mem_op *op); + +int spi_mem_exec_op(struct spi_slave *slave, const struct spi_mem_op *op); + +#ifndef __UBOOT__ +int spi_mem_driver_register_with_owner(struct spi_mem_driver *drv, + struct module *owner); + +void spi_mem_driver_unregister(struct spi_mem_driver *drv); + +#define spi_mem_driver_register(__drv) \ + spi_mem_driver_register_with_owner(__drv, THIS_MODULE) + +#define module_spi_mem_driver(__drv) \ + module_driver(__drv, spi_mem_driver_register, \ + spi_mem_driver_unregister) +#endif + +#endif /* __LINUX_SPI_MEM_H */ diff --git a/include/spi.h b/include/spi.h index 9754c53aa1..938627bc01 100644 --- a/include/spi.h +++ b/include/spi.h @@ -9,6 +9,8 @@ #ifndef _SPI_H_ #define _SPI_H_ +#include + /* SPI mode flags */ #define SPI_CPHA BIT(0) /* clock phase */ #define SPI_CPOL BIT(1) /* clock polarity */ @@ -402,6 +404,15 @@ struct dm_spi_ops { int (*xfer)(struct udevice *dev, unsigned int bitlen, const void *dout, void *din, unsigned long flags); + /** + * Optimized handlers for SPI memory-like operations. + * + * Optimized/dedicated operations for interactions with SPI memory. This + * field is optional and should only be implemented if the controller + * has native support for memory like operations. + */ + const struct spi_controller_mem_ops *mem_ops; + /** * Set transfer speed. * This sets a new speed to be applied for next spi_xfer(). -- cgit From 0a6d6bae03864938f073cc114992c40f2338a155 Mon Sep 17 00:00:00 2001 From: Peter Pan Date: Thu, 16 Aug 2018 17:30:12 +0200 Subject: mtd: nand: Add core infrastructure to support SPI NANDs Add a SPI NAND framework based on the generic NAND framework and the spi-mem infrastructure. In its current state, this framework supports the following features: - single/dual/quad IO modes - on-die ECC Signed-off-by: Peter Pan Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal Acked-by: Jagan Teki --- include/linux/mtd/spinand.h | 427 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 427 insertions(+) create mode 100644 include/linux/mtd/spinand.h (limited to 'include') diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h new file mode 100644 index 0000000000..ad59fc01ef --- /dev/null +++ b/include/linux/mtd/spinand.h @@ -0,0 +1,427 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2016-2017 Micron Technology, Inc. + * + * Authors: + * Peter Pan + */ +#ifndef __LINUX_MTD_SPINAND_H +#define __LINUX_MTD_SPINAND_H + +#ifndef __UBOOT__ +#include +#include +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#endif + +/** + * Standard SPI NAND flash operations + */ + +#define SPINAND_RESET_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) \ + 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(ndummy, buf, len) \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0x9f, 1), \ + SPI_MEM_OP_NO_ADDR, \ + SPI_MEM_OP_DUMMY(ndummy, 1), \ + SPI_MEM_OP_DATA_IN(len, buf, 1)) + +#define SPINAND_SET_FEATURE_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) \ + 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) \ + 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) \ + 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), \ + SPI_MEM_OP_ADDR(2, addr, 1), \ + SPI_MEM_OP_DUMMY(ndummy, 1), \ + SPI_MEM_OP_DATA_IN(len, buf, 1)) + +#define SPINAND_PAGE_READ_FROM_CACHE_X2_OP(addr, ndummy, buf, len) \ + 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)) + +#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_DUALIO_OP(addr, ndummy, buf, len) \ + 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)) + +#define SPINAND_PAGE_READ_FROM_CACHE_QUADIO_OP(addr, ndummy, buf, len) \ + 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)) + +#define SPINAND_PROG_EXEC_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) \ + 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) \ + 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)) + +/** + * Standard SPI NAND flash commands + */ +#define SPINAND_CMD_PROG_LOAD_X4 0x32 +#define SPINAND_CMD_PROG_LOAD_RDM_DATA_X4 0x34 + +/* feature register */ +#define REG_BLOCK_LOCK 0xa0 +#define BL_ALL_UNLOCKED 0x00 + +/* configuration register */ +#define REG_CFG 0xb0 +#define CFG_OTP_ENABLE BIT(6) +#define CFG_ECC_ENABLE BIT(4) +#define CFG_QUAD_ENABLE BIT(0) + +/* status register */ +#define REG_STATUS 0xc0 +#define STATUS_BUSY BIT(0) +#define STATUS_ERASE_FAILED BIT(2) +#define STATUS_PROG_FAILED BIT(3) +#define STATUS_ECC_MASK GENMASK(5, 4) +#define STATUS_ECC_NO_BITFLIPS (0 << 4) +#define STATUS_ECC_HAS_BITFLIPS (1 << 4) +#define STATUS_ECC_UNCOR_ERROR (2 << 4) + +struct spinand_op; +struct spinand_device; + +#define SPINAND_MAX_ID_LEN 4 + +/** + * struct spinand_id - SPI NAND id structure + * @data: buffer containing the id bytes. Currently 4 bytes large, but can + * be extended if required + * @len: ID length + * + * struct_spinand_id->data contains all bytes returned after a READ_ID command, + * including dummy bytes if the chip does not emit ID bytes right after the + * READ_ID command. The responsibility to extract real ID bytes is left to + * struct_manufacurer_ops->detect(). + */ +struct spinand_id { + u8 data[SPINAND_MAX_ID_LEN]; + int len; +}; + +/** + * struct manufacurer_ops - SPI NAND manufacturer specific operations + * @detect: detect a SPI NAND device. Every time a SPI NAND device is probed + * the core calls the struct_manufacurer_ops->detect() hook of each + * registered manufacturer until one of them return 1. Note that + * the first thing to check in this hook is that the manufacturer ID + * in struct_spinand_device->id matches the manufacturer whose + * ->detect() hook has been called. Should return 1 if there's a + * match, 0 if the manufacturer ID does not match and a negative + * error code otherwise. When true is returned, the core assumes + * that properties of the NAND chip (spinand->base.memorg and + * spinand->base.eccreq) have been filled + * @init: initialize a SPI NAND device + * @cleanup: cleanup a SPI NAND device + * + * Each SPI NAND manufacturer driver should implement this interface so that + * NAND chips coming from this vendor can be detected and initialized properly. + */ +struct spinand_manufacturer_ops { + int (*detect)(struct spinand_device *spinand); + int (*init)(struct spinand_device *spinand); + void (*cleanup)(struct spinand_device *spinand); +}; + +/** + * struct spinand_manufacturer - SPI NAND manufacturer instance + * @id: manufacturer ID + * @name: manufacturer name + * @ops: manufacturer operations + */ +struct spinand_manufacturer { + u8 id; + char *name; + const struct spinand_manufacturer_ops *ops; +}; + +/** + * struct spinand_op_variants - SPI NAND operation variants + * @ops: the list of variants for a given operation + * @nops: the number of variants + * + * Some operations like read-from-cache/write-to-cache have several variants + * depending on the number of IO lines you use to transfer data or address + * cycles. This structure is a way to describe the different variants supported + * by a chip and let the core pick the best one based on the SPI mem controller + * capabilities. + */ +struct spinand_op_variants { + const struct spi_mem_op *ops; + unsigned int nops; +}; + +#define SPINAND_OP_VARIANTS(name, ...) \ + const struct spinand_op_variants name = { \ + .ops = (struct spi_mem_op[]) { __VA_ARGS__ }, \ + .nops = sizeof((struct spi_mem_op[]){ __VA_ARGS__ }) / \ + sizeof(struct spi_mem_op), \ + } + +/** + * spinand_ecc_info - description of the on-die ECC implemented by a SPI NAND + * chip + * @get_status: get the ECC status. Should return a positive number encoding + * the number of corrected bitflips if correction was possible or + * -EBADMSG if there are uncorrectable errors. I can also return + * other negative error codes if the error is not caused by + * uncorrectable bitflips + * @ooblayout: the OOB layout used by the on-die ECC implementation + */ +struct spinand_ecc_info { + int (*get_status)(struct spinand_device *spinand, u8 status); + const struct mtd_ooblayout_ops *ooblayout; +}; + +#define SPINAND_HAS_QE_BIT BIT(0) + +/** + * struct spinand_info - Structure used to describe SPI NAND chips + * @model: model name + * @devid: device ID + * @flags: OR-ing of the SPINAND_XXX flags + * @memorg: memory organization + * @eccreq: ECC requirements + * @eccinfo: on-die ECC info + * @op_variants: operations variants + * @op_variants.read_cache: variants of the read-cache operation + * @op_variants.write_cache: variants of the write-cache operation + * @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 + * + * Each SPI NAND manufacturer driver should have a spinand_info table + * describing all the chips supported by the driver. + */ +struct spinand_info { + const char *model; + u8 devid; + u32 flags; + struct nand_memory_organization memorg; + struct nand_ecc_req eccreq; + struct spinand_ecc_info eccinfo; + struct { + const struct spinand_op_variants *read_cache; + const struct spinand_op_variants *write_cache; + const struct spinand_op_variants *update_cache; + } op_variants; + int (*select_target)(struct spinand_device *spinand, + unsigned int target); +}; + +#define SPINAND_INFO_OP_VARIANTS(__read, __write, __update) \ + { \ + .read_cache = __read, \ + .write_cache = __write, \ + .update_cache = __update, \ + } + +#define SPINAND_ECCINFO(__ooblayout, __get_status) \ + .eccinfo = { \ + .ooblayout = __ooblayout, \ + .get_status = __get_status, \ + } + +#define SPINAND_SELECT_TARGET(__func) \ + .select_target = __func, + +#define SPINAND_INFO(__model, __id, __memorg, __eccreq, __op_variants, \ + __flags, ...) \ + { \ + .model = __model, \ + .devid = __id, \ + .memorg = __memorg, \ + .eccreq = __eccreq, \ + .op_variants = __op_variants, \ + .flags = __flags, \ + __VA_ARGS__ \ + } + +/** + * struct spinand_device - SPI NAND device instance + * @base: NAND device instance + * @slave: pointer to the SPI slave object + * @lock: lock used to serialize accesses to the NAND + * @id: NAND ID as returned by READ_ID + * @flags: NAND flags + * @op_templates: various SPI mem op templates + * @op_templates.read_cache: read cache op template + * @op_templates.write_cache: write cache op template + * @op_templates.update_cache: update cache op template + * @select_target: select a specific target/die. Usually called before sending + * a command addressing a page or an eraseblock embedded in + * this die. Only required if your chip exposes several dies + * @cur_target: currently selected target/die + * @eccinfo: on-die ECC information + * @cfg_cache: config register cache. One entry per die + * @databuf: bounce buffer for data + * @oobbuf: bounce buffer for OOB data + * @scratchbuf: buffer used for everything but page accesses. This is needed + * because the spi-mem interface explicitly requests that buffers + * passed in spi_mem_op be DMA-able, so we can't based the bufs on + * the stack + * @manufacturer: SPI NAND manufacturer information + * @priv: manufacturer private data + */ +struct spinand_device { + struct nand_device base; +#ifndef __UBOOT__ + struct spi_mem *spimem; + struct mutex lock; +#else + struct spi_slave *slave; +#endif + struct spinand_id id; + u32 flags; + + struct { + const struct spi_mem_op *read_cache; + const struct spi_mem_op *write_cache; + const struct spi_mem_op *update_cache; + } op_templates; + + int (*select_target)(struct spinand_device *spinand, + unsigned int target); + unsigned int cur_target; + + struct spinand_ecc_info eccinfo; + + u8 *cfg_cache; + u8 *databuf; + u8 *oobbuf; + u8 *scratchbuf; + const struct spinand_manufacturer *manufacturer; + void *priv; +}; + +/** + * mtd_to_spinand() - Get the SPI NAND device attached to an MTD instance + * @mtd: MTD instance + * + * Return: the SPI NAND device attached to @mtd. + */ +static inline struct spinand_device *mtd_to_spinand(struct mtd_info *mtd) +{ + return container_of(mtd_to_nanddev(mtd), struct spinand_device, base); +} + +/** + * spinand_to_mtd() - Get the MTD device embedded in a SPI NAND device + * @spinand: SPI NAND device + * + * Return: the MTD device embedded in @spinand. + */ +static inline struct mtd_info *spinand_to_mtd(struct spinand_device *spinand) +{ + return nanddev_to_mtd(&spinand->base); +} + +/** + * nand_to_spinand() - Get the SPI NAND device embedding an NAND object + * @nand: NAND object + * + * Return: the SPI NAND device embedding @nand. + */ +static inline struct spinand_device *nand_to_spinand(struct nand_device *nand) +{ + return container_of(nand, struct spinand_device, base); +} + +/** + * spinand_to_nand() - Get the NAND device embedded in a SPI NAND object + * @spinand: SPI NAND device + * + * Return: the NAND device embedded in @spinand. + */ +static inline struct nand_device * +spinand_to_nand(struct spinand_device *spinand) +{ + return &spinand->base; +} + +/** + * spinand_set_of_node - Attach a DT node to a SPI NAND device + * @spinand: SPI NAND device + * @np: DT node + * + * Attach a DT node to a SPI NAND device. + */ +static inline void spinand_set_of_node(struct spinand_device *spinand, + const struct device_node *np) +{ + nanddev_set_of_node(&spinand->base, np); +} + +int spinand_match_and_init(struct spinand_device *dev, + const struct spinand_info *table, + unsigned int table_size, u8 devid); + +int spinand_upd_cfg(struct spinand_device *spinand, u8 mask, u8 val); +int spinand_select_target(struct spinand_device *spinand, unsigned int target); + +#endif /* __LINUX_MTD_SPINAND_H */ -- cgit From 883d8778ae177172c0a53c018faa39e61f30dea3 Mon Sep 17 00:00:00 2001 From: Peter Pan Date: Thu, 16 Aug 2018 17:30:13 +0200 Subject: mtd: spinand: Add initial support for Micron MT29F2G01ABAGD Add a basic driver for Micron SPI NANDs. Only one device is supported right now, but the driver will be extended to support more devices afterwards. Signed-off-by: Peter Pan Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal Acked-by: Jagan Teki --- include/linux/mtd/spinand.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include') diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index ad59fc01ef..5302d38de8 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -200,6 +200,9 @@ struct spinand_manufacturer { const struct spinand_manufacturer_ops *ops; }; +/* SPI NAND manufacturers */ +extern const struct spinand_manufacturer micron_spinand_manufacturer; + /** * struct spinand_op_variants - SPI NAND operation variants * @ops: the list of variants for a given operation -- cgit From 3181c0a622d35bd8e6d4407458e7204d4df5a8c1 Mon Sep 17 00:00:00 2001 From: Frieder Schrempf Date: Thu, 16 Aug 2018 17:30:14 +0200 Subject: mtd: spinand: Add initial support for Winbond W25M02GV Add support for the W25M02GV chip. Signed-off-by: Frieder Schrempf Signed-off-by: Boris Brezillon Signed-off-by: Miquel Raynal Acked-by: Jagan Teki --- include/linux/mtd/spinand.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index 5302d38de8..d40a7c8f4e 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -202,6 +202,7 @@ struct spinand_manufacturer { /* SPI NAND manufacturers */ extern const struct spinand_manufacturer micron_spinand_manufacturer; +extern const struct spinand_manufacturer winbond_spinand_manufacturer; /** * struct spinand_op_variants - SPI NAND operation variants -- cgit From 6f041ccabb03bea16c2f21f3254dc9c1cb38425c Mon Sep 17 00:00:00 2001 From: Boris Brezillon Date: Thu, 16 Aug 2018 17:30:15 +0200 Subject: mtd: spinand: Add initial support for the MX35LF1GE4AB chip Add minimal support for the MX35LF1GE4AB SPI NAND chip. Signed-off-by: Boris Brezillon Acked-by: Jagan Teki --- include/linux/mtd/spinand.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index d40a7c8f4e..8c9c756179 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -201,6 +201,7 @@ struct spinand_manufacturer { }; /* SPI NAND manufacturers */ +extern const struct spinand_manufacturer macronix_spinand_manufacturer; extern const struct spinand_manufacturer micron_spinand_manufacturer; extern const struct spinand_manufacturer winbond_spinand_manufacturer; -- cgit From 00ac922db4085982011438e9471e0fcacca2e8fc Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Thu, 6 Sep 2018 09:08:45 +0200 Subject: cmd: mtdparts: accept spi-nand devices Let spi-nand devices be recognized by mtdparts. This is superfluous but a full mtdparts rework would be very time-consuming. Signed-off-by: Miquel Raynal Acked-by: Jagan Teki Reviewed-by: Boris Brezillon Reviewed-by: Stefan Roese --- include/jffs2/load_kernel.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/jffs2/load_kernel.h b/include/jffs2/load_kernel.h index 1ddff062ad..9346d7ee9f 100644 --- a/include/jffs2/load_kernel.h +++ b/include/jffs2/load_kernel.h @@ -15,9 +15,12 @@ #define MTD_DEV_TYPE_NOR 0x0001 #define MTD_DEV_TYPE_NAND 0x0002 #define MTD_DEV_TYPE_ONENAND 0x0004 +#define MTD_DEV_TYPE_SPINAND 0x0008 -#define MTD_DEV_TYPE(type) ((type == MTD_DEV_TYPE_NAND) ? "nand" : \ - (type == MTD_DEV_TYPE_ONENAND) ? "onenand" : "nor") +#define MTD_DEV_TYPE(type) (type == MTD_DEV_TYPE_NAND ? "nand" : \ + (type == MTD_DEV_TYPE_NOR ? "nor" : \ + (type == MTD_DEV_TYPE_ONENAND ? "onenand" : \ + "spi-nand"))) \ struct mtd_device { struct list_head link; -- cgit From 739def7253c4b782bcb9cc816edf3b2f2aeafb4e Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Sat, 29 Sep 2018 12:58:23 +0200 Subject: dm: drop unused helper in MTD header include/mtd.h might be included by files even if CONFIG_DM is not enabled. In this case, the call to dev_get_uclass_priv() would trigger a build error. Because this helper has no user, let's drop it off. Signed-off-by: Miquel Raynal Reviewed-by: Jagan Teki --- include/mtd.h | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'include') diff --git a/include/mtd.h b/include/mtd.h index 548e7f191b..9e5c807294 100644 --- a/include/mtd.h +++ b/include/mtd.h @@ -8,15 +8,4 @@ #include -/* - * Get mtd_info structure of the dev, which is stored as uclass private. - * - * @dev: The MTD device - * @return: pointer to mtd_info, NULL on error - */ -static inline struct mtd_info *mtd_get_info(struct udevice *dev) -{ - return dev_get_uclass_priv(dev); -} - #endif /* _MTD_H_ */ -- cgit From e9f62db64b55fa3451731781cc3b1d3bdd271d54 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Sat, 29 Sep 2018 12:58:24 +0200 Subject: mtd: uclass: add probe function The user might want to trigger the probe of any MTD device, export these functions so they can be called from a command source file. Signed-off-by: Miquel Raynal Acked-by: Jagan Teki Reviewed-by: Stefan Roese Reviewed-by: Boris Brezillon --- include/mtd.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/mtd.h b/include/mtd.h index 9e5c807294..75aca262d4 100644 --- a/include/mtd.h +++ b/include/mtd.h @@ -8,4 +8,6 @@ #include +int mtd_probe(struct udevice *dev); + #endif /* _MTD_H_ */ -- cgit From 21cc1fb5af06e468c74ae601bac719d306523f9c Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Sat, 29 Sep 2018 12:58:25 +0200 Subject: mtd: mtdpart: add a generic mtdparts-like parser The current parser is very specific to U-Boot mtdparts implementation. It does not use MTD structures like mtd_info and mtd_partition. Copy and adapt the current parser in drivers/mtd/mtd-uclass.c (to not break the current use of mtdparts.c itself) and write some kind of a wrapper around the current implementation to allow other commands to benefit from this parsing in a user-friendly way. This new function will allocate an mtd_partition array for each successful call. This array must be freed after use by the caller. The given 'mtdparts' buffer pointer will be moved forward to the next MTD device (if any, it will point towards a '\0' character otherwise). Signed-off-by: Miquel Raynal Acked-by: Jagan Teki Reviewed-by: Stefan Roese Reviewed-by: Boris Brezillon --- include/linux/mtd/partitions.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'include') diff --git a/include/linux/mtd/partitions.h b/include/linux/mtd/partitions.h index ce0e8dbee4..6eea0a547a 100644 --- a/include/linux/mtd/partitions.h +++ b/include/linux/mtd/partitions.h @@ -87,4 +87,25 @@ int mtd_add_partition(struct mtd_info *master, const char *name, int mtd_del_partition(struct mtd_info *master, int partno); uint64_t mtd_get_device_size(const struct mtd_info *mtd); +#if defined(CONFIG_MTD_PARTITIONS) +int mtd_parse_partitions(struct mtd_info *parent, const char **_mtdparts, + struct mtd_partition **_parts, int *_nparts); +void mtd_free_parsed_partitions(struct mtd_partition *parts, + unsigned int nparts); +#else +static inline int +mtd_parse_partitions(struct mtd_info *parent, const char **_mtdparts, + struct mtd_partition **_parts, int *_nparts) +{ + *_nparts = 0; + + return 0; +} +static inline void +mtd_free_parsed_partitions(struct mtd_partition *parts, unsigned int nparts) +{ + return; +} +#endif /* defined(MTD_PARTITIONS) */ + #endif -- cgit From ff4afa8a981e22eef670c7c857cb87983346cc2c Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Sat, 29 Sep 2018 12:58:26 +0200 Subject: mtd: uboot: search for an equivalent MTD name with the mtdids Using an MTD device (resp. partition) name in mtdparts is simple and straightforward. However, for a long time already, another name was given in mtdparts to indicate a device (resp. partition) so the "mtdids" environment variable was created to do the match. Let's create a function that, from an MTD device (resp. partition) name, search for the equivalent name in the "mtdparts" environment variable thanks to the "mtdids" string. Signed-off-by: Miquel Raynal Reviewed-by: Stefan Roese Reviewed-by: Boris Brezillon --- include/linux/mtd/mtd.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include') diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index b8c2c3fd59..af6f4a61f8 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -549,5 +549,10 @@ int mtd_arg_off_size(int argc, char *const argv[], int *idx, loff_t *off, void mtd_get_len_incl_bad(struct mtd_info *mtd, uint64_t offset, const uint64_t length, uint64_t *len_incl_bad, int *truncated); + +/* drivers/mtd/mtd_uboot.c */ +int mtd_search_alternate_name(const char *mtdname, char *altname, + unsigned int max_len); + #endif #endif /* __MTD_MTD_H__ */ -- cgit From 2a74930da57f6fbe3c24509f1d481f435acd2356 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Sat, 29 Sep 2018 12:58:27 +0200 Subject: mtd: mtdpart: implement proper partition handling Instead of collecting partitions in a flat list, create a hierarchy within the mtd_info structure: use a partitions list to keep track of the partitions of an MTD device (which might be itself a partition of another MTD device), a pointer to the parent device (NULL when the MTD device is the root one, not a partition). By also saving directly in mtd_info the offset of the partition, we can get rid of the mtd_part structure. Signed-off-by: Miquel Raynal Reviewed-by: Stefan Roese Reviewed-by: Boris Brezillon --- include/linux/mtd/mtd.h | 32 ++++++++++++++++++++++++++++++++ include/linux/mtd/partitions.h | 1 - 2 files changed, 32 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index af6f4a61f8..68e5915324 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -20,6 +20,7 @@ #include #include #include +#include #include #if IS_ENABLED(CONFIG_DM) #include @@ -307,6 +308,27 @@ struct mtd_info { struct udevice *dev; #endif int usecount; + + /* MTD devices do not have any parent. MTD partitions do. */ + struct mtd_info *parent; + + /* + * Offset of the partition relatively to the parent offset. + * Is 0 for real MTD devices (ie. not partitions). + */ + u64 offset; + + /* + * List node used to add an MTD partition to the parent + * partition list. + */ + struct list_head node; + + /* + * List of partitions attached to this MTD device (the parent + * MTD device can itself be a partition). + */ + struct list_head partitions; }; #if IS_ENABLED(CONFIG_DM) @@ -334,6 +356,16 @@ static inline const struct device_node *mtd_get_of_node(struct mtd_info *mtd) } #endif +static inline bool mtd_is_partition(const struct mtd_info *mtd) +{ + return mtd->parent; +} + +static inline bool mtd_has_partitions(const struct mtd_info *mtd) +{ + return !list_empty(&mtd->partitions); +} + int mtd_ooblayout_ecc(struct mtd_info *mtd, int section, struct mtd_oob_region *oobecc); int mtd_ooblayout_find_eccregion(struct mtd_info *mtd, int eccbyte, diff --git a/include/linux/mtd/partitions.h b/include/linux/mtd/partitions.h index 6eea0a547a..3822237f2a 100644 --- a/include/linux/mtd/partitions.h +++ b/include/linux/mtd/partitions.h @@ -81,7 +81,6 @@ extern void register_mtd_parser(struct mtd_part_parser *parser); extern void deregister_mtd_parser(struct mtd_part_parser *parser); #endif -int mtd_is_partition(const struct mtd_info *mtd); int mtd_add_partition(struct mtd_info *master, const char *name, long long offset, long long length); int mtd_del_partition(struct mtd_info *master, int partno); -- cgit From 5db66b3aee6f2c057706d8715f7e5c472e82f047 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Sat, 29 Sep 2018 12:58:28 +0200 Subject: cmd: mtd: add 'mtd' command There should not be a 'nand' command, a 'sf' command and certainly not a new 'spi-nand' command. Write a 'mtd' command instead to manage all MTD devices/partitions at once. This should be the preferred way to access any MTD device. Signed-off-by: Miquel Raynal Acked-by: Jagan Teki Reviewed-by: Stefan Roese Reviewed-by: Boris Brezillon --- include/mtd.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/mtd.h b/include/mtd.h index 75aca262d4..65fcd3c700 100644 --- a/include/mtd.h +++ b/include/mtd.h @@ -9,5 +9,6 @@ #include int mtd_probe(struct udevice *dev); +int mtd_probe_devices(void); #endif /* _MTD_H_ */ -- cgit