Marvell NAND controller rework with ->exec_op()
Subsystems (10)
cover Marvell NAND controller rework with ->exec_op()
Hi,
This series adds the implementation of the NAND framework ->exec_op() interface with all the related hooks and helpers. The reasons for adding it are explained in details in the commit log "mtd: nand: add
->exec_op() implementation", but mostly it will ease later expansion of
the framework, as well as the implementation of new vendor specific commands, and should also ease driver development.
The series contains as well the reworked NAND controller driver from Marvell.
A lot of comments are written to explain how to use the new API and the Marvell driver shows how to implement it. A proper external documentation is being written and will later be submitted.
It also changes the device tree NAND node definition for all platforms referring to the Marvell driver to use the new bindings. They are more hierarchical and fit the real organization of the hardware, by having NAND partitions that are part of NAND chip nodes, themselves part of the NAND controller node.
See the commit log of "mtd: nand: add reworked Marvell NAND controller driver" for details about why a completely new driver is needed.
As this series changes several core functions and also directly touches multiple controller drivers, it would be great to have reviews and tests from the concerned maintainers. Only PXA3xx SoCs are not supported yet (explaining the RFC) as having a local board to test is really needed.
Thank you,
Miqu?l
Boris Brezillon (2):
mtd: nand: provide several helpers to do common NAND operations
mtd: nand: force drivers to explicitly send READ/PROG commands
Miquel Raynal (10):
mtd: nand: use a static data_interface in the nand_chip structure
mtd: nand: add ->exec_op() implementation
dt-bindings: mtd: add Marvell NAND controller documentation
mtd: nand: add reworked Marvell NAND controller driver
ARM: dts: armada-370-xp: use reworked NAND controller driver
ARM: dts: armada-375: use reworked NAND controller driver
ARM: dts: armada-38x: use reworked NAND controller driver
ARM: dts: armada-39x: use reworked NAND controller driver
ARM: dts: pxa: use reworked NAND controller driver
ARM64: dts: marvell: use reworked NAND controller driver on Armada
7K/8K
.../devicetree/bindings/mtd/marvell-nand.txt | 95 + arch/arm/boot/dts/armada-370-db.dts | 56 +- arch/arm/boot/dts/armada-370-dlink-dns327l.dts | 119 +- arch/arm/boot/dts/armada-370-mirabox.dts | 50 +- arch/arm/boot/dts/armada-370-netgear-rn102.dts | 89 +- arch/arm/boot/dts/armada-370-netgear-rn104.dts | 89 +- arch/arm/boot/dts/armada-370-rd.dts | 51 +- arch/arm/boot/dts/armada-370-seagate-nas-xbay.dtsi | 63 +- arch/arm/boot/dts/armada-370-xp.dtsi | 6 +- arch/arm/boot/dts/armada-375-db.dts | 49 +- arch/arm/boot/dts/armada-375.dtsi | 6 +- arch/arm/boot/dts/armada-385-db-ap.dts | 68 +- arch/arm/boot/dts/armada-385-linksys-caiman.dts | 129 +- arch/arm/boot/dts/armada-385-linksys-cobra.dts | 129 +- arch/arm/boot/dts/armada-385-linksys-rango.dts | 141 +- arch/arm/boot/dts/armada-385-linksys-shelby.dts | 129 +- arch/arm/boot/dts/armada-385-linksys.dtsi | 15 +- arch/arm/boot/dts/armada-388-db.dts | 54 +- arch/arm/boot/dts/armada-38x.dtsi | 6 +- arch/arm/boot/dts/armada-390-db.dts | 65 +- arch/arm/boot/dts/armada-395-gp.dts | 73 +- arch/arm/boot/dts/armada-398-db.dts | 59 +- arch/arm/boot/dts/armada-39x.dtsi | 6 +- arch/arm/boot/dts/armada-xp-db-dxbc2.dts | 1 - arch/arm/boot/dts/armada-xp-db-xc3-24g4xg.dts | 1 - arch/arm/boot/dts/armada-xp-db.dts | 1 - arch/arm/boot/dts/armada-xp-gp.dts | 1 - arch/arm/boot/dts/armada-xp-lenovo-ix4-300d.dts | 1 - arch/arm/boot/dts/armada-xp-linksys-mamba.dts | 155 +- arch/arm/boot/dts/armada-xp-netgear-rn2120.dts | 89 +- arch/arm/boot/dts/pxa3xx.dtsi | 6 +- arch/arm64/boot/dts/marvell/armada-7040-db.dts | 47 +- .../boot/dts/marvell/armada-cp110-master.dtsi | 6 +- .../arm64/boot/dts/marvell/armada-cp110-slave.dtsi | 6 +- drivers/mtd/nand/Kconfig | 11 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/atmel/nand-controller.c | 9 +- drivers/mtd/nand/bf5xx_nand.c | 6 +- drivers/mtd/nand/brcmnand/brcmnand.c | 20 +- drivers/mtd/nand/cafe_nand.c | 20 +- drivers/mtd/nand/denali.c | 109 +- drivers/mtd/nand/diskonchip.c | 4 +- drivers/mtd/nand/docg4.c | 19 +- drivers/mtd/nand/fsl_elbc_nand.c | 6 +- drivers/mtd/nand/fsl_ifc_nand.c | 6 +- drivers/mtd/nand/fsmc_nand.c | 5 +- drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 76 +- drivers/mtd/nand/hisi504_nand.c | 9 +- drivers/mtd/nand/jz4740_nand.c | 16 +- drivers/mtd/nand/lpc32xx_mlc.c | 7 +- drivers/mtd/nand/lpc32xx_slc.c | 33 +- drivers/mtd/nand/marvell_nand.c | 2384 ++++++++++++++++++++ drivers/mtd/nand/mtk_nand.c | 21 +- drivers/mtd/nand/nand_base.c | 2020 ++++++++++++++--- drivers/mtd/nand/nand_hynix.c | 106 +- drivers/mtd/nand/nand_micron.c | 50 +- drivers/mtd/nand/nand_timings.c | 23 +- drivers/mtd/nand/omap2.c | 8 +- drivers/mtd/nand/pxa3xx_nand.c | 8 +- drivers/mtd/nand/qcom_nandc.c | 16 +- drivers/mtd/nand/r852.c | 11 +- drivers/mtd/nand/sh_flctl.c | 6 +- drivers/mtd/nand/sunxi_nand.c | 97 +- drivers/mtd/nand/tango_nand.c | 27 +- drivers/mtd/nand/tmio_nand.c | 5 +- drivers/mtd/nand/vf610_nfc.c | 6 +- drivers/staging/mt29f_spinand/mt29f_spinand.c | 7 +- include/linux/mtd/rawnand.h | 391 +++- 68 files changed, 5980 insertions(+), 1424 deletions(-) create mode 100644 Documentation/devicetree/bindings/mtd/marvell-nand.txt create mode 100644 drivers/mtd/nand/marvell_nand.c
Signature · 1 line
2.11.0
RFC 1/12 mtd: nand: provide several helpers to do common NAND operations
From: Boris Brezillon <[email protected]>
This is part of the process of removing direct calls to ->cmdfunc() outside of the core in order to introduce a better interface to execute NAND operations.
Here we provide several helpers and make use of them to remove all direct calls to ->cmdfunc(). This way, we can easily modify those helpers to make use of the new ->exec_op() interface when available.
--- drivers/mtd/nand/atmel/nand-controller.c | 2 +- drivers/mtd/nand/brcmnand/brcmnand.c | 9 +- drivers/mtd/nand/cafe_nand.c | 14 +- drivers/mtd/nand/denali.c | 2 +- drivers/mtd/nand/diskonchip.c | 4 +- drivers/mtd/nand/docg4.c | 7 +- drivers/mtd/nand/fsmc_nand.c | 5 +- drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 56 +- drivers/mtd/nand/hisi504_nand.c | 3 +- drivers/mtd/nand/jz4740_nand.c | 16 +- drivers/mtd/nand/lpc32xx_mlc.c | 2 +- drivers/mtd/nand/lpc32xx_slc.c | 22 +- drivers/mtd/nand/mtk_nand.c | 11 +- drivers/mtd/nand/nand_base.c | 956 ++++++++++++++++++++++++++----- drivers/mtd/nand/nand_hynix.c | 17 +- drivers/mtd/nand/nand_micron.c | 17 +- drivers/mtd/nand/omap2.c | 8 +- drivers/mtd/nand/pxa3xx_nand.c | 8 +- drivers/mtd/nand/qcom_nandc.c | 16 +- drivers/mtd/nand/r852.c | 11 +- drivers/mtd/nand/sunxi_nand.c | 67 +-- drivers/mtd/nand/tango_nand.c | 26 +- drivers/mtd/nand/tmio_nand.c | 5 +- include/linux/mtd/rawnand.h | 25 + 24 files changed, 943 insertions(+), 366 deletions(-)
drivers/mtd/nand/atmel/nand-controller.c +1 -1
@@ -1000,7 +1000,7 @@ static int atmel_hsmc_nand_pmecc_read_pg(struct nand_chip *chip, u8 *buf,* to the non-optimized one. */ if (nand->activecs->rb.type !=ATMEL_NAND_NATIVE_RB ) { - chip->cmdfunc (mtd,NAND_CMD_READ0 ,0x00 , page); +nand_read_page_op (chip, page,0 ,NULL ,0 );return atmel_nand_pmecc_read_pg (chip, buf, oob_required, page, raw);
drivers/mtd/nand/brcmnand/brcmnand.c +4 -5
@@ -1071,7 +1071,7 @@ static void brcmnand_wp(struct mtd_info *mtd, int wp)return ;brcmnand_set_wp (ctrl, wp); - chip->cmdfunc (mtd,NAND_CMD_STATUS ,-1 ,-1 ); +nand_status_op (chip,NULL );/* NAND_STATUS_WP 0x00 = protected, 0x80 = not protected */ ret = bcmnand_ctrl_poll_status(ctrl, NAND_CTRL_RDY |
@@ -1453,7 +1453,7 @@ static uint8_t brcmnand_read_byte(struct mtd_info *mtd)/* At FC_BYTES boundary, switch to next column */ if (host->last_byte >0 && offs ==0 ) - chip->cmdfunc (mtd,NAND_CMD_RNDOUT , addr,-1 ); +nand_change_read_column_op (chip, addr,NULL ,0 , false); ret = ctrl->flash_cache[offs];break ;
@@ -1689,7 +1689,7 @@ static int brcmstb_nand_verify_erased_page(struct mtd_info *mtd, sas = mtd->oobsize / chip->ecc.steps;/* read without ecc for verification */ - chip->cmdfunc (mtd,NAND_CMD_READ0 ,0x00 , page); +nand_read_page_op (chip, page,0 ,NULL ,0 ); ret = chip->ecc.read_page_raw (mtd, chip, buf, true, page);if (ret)return ret;
@@ -2369,12 +2369,11 @@ static int brcmnand_resume(struct device *dev)list_for_each_entry (host, &ctrl->host_list, node) {struct nand_chip *chip = &host->chip; -struct mtd_info *mtd =nand_to_mtd (chip);brcmnand_save_restore_cs_config (host,1 );/* Reset the chip, required by some chips after power-up */ - chip->cmdfunc (mtd,NAND_CMD_RESET ,-1 ,-1 ); +nand_reset_op (chip); }return 0 ;
drivers/mtd/nand/cafe_nand.c +3 -11
@@ -353,23 +353,15 @@ static void cafe_nand_bug(struct mtd_info *mtd)static int cafe_nand_write_oob (struct mtd_info *mtd,struct nand_chip *chip,int page) { -int status =0 ; - - chip->cmdfunc (mtd,NAND_CMD_SEQIN , mtd->writesize, page); - chip->write_buf (mtd, chip->oob_poi, mtd->oobsize); - chip->cmdfunc (mtd,NAND_CMD_PAGEPROG ,-1 ,-1 ); - status = chip->waitfunc (mtd, chip); - -return status &NAND_STATUS_FAIL ? -EIO :0 ; +return nand_prog_page_op (chip, page, mtd->writesize, chip->oob_poi, + mtd->oobsize); }/* Don't use -- use nand_read_oob_std for now */ static int cafe_nand_read_oob (struct mtd_info *mtd,struct nand_chip *chip,int page) { - chip->cmdfunc (mtd,NAND_CMD_READOOB ,0 , page); - chip->read_buf (mtd, chip->oob_poi, mtd->oobsize); -return 0 ; +return nand_read_oob_op (chip, page,0 , chip->oob_poi, mtd->oobsize); }/** * cafe_nand_read_page_syndrome - [REPLACEABLE] hardware ecc syndrome based page read
drivers/mtd/nand/denali.c +1 -1
@@ -951,7 +951,7 @@ static int denali_erase(struct mtd_info *mtd, int page) irq_status =denali_wait_for_irq (denali,INTR__ERASE_COMP |INTR__ERASE_FAIL ); -return irq_status &INTR__ERASE_COMP ?0 :NAND_STATUS_FAIL ; +return irq_status &INTR__ERASE_FAIL ? -EIO :0 ; } static int denali_setup_data_interface(struct mtd_info *mtd, int chipnr,
drivers/mtd/nand/diskonchip.c +2 -2
@@ -448,7 +448,7 @@ static int doc200x_wait(struct mtd_info *mtd, struct nand_chip *this)int status;DoC_WaitReady (doc); - this->cmdfunc (mtd,NAND_CMD_STATUS ,-1 ,-1 ); +nand_status_op (this,NULL );DoC_WaitReady (doc); status = (int )this->read_byte (mtd);
@@ -595,7 +595,7 @@ static void doc2001plus_select_chip(struct mtd_info *mtd, int chip)/* Assert ChipEnable and deassert WriteProtect */ WriteDOC ((DOC_FLASH_CE ), docptr, Mplus_FlashSelect); - this->cmdfunc (mtd,NAND_CMD_RESET ,-1 ,-1 ); +nand_reset_op (this); doc->curchip = chip; doc->curfloor = floor;
drivers/mtd/nand/docg4.c +6 -1
@@ -900,6 +900,7 @@ static int docg4_erase_block(struct mtd_info *mtd, int page)struct docg4_priv *doc =nand_get_controller_data (nand);void __iomem *docptr = doc->virtadr;uint16_t g4_page; +int status;dev_dbg (doc->dev,"%s: page %04x\n" , __func__, page);
@@ -939,7 +940,11 @@ static int docg4_erase_block(struct mtd_info *mtd, int page)poll_status (doc);write_nop (docptr); -return nand->waitfunc (mtd, nand); + status = nand->waitfunc (mtd, nand); +if (status <0 ) +return status; + +return status &NAND_STATUS_FAIL ? -EIO :0 ; } static int write_page(struct mtd_info *mtd, struct nand_chip *nand,
drivers/mtd/nand/fsmc_nand.c +2 -3
@@ -697,7 +697,7 @@ static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,unsigned int max_bitflips =0 ;for (i =0 , s =0 ; s < eccsteps; s++, i += eccbytes, p += eccsize) { - chip->cmdfunc (mtd,NAND_CMD_READ0 , s * eccsize, page); +nand_read_page_op (chip, page, s * eccsize,NULL ,0 ); chip->ecc.hwctl (mtd,NAND_ECC_READ ); chip->read_buf (mtd, p, eccsize);
@@ -720,8 +720,7 @@ static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,if (chip->options &NAND_BUSWIDTH_16 ) len =roundup (len,2 ); - chip->cmdfunc (mtd,NAND_CMD_READOOB , off, page); - chip->read_buf (mtd, oob + j, len); +nand_read_oob_op (chip, page, off, oob + j, len); j += len; }
drivers/mtd/nand/gpmi-nand/gpmi-nand.c +18 -38
137 lines not on this page · raw message
drivers/mtd/nand/hisi504_nand.c +1 -2
@@ -574,8 +574,7 @@ static int hisi_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip, {struct hinfc_host *host =nand_get_controller_data (chip); - chip->cmdfunc (mtd,NAND_CMD_READOOB ,0 , page); - chip->read_buf (mtd, chip->oob_poi, mtd->oobsize); +nand_read_oob_op (chip, page,0 , chip->oob_poi, mtd->oobsize);if (host->irq_status &HINFC504_INTS_UE ) { host->irq_status =0 ;
drivers/mtd/nand/jz4740_nand.c +8 -8
@@ -313,6 +313,7 @@ static int jz_nand_detect_bank(struct platform_device *pdev,uint32_t ctrl;struct nand_chip *chip = &nand->chip;struct mtd_info *mtd =nand_to_mtd (chip); +u8 id[2 ];/* Request I/O resource. */ sprintf (res_name,"bank%d" , bank);
@@ -335,17 +336,16 @@ static int jz_nand_detect_bank(struct platform_device *pdev,/* Retrieve the IDs from the first chip. */ chip->select_chip (mtd,0 ); - chip->cmdfunc (mtd,NAND_CMD_RESET ,-1 ,-1 ); - chip->cmdfunc (mtd,NAND_CMD_READID ,0x00 ,-1 ); - *nand_maf_id = chip->read_byte (mtd); - *nand_dev_id = chip->read_byte (mtd); +nand_reset_op (chip); +nand_readid_op (chip,0 , id,sizeof (id)); + *nand_maf_id = id[0 ]; + *nand_dev_id = id[1 ]; }else {/* Detect additional chip. */ chip->select_chip (mtd, chipnr); - chip->cmdfunc (mtd,NAND_CMD_RESET ,-1 ,-1 ); - chip->cmdfunc (mtd,NAND_CMD_READID ,0x00 ,-1 ); -if (*nand_maf_id != chip->read_byte (mtd) - || *nand_dev_id != chip->read_byte (mtd)) { +nand_reset_op (chip); +nand_readid_op (chip,0 , id,sizeof (id)); +if (*nand_maf_id != id[0 ] || *nand_dev_id != id[1 ]) { ret = -ENODEV ; goto notfound_id; }
drivers/mtd/nand/lpc32xx_mlc.c +1 -1
@@ -461,7 +461,7 @@ static int lpc32xx_read_page(struct mtd_info *mtd, struct nand_chip *chip, }/* Writing Command and Address */ - chip->cmdfunc (mtd,NAND_CMD_READ0 ,0 , page); +nand_read_page_op (chip, page,0 ,NULL ,0 );/* For all sub-pages */ for (i =0 ; i < host->mlcsubpages; i++) {
drivers/mtd/nand/lpc32xx_slc.c +5 -17
@@ -399,10 +399,7 @@ static void lpc32xx_nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, intstatic int lpc32xx_nand_read_oob_syndrome (struct mtd_info *mtd,struct nand_chip *chip,int page) { - chip->cmdfunc (mtd,NAND_CMD_READOOB ,0 , page); - chip->read_buf (mtd, chip->oob_poi, mtd->oobsize); - -return 0 ; +return nand_read_oob_op (chip, page,0 , chip->oob_poi, mtd->oobsize); }/*
@@ -411,17 +408,8 @@ static int lpc32xx_nand_read_oob_syndrome(struct mtd_info *mtd,static int lpc32xx_nand_write_oob_syndrome (struct mtd_info *mtd,struct nand_chip *chip,int page) { -int status; - - chip->cmdfunc (mtd,NAND_CMD_SEQIN , mtd->writesize, page); - chip->write_buf (mtd, chip->oob_poi, mtd->oobsize); - -/* Send command to program the OOB data */ - chip->cmdfunc (mtd,NAND_CMD_PAGEPROG ,-1 ,-1 ); - - status = chip->waitfunc (mtd, chip); - -return status &NAND_STATUS_FAIL ? -EIO :0 ; +return nand_prog_page_op (chip, page, mtd->writesize, chip->oob_poi, + mtd->oobsize); }/*
@@ -632,7 +620,7 @@ static int lpc32xx_nand_read_page_syndrome(struct mtd_info *mtd,uint8_t *oobecc, tmpecc[LPC32XX_ECC_SAVE_SIZE ];/* Issue read command */ - chip->cmdfunc (mtd,NAND_CMD_READ0 ,0 , page); +nand_read_page_op (chip, page,0 ,NULL ,0 );/* Read data and oob, calculate ECC */ status =lpc32xx_xfer (mtd, buf, chip->ecc.steps,1 );
@@ -675,7 +663,7 @@ static int lpc32xx_nand_read_page_raw_syndrome(struct mtd_info *mtd, int page) {/* Issue read command */ - chip->cmdfunc (mtd,NAND_CMD_READ0 ,0 , page); +nand_read_page_op (chip, page,0 ,NULL ,0 );/* Raw reads can just use the FIFO interface */ chip->read_buf (mtd, buf, chip->ecc.size * chip->ecc.steps);
drivers/mtd/nand/mtk_nand.c +4 -7
@@ -834,16 +834,13 @@ static int mtk_nfc_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, {int ret; - chip->cmdfunc (mtd,NAND_CMD_SEQIN ,0x00 , page); +nand_prog_page_begin_op (chip, page,0 ,NULL ,0 ); ret =mtk_nfc_write_page_raw (mtd, chip,NULL ,1 , page);if (ret <0 )return -EIO ; - chip->cmdfunc (mtd,NAND_CMD_PAGEPROG ,-1 ,-1 ); - ret = chip->waitfunc (mtd, chip); - -return ret &NAND_STATUS_FAIL ? -EIO :0 ; +return nand_prog_page_end_op (chip); }static int mtk_nfc_update_ecc_stats (struct mtd_info *mtd,u8 *buf,u32 sectors)
@@ -1016,7 +1013,7 @@ static int mtk_nfc_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,static int mtk_nfc_read_oob_std (struct mtd_info *mtd,struct nand_chip *chip,int page) { - chip->cmdfunc (mtd,NAND_CMD_READ0 ,0 , page); +nand_read_page_op (chip, page,0 ,NULL ,0 );return mtk_nfc_read_page_raw (mtd, chip,NULL ,1 , page); }
@@ -1556,7 +1553,7 @@ static int mtk_nfc_resume(struct device *dev) mtd =nand_to_mtd (nand);for (i =0 ; i < chip->nsels; i++) { nand->select_chip (mtd, i); - nand->cmdfunc (mtd,NAND_CMD_RESET ,-1 ,-1 ); +nand_reset_op (nand); } }
drivers/mtd/nand/nand_base.c +797 -159
1,379 lines not on this page · raw message
drivers/mtd/nand/nand_hynix.c +6 -11
@@ -67,15 +67,11 @@ struct hynix_read_retry_otp {static bool hynix_nand_has_valid_jedecid (struct nand_chip *chip) { -struct mtd_info *mtd =nand_to_mtd (chip); -u8 jedecid[6 ] = { }; -int i =0 ; +u8 jedecid[5 ] = { }; - chip->cmdfunc (mtd,NAND_CMD_READID ,0x40 ,-1 ); -for (i =0 ; i <5 ; i++) - jedecid[i] = chip->read_byte (mtd); +nand_readid_op (chip,0x40 , jedecid,sizeof (jedecid)); -return !strcmp ("JEDEC" , jedecid); +return !strncmp ("JEDEC" , jedecid,sizeof (jedecid)); }static int hynix_nand_setup_read_retry (struct mtd_info *mtd,int retry_mode)
@@ -175,7 +171,7 @@ static int hynix_read_rr_otp(struct nand_chip *chip,struct mtd_info *mtd =nand_to_mtd (chip);int i; - chip->cmdfunc (mtd,NAND_CMD_RESET ,-1 ,-1 ); +nand_reset_op (chip); chip->cmdfunc (mtd,NAND_HYNIX_CMD_SET_PARAMS ,-1 ,-1 );
@@ -195,11 +191,10 @@ static int hynix_read_rr_otp(struct nand_chip *chip, chip->cmdfunc (mtd,0x19 ,-1 ,-1 );/* Now read the page */ - chip->cmdfunc (mtd,NAND_CMD_READ0 ,0x0 , info->page); - chip->read_buf (mtd, buf, info->size); +nand_read_page_op (chip, info->page,0 , buf, info->size);/* Put everything back to normal */ - chip->cmdfunc (mtd,NAND_CMD_RESET ,-1 ,-1 ); +nand_reset_op (chip); chip->cmdfunc (mtd,NAND_HYNIX_CMD_SET_PARAMS ,0x38 ,-1 ); chip->write_byte (mtd,0x0 ); chip->cmdfunc (mtd,NAND_HYNIX_CMD_APPLY_PARAMS ,-1 ,-1 );
drivers/mtd/nand/nand_micron.c +2 -15
@@ -137,8 +137,6 @@ micron_nand_read_page_on_die_ecc(struct mtd_info *mtd, struct nand_chip *chip,else if (status &NAND_STATUS_WRITE_RECOMMENDED ) max_bitflips = chip->ecc.strength; - chip->cmdfunc (mtd,NAND_CMD_READ0 ,-1 ,-1 ); -nand_read_page_raw (mtd, chip, buf, oob_required, page);micron_nand_on_die_ecc_setup (chip, false);
@@ -155,10 +153,7 @@ micron_nand_write_page_on_die_ecc(struct mtd_info *mtd, struct nand_chip *chip,micron_nand_on_die_ecc_setup (chip, true); - chip->cmdfunc (mtd,NAND_CMD_SEQIN ,0x00 , page); -nand_write_page_raw (mtd, chip, buf, oob_required, page); - chip->cmdfunc (mtd,NAND_CMD_PAGEPROG ,-1 ,-1 ); - status = chip->waitfunc (mtd, chip); + status =nand_write_page_raw (mtd, chip, buf, oob_required, page);micron_nand_on_die_ecc_setup (chip, false);
@@ -171,7 +166,6 @@ micron_nand_read_page_raw_on_die_ecc(struct mtd_info *mtd, uint8_t *buf, int oob_required, int page) { - chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);nand_read_page_raw (mtd ,chip ,buf ,oob_required ,page );return 0 ;
@@ -183,14 +177,7 @@ micron_nand_write_page_raw_on_die_ecc(struct mtd_info *mtd, const uint8_t *buf, int oob_required, int page) { - int status; - - chip->cmdfunc (mtd,NAND_CMD_SEQIN ,0x00 , page); -nand_write_page_raw (mtd, chip, buf, oob_required, page); - chip->cmdfunc (mtd,NAND_CMD_PAGEPROG ,-1 ,-1 ); - status = chip->waitfunc (mtd, chip); - -return status &NAND_STATUS_FAIL ? -EIO :0 ; + return nand_write_page_raw(mtd, chip, buf, oob_required, page); } enum {
drivers/mtd/nand/omap2.c +4 -4
@@ -1538,10 +1538,10 @@ static int omap_read_page_bch(struct mtd_info *mtd, struct nand_chip *chip, chip->read_buf (mtd, buf, mtd->writesize);/* Read oob bytes */ - chip->cmdfunc (mtd,NAND_CMD_RNDOUT , - mtd->writesize +BADBLOCK_MARKER_LENGTH ,-1 ); - chip->read_buf (mtd, chip->oob_poi +BADBLOCK_MARKER_LENGTH , - chip->ecc.total); +nand_change_read_column_op (chip, + mtd->writesize +BADBLOCK_MARKER_LENGTH , + chip->oob_poi +BADBLOCK_MARKER_LENGTH , + chip->ecc.total, false);/* Calculate ecc bytes */ chip->ecc.calculate (mtd, buf, ecc_calc);
drivers/mtd/nand/pxa3xx_nand.c +3 -5
@@ -520,15 +520,13 @@ static int pxa3xx_nand_init_timings_compat(struct pxa3xx_nand_host *host,struct nand_chip *chip = &host->chip;struct pxa3xx_nand_info *info = host->info_data;const struct pxa3xx_nand_flash *f =NULL ; -struct mtd_info *mtd =nand_to_mtd (&host->chip);int i, id, ntypes; +u8 idbuf[2 ]; ntypes =ARRAY_SIZE (builtin_flash_types); - chip->cmdfunc (mtd,NAND_CMD_READID ,0x00 ,-1 ); - - id = chip->read_byte (mtd); - id |= chip->read_byte (mtd) <<0x8 ; +nand_readid_op (chip,0 , idbuf,sizeof (idbuf)); + id = idbuf[0 ] | (idbuf[1 ] <<8 );for (i =0 ; i < ntypes; i++) { f = &builtin_flash_types[i];
drivers/mtd/nand/qcom_nandc.c +4 -12
@@ -1990,7 +1990,7 @@ static int qcom_nandc_write_oob(struct mtd_info *mtd, struct nand_chip *chip,struct nand_ecc_ctrl *ecc = &chip->ecc;u8 *oob = chip->oob_poi;int data_size, oob_size; -int ret, status =0 ; +int ret; host->use_ecc = true;
@@ -2027,11 +2027,7 @@ static int qcom_nandc_write_oob(struct mtd_info *mtd, struct nand_chip *chip,return -EIO ; } - chip->cmdfunc (mtd,NAND_CMD_PAGEPROG ,-1 ,-1 ); - - status = chip->waitfunc (mtd, chip); - -return status &NAND_STATUS_FAIL ? -EIO :0 ; +return nand_prog_page_end_op (chip); }static int qcom_nandc_block_bad (struct mtd_info *mtd,loff_t ofs)
@@ -2081,7 +2077,7 @@ static int qcom_nandc_block_markbad(struct mtd_info *mtd, loff_t ofs)struct qcom_nand_host *host =to_qcom_nand_host (chip);struct qcom_nand_controller *nandc =get_qcom_nand_controller (chip);struct nand_ecc_ctrl *ecc = &chip->ecc; -int page, ret, status =0 ; +int page, ret;clear_read_regs (nandc);clear_bam_transaction (nandc);
@@ -2114,11 +2110,7 @@ static int qcom_nandc_block_markbad(struct mtd_info *mtd, loff_t ofs)return -EIO ; } - chip->cmdfunc (mtd,NAND_CMD_PAGEPROG ,-1 ,-1 ); - - status = chip->waitfunc (mtd, chip); - -return status &NAND_STATUS_FAIL ? -EIO :0 ; +return nand_prog_page_end_op (chip); }/*
drivers/mtd/nand/r852.c +4 -7
@@ -364,7 +364,7 @@ static int r852_wait(struct mtd_info *mtd, struct nand_chip *chip)struct r852_device *dev =nand_get_controller_data (chip);unsigned long timeout; -int status; +u8 status; timeout = jiffies + (chip->state ==FL_ERASING ?msecs_to_jiffies (400 ) :msecs_to_jiffies (20 ));
@@ -373,8 +373,7 @@ static int r852_wait(struct mtd_info *mtd, struct nand_chip *chip)if (chip->dev_ready (mtd))break ; - chip->cmdfunc (mtd,NAND_CMD_STATUS ,-1 ,-1 ); - status = (int )chip->read_byte (mtd); +nand_status_op (chip, &status);/* Unfortunelly, no way to send detailed error status... */ if (dev->dma_error) {
@@ -522,9 +521,7 @@ static int r852_ecc_correct(struct mtd_info *mtd, uint8_t *dat,static int r852_read_oob (struct mtd_info *mtd,struct nand_chip *chip,int page) { - chip->cmdfunc (mtd,NAND_CMD_READOOB ,0 , page); - chip->read_buf (mtd, chip->oob_poi, mtd->oobsize); -return 0 ; +return nand_read_oob_op (chip, page,0 , chip->oob_poi, mtd->oobsize); }/*
@@ -1046,7 +1043,7 @@ static int r852_resume(struct device *device)if (dev->card_registred) {r852_engine_enable (dev); dev->chip->select_chip (mtd,0 ); - dev->chip->cmdfunc (mtd,NAND_CMD_RESET ,-1 ,-1 ); +nand_reset_op (dev->chip); dev->chip->select_chip (mtd,-1 ); }
drivers/mtd/nand/sunxi_nand.c +31 -36
148 lines not on this page · raw message
drivers/mtd/nand/tango_nand.c +8 -18
@@ -329,7 +329,7 @@ static void aux_read(struct nand_chip *chip, u8 **buf, int len, int *pos)if (!*buf) {/* skip over "len" bytes */ - chip->cmdfunc (mtd,NAND_CMD_RNDOUT , *pos,-1 ); +nand_change_read_column_op (chip, *pos,NULL ,0 , false); }else {tango_read_buf (mtd, *buf, len); *buf += len;
@@ -344,7 +344,7 @@ static void aux_write(struct nand_chip *chip, const u8 **buf, int len, int *pos)if (!*buf) {/* skip over "len" bytes */ - chip->cmdfunc (mtd,NAND_CMD_RNDIN , *pos,-1 ); +nand_change_write_column_op (chip, *pos,NULL ,0 , false); }else {tango_write_buf (mtd, *buf, len); *buf += len;
@@ -427,7 +427,7 @@ static void raw_write(struct nand_chip *chip, const u8 *buf, const u8 *oob)static int tango_read_page_raw (struct mtd_info *mtd,struct nand_chip *chip,u8 *buf,int oob_required,int page) { - chip->cmdfunc (mtd,NAND_CMD_READ0 ,0 , page); +nand_read_page_op (chip, page,0 ,NULL ,0 );raw_read (chip, buf, chip->oob_poi);return 0 ; }
@@ -435,23 +435,15 @@ static int tango_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,static int tango_write_page_raw (struct mtd_info *mtd,struct nand_chip *chip,const u8 *buf,int oob_required,int page) { -int status; - - chip->cmdfunc (mtd,NAND_CMD_SEQIN ,0 , page); +nand_prog_page_begin_op (chip, page,0 ,NULL ,0 );raw_write (chip, buf, chip->oob_poi); - chip->cmdfunc (mtd,NAND_CMD_PAGEPROG ,-1 ,-1 ); - - status = chip->waitfunc (mtd, chip); -if (status &NAND_STATUS_FAIL ) -return -EIO ; - -return 0 ; +return nand_prog_page_end_op (chip); }static int tango_read_oob (struct mtd_info *mtd,struct nand_chip *chip,int page) { - chip->cmdfunc (mtd,NAND_CMD_READ0 ,0 , page); +nand_read_page_op (chip, page,0 ,NULL ,0 );raw_read (chip,NULL , chip->oob_poi);return 0 ; }
@@ -459,11 +451,9 @@ static int tango_read_oob(struct mtd_info *mtd, struct nand_chip *chip,static int tango_write_oob (struct mtd_info *mtd,struct nand_chip *chip,int page) { - chip->cmdfunc (mtd,NAND_CMD_SEQIN ,0 , page); +nand_prog_page_begin_op (chip, page,0 ,NULL ,0 );raw_write (chip,NULL , chip->oob_poi); - chip->cmdfunc (mtd,NAND_CMD_PAGEPROG ,-1 ,-1 ); - chip->waitfunc (mtd, chip); -return 0 ; +return nand_prog_page_end_op (chip); }static int oob_ecc (struct mtd_info *mtd,int idx,struct mtd_oob_region *res)
drivers/mtd/nand/tmio_nand.c +3 -2
@@ -192,6 +192,7 @@ tmio_nand_wait(struct mtd_info *mtd, struct nand_chip *nand_chip) {struct tmio_nand *tmio =mtd_to_tmio (mtd);long timeout; +u8 status;/* enable RDYREQ interrupt */ tmio_iowrite8 (0x0f , tmio->fcr +FCR_ISR );
@@ -212,8 +213,8 @@ tmio_nand_wait(struct mtd_info *mtd, struct nand_chip *nand_chip)dev_warn (&tmio->dev->dev,"timeout waiting for interrupt\n" ); } - nand_chip->cmdfunc (mtd,NAND_CMD_STATUS ,-1 ,-1 ); -return nand_chip->read_byte (mtd); + nand_status_op(chip, &status) +return status; }/*
include/linux/mtd/rawnand.h +25 -0
@@ -1316,6 +1316,31 @@ int nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,/* Reset and initialize a NAND device */ int nand_reset (struct nand_chip *chip,int chipnr); +/* NAND operation helpers */ +int nand_reset_op (struct nand_chip *chip); +int nand_readid_op (struct nand_chip *chip,u8 addr,void *buf, +unsigned int len); +int nand_status_op (struct nand_chip *chip,u8 *status); +int nand_erase_op (struct nand_chip *chip,unsigned int eraseblock); +int nand_read_page_op (struct nand_chip *chip,unsigned int page, +unsigned int column,void *buf,unsigned int len); +int nand_change_read_column_op (struct nand_chip *chip,unsigned int column, +void *buf,unsigned int len,bool force_8bit); +int nand_read_oob_op (struct nand_chip *chip,unsigned int page, +unsigned int column,void *buf,unsigned int len); +int nand_prog_page_begin_op (struct nand_chip *chip,unsigned int page, +unsigned int column,const void *buf, +unsigned int len); +int nand_prog_page_end_op (struct nand_chip *chip); +int nand_prog_page_op (struct nand_chip *chip,unsigned int page, +unsigned int column,const void *buf,unsigned int len); +int nand_change_write_column_op (struct nand_chip *chip,unsigned int column, +const void *buf,unsigned int len,bool force_8bit); +int nand_read_data_op (struct nand_chip *chip,void *buf,unsigned int len, +bool force_8bits); +int nand_write_data_op (struct nand_chip *chip,const void *buf, +unsigned int len,bool force_8bits); +/* Free resources held by the NAND device */ void nand_cleanup (struct nand_chip *chip);
Signature · 1 line
2.11.0
RFC 2/12 mtd: nand: force drivers to explicitly send READ/PROG commands
In reply to Miquel RaynalFrom: Boris Brezillon <[email protected]>
The core currently send the READ0 and SEQIN+PAGEPROG commands in nand_do_read/write_ops(). This is inconsistent with ->read/write_oob[_raw]() hooks behavior which are expected to send these commands.
There's already a flag (NAND_ECC_CUSTOM_PAGE_ACCESS) to inform the core that a specific controller wants to send the READ/SEQIN+PAGEPROG commands on its own, but it's an opt-in flag, and existing drivers are unlikely to be updated to pass it.
Moreover, some controllers cannot dissociate the READ/PAGEPROG commands from the associated data transfer and ECC engine activation, and developers have to hack things in their ->cmdfunc() implementation to handle such complex cases, or have to accept the perf penalty of sending twice the same command.
To address this problem we are planning on adding a new interface which is passed all information about a NAND operation (including the amount of data to transfer) and replacing all calls to ->cmdfunc() to calls to this new ->exec_op() hook. But, in order to do that, we need to have all ->cmdfunc() calls placed near their associated ->read/write_buf/byte() calls.
Modify the core and relevant drivers to make NAND_ECC_CUSTOM_PAGE_ACCESS the default case, and remove this flag.
--- drivers/mtd/nand/atmel/nand-controller.c | 7 ++- drivers/mtd/nand/bf5xx_nand.c | 6 ++- drivers/mtd/nand/brcmnand/brcmnand.c | 13 +++-- drivers/mtd/nand/cafe_nand.c | 6 +-- drivers/mtd/nand/denali.c | 14 +++++- drivers/mtd/nand/docg4.c | 12 +++-- drivers/mtd/nand/fsl_elbc_nand.c | 6 +-- drivers/mtd/nand/fsl_ifc_nand.c | 6 +-- drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 30 ++++++------ drivers/mtd/nand/hisi504_nand.c | 6 +-- drivers/mtd/nand/lpc32xx_mlc.c | 5 +- drivers/mtd/nand/lpc32xx_slc.c | 11 +++-- drivers/mtd/nand/mtk_nand.c | 10 ++-- drivers/mtd/nand/nand_base.c | 68 +++++++++------------------ drivers/mtd/nand/nand_micron.c | 1 - drivers/mtd/nand/sh_flctl.c | 6 +-- drivers/mtd/nand/sunxi_nand.c | 34 +++++++++----- drivers/mtd/nand/tango_nand.c | 1 - drivers/mtd/nand/vf610_nfc.c | 6 +-- drivers/staging/mt29f_spinand/mt29f_spinand.c | 7 ++- include/linux/mtd/rawnand.h | 11 ----- 21 files changed, 142 insertions(+), 124 deletions(-)
drivers/mtd/nand/atmel/nand-controller.c +5 -2
@@ -841,6 +841,8 @@ static int atmel_nand_pmecc_write_pg(struct nand_chip *chip, const u8 *buf,struct atmel_nand *nand =to_atmel_nand (chip);int ret; +nand_prog_page_begin_op (chip, page,0 ,NULL ,0 ); + ret =atmel_nand_pmecc_enable (chip,NAND_ECC_WRITE , raw);if (ret)return ret;
@@ -857,7 +859,7 @@ static int atmel_nand_pmecc_write_pg(struct nand_chip *chip, const u8 *buf,atmel_nand_write_buf (mtd, chip->oob_poi, mtd->oobsize); -return 0 ; +return nand_prog_page_end_op (chip); } static int atmel_nand_pmecc_write_page(struct mtd_info *mtd,
@@ -881,6 +883,8 @@ static int atmel_nand_pmecc_read_pg(struct nand_chip *chip, u8 *buf,struct mtd_info *mtd =nand_to_mtd (chip);int ret; +nand_read_page_op (chip, page,0 ,NULL ,0 ); + ret =atmel_nand_pmecc_enable (chip,NAND_ECC_READ , raw);if (ret)return ret;
@@ -1178,7 +1182,6 @@ static int atmel_hsmc_nand_ecc_init(struct atmel_nand *nand) chip->ecc.write_page = atmel_hsmc_nand_pmecc_write_page; chip->ecc.read_page_raw = atmel_hsmc_nand_pmecc_read_page_raw; chip->ecc.write_page_raw = atmel_hsmc_nand_pmecc_write_page_raw; - chip->ecc.options |=NAND_ECC_CUSTOM_PAGE_ACCESS ;return 0 ; }
drivers/mtd/nand/bf5xx_nand.c +4 -2
@@ -572,6 +572,8 @@ static void bf5xx_nand_dma_write_buf(struct mtd_info *mtd,static int bf5xx_nand_read_page_raw (struct mtd_info *mtd,struct nand_chip *chip,uint8_t *buf,int oob_required,int page) { +nand_read_page_op (chip, page,0 ,NULL ,0 ); +bf5xx_nand_read_buf (mtd, buf, mtd->writesize);bf5xx_nand_read_buf (mtd, chip->oob_poi, mtd->oobsize);
@@ -582,10 +584,10 @@ static int bf5xx_nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int oob_required, int page) { - bf5xx_nand_write_buf(mtd, buf, mtd->writesize); + nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize);bf5xx_nand_write_buf (mtd, chip->oob_poi, mtd->oobsize); -return 0 ; +return nand_prog_page_end_op (chip); }/*
drivers/mtd/nand/brcmnand/brcmnand.c +10 -3
@@ -1689,7 +1689,6 @@ static int brcmstb_nand_verify_erased_page(struct mtd_info *mtd, sas = mtd->oobsize / chip->ecc.steps;/* read without ecc for verification */ -nand_read_page_op (chip, page,0 ,NULL ,0 ); ret = chip->ecc.read_page_raw (mtd, chip, buf, true, page);if (ret)return ret;
@@ -1793,6 +1792,8 @@ static int brcmnand_read_page(struct mtd_info *mtd, struct nand_chip *chip,struct brcmnand_host *host =nand_get_controller_data (chip);u8 *oob = oob_required ? (u8 *)chip->oob_poi :NULL ; +nand_read_page_op (chip, page,0 ,NULL ,0 ); +return brcmnand_read (mtd, chip, host->last_addr, mtd->writesize >>FC_SHIFT , (u32 *)buf, oob); }
@@ -1804,6 +1805,8 @@ static int brcmnand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,u8 *oob = oob_required ? (u8 *)chip->oob_poi :NULL ;int ret; +nand_read_page_op (chip, page,0 ,NULL ,0 ); +brcmnand_set_ecc_enabled (host,0 ); ret =brcmnand_read (mtd, chip, host->last_addr, mtd->writesize >>FC_SHIFT , (u32 *)buf, oob);
@@ -1909,8 +1912,10 @@ static int brcmnand_write_page(struct mtd_info *mtd, struct nand_chip *chip,struct brcmnand_host *host =nand_get_controller_data (chip);void *oob = oob_required ? chip->oob_poi :NULL ; +nand_prog_page_begin_op (chip, page,0 ,NULL ,0 );brcmnand_write (mtd, chip, host->last_addr, (const u32 *)buf, oob); -return 0 ; + +return nand_prog_page_end_op (chip); } static int brcmnand_write_page_raw(struct mtd_info *mtd,
@@ -1920,10 +1925,12 @@ static int brcmnand_write_page_raw(struct mtd_info *mtd,struct brcmnand_host *host =nand_get_controller_data (chip);void *oob = oob_required ? chip->oob_poi :NULL ; +nand_prog_page_begin_op (chip, page,0 ,NULL ,0 );brcmnand_set_ecc_enabled (host,0 );brcmnand_write (mtd, chip, host->last_addr, (const u32 *)buf, oob);brcmnand_set_ecc_enabled (host,1 ); -return 0 ; + +return nand_prog_page_end_op (chip); } static int brcmnand_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
drivers/mtd/nand/cafe_nand.c +3 -3
@@ -383,7 +383,7 @@ static int cafe_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip,cafe_readl (cafe,NAND_ECC_RESULT ), cafe_readl(cafe, NAND_ECC_SYN01)); - chip->read_buf (mtd, buf, mtd->writesize); +nand_read_page_op (chip, page,0 , buf, mtd->writesize); chip->read_buf (mtd, chip->oob_poi, mtd->oobsize);if (checkecc &&cafe_readl (cafe,NAND_ECC_RESULT ) & (1 <<18 )) {
@@ -541,13 +541,13 @@ static int cafe_nand_write_page_lowlevel(struct mtd_info *mtd, {struct cafe_priv *cafe =nand_get_controller_data (chip); - chip->write_buf (mtd, buf, mtd->writesize); +nand_prog_page_begin_op (chip, page,0 , buf, mtd->writesize); chip->write_buf (mtd, chip->oob_poi, mtd->oobsize);/* Set up ECC autogeneration */ cafe->ctl2 |= (1 <<30 ); -return 0 ; +return nand_prog_page_end_op (chip); }static int cafe_nand_block_bad (struct mtd_info *mtd,loff_t ofs)
drivers/mtd/nand/denali.c +12 -2
@@ -550,11 +550,14 @@ static int denali_pio_read(struct denali_nand_info *denali, void *buf,static int denali_pio_write (struct denali_nand_info *denali,const void *buf,size_t size,int page,int raw) { +struct nand_chip *chip = &denali->nand;u32 addr =DENALI_MAP01 |DENALI_BANK (denali) | page;const uint32_t *buf32 = (uint32_t *)buf;uint32_t irq_status;int i; +nand_prog_page_begin_op (chip, page,0 ,NULL ,0 ); +denali_reset_irq (denali);for (i =0 ; i < size /4 ; i++)
@@ -580,6 +583,7 @@ static int denali_pio_xfer(struct denali_nand_info *denali, void *buf,static int denali_dma_xfer (struct denali_nand_info *denali,void *buf,size_t size,int page,int raw,int write) { +struct nand_chip *chip = &denali->nand;dma_addr_t dma_addr;uint32_t irq_mask, irq_status, ecc_err_mask;enum dma_data_direction dir = write ?DMA_TO_DEVICE :DMA_FROM_DEVICE ;
@@ -625,7 +629,10 @@ static int denali_dma_xfer(struct denali_nand_info *denali, void *buf,if (irq_status &INTR__ERASED_PAGE )memset (buf,0xff , size); -return ret; +if (ret) +return ret; + +return nand_prog_page_end_op (chip); } static int denali_data_xfer(struct denali_nand_info *denali, void *buf,
@@ -790,6 +797,8 @@ static int denali_write_oob(struct mtd_info *mtd, struct nand_chip *chip,struct denali_nand_info *denali =mtd_to_denali (mtd);int status; +nand_read_page_op (chip, page,0 ,NULL ,0 ); +denali_reset_irq (denali);denali_oob_xfer (mtd, chip, page,1 );
@@ -846,6 +855,8 @@ static int denali_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,size_t size = writesize + oobsize;int i, pos, len; +nand_read_page_op (chip, page,0 ,NULL ,0 ); +/* * Fill the buffer with 0xff first except the full page transfer. * This simplifies the logic.
@@ -1359,7 +1370,6 @@ int denali_init(struct denali_nand_info *denali) chip->read_buf = denali_read_buf; chip->write_buf = denali_write_buf; } - chip->ecc.options |=NAND_ECC_CUSTOM_PAGE_ACCESS ; chip->ecc.read_page = denali_read_page; chip->ecc.read_page_raw = denali_read_page_raw; chip->ecc.write_page = denali_write_page;
drivers/mtd/nand/docg4.c +8 -4
@@ -785,6 +785,8 @@ static int read_page(struct mtd_info *mtd, struct nand_chip *nand,dev_dbg (doc->dev,"%s: page %08x\n" , __func__, page); +nand_read_page_op (nand, page,0 ,NULL ,0 ); + writew(DOC_ECCCONF0_READ_MODE | DOC_ECCCONF0_ECC_ENABLE | DOC_ECCCONF0_UNKNOWN |
@@ -948,7 +950,7 @@ static int docg4_erase_block(struct mtd_info *mtd, int page) }static int write_page (struct mtd_info *mtd,struct nand_chip *nand, -const uint8_t *buf,bool use_ecc) +const uint8_t *buf,int page,bool use_ecc) {struct docg4_priv *doc =nand_get_controller_data (nand);void __iomem *docptr = doc->virtadr;
@@ -956,6 +958,8 @@ static int write_page(struct mtd_info *mtd, struct nand_chip *nand,dev_dbg (doc->dev,"%s...\n" , __func__); +nand_prog_page_begin_op (nand, page,0 ,NULL ,0 ); + writew(DOC_ECCCONF0_ECC_ENABLE | DOC_ECCCONF0_UNKNOWN | DOCG4_BCH_SIZE,
@@ -1000,19 +1004,19 @@ static int write_page(struct mtd_info *mtd, struct nand_chip *nand,writew (0 , docptr +DOC_DATAEND );write_nop (docptr); -return 0 ; +return nand_prog_page_end_op (nand); }static int docg4_write_page_raw (struct mtd_info *mtd,struct nand_chip *nand,const uint8_t *buf,int oob_required,int page) { -return write_page (mtd, nand, buf, false); +return write_page (mtd, nand, buf, page, false); }static int docg4_write_page (struct mtd_info *mtd,struct nand_chip *nand,const uint8_t *buf,int oob_required,int page) { -return write_page (mtd, nand, buf, true); +return write_page (mtd, nand, buf, page, true); } static int docg4_write_oob(struct mtd_info *mtd, struct nand_chip *nand,
drivers/mtd/nand/fsl_elbc_nand.c +3 -3
@@ -713,7 +713,7 @@ static int fsl_elbc_read_page(struct mtd_info *mtd, struct nand_chip *chip,struct fsl_lbc_ctrl *ctrl = priv->ctrl;struct fsl_elbc_fcm_ctrl *elbc_fcm_ctrl = ctrl->nand; -fsl_elbc_read_buf (mtd, buf, mtd->writesize); +nand_read_page_op (chip, page,0 , buf, mtd->writesize);if (oob_required)fsl_elbc_read_buf (mtd, chip->oob_poi, mtd->oobsize);
@@ -729,10 +729,10 @@ static int fsl_elbc_read_page(struct mtd_info *mtd, struct nand_chip *chip,static int fsl_elbc_write_page (struct mtd_info *mtd,struct nand_chip *chip,const uint8_t *buf,int oob_required,int page) { -fsl_elbc_write_buf (mtd, buf, mtd->writesize); +nand_prog_page_begin_op (chip, page,0 , buf, mtd->writesize);fsl_elbc_write_buf (mtd, chip->oob_poi, mtd->oobsize); -return 0 ; +return nand_prog_page_end_op (chip); }/* ECC will be calculated automatically, and errors will be detected in
drivers/mtd/nand/fsl_ifc_nand.c +3 -3
@@ -688,7 +688,7 @@ static int fsl_ifc_read_page(struct mtd_info *mtd, struct nand_chip *chip,struct fsl_ifc_ctrl *ctrl = priv->ctrl;struct fsl_ifc_nand_ctrl *nctrl = ifc_nand_ctrl; -fsl_ifc_read_buf (mtd, buf, mtd->writesize); +nand_read_page_op (chip, page,0 , buf, mtd->writesize);if (oob_required)fsl_ifc_read_buf (mtd, chip->oob_poi, mtd->oobsize);
@@ -711,10 +711,10 @@ static int fsl_ifc_read_page(struct mtd_info *mtd, struct nand_chip *chip,static int fsl_ifc_write_page (struct mtd_info *mtd,struct nand_chip *chip,const uint8_t *buf,int oob_required,int page) { -fsl_ifc_write_buf (mtd, buf, mtd->writesize); +nand_prog_page_begin_op (chip, page,0 , buf, mtd->writesize);fsl_ifc_write_buf (mtd, chip->oob_poi, mtd->oobsize); -return 0 ; +return nand_prog_page_end_op (chip); }static int fsl_ifc_chip_init_tail (struct mtd_info *mtd)
drivers/mtd/nand/gpmi-nand/gpmi-nand.c +16 -14
@@ -1043,6 +1043,8 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,unsigned int max_bitflips =0 ;int ret; +nand_read_page_op (chip, page,0 ,NULL ,0 ); +dev_dbg (this->dev,"page number is : %d\n" , page); ret = read_page_prepare(this, buf, nfc_geo->payload_size, this->payload_virt, this->payload_phys,
@@ -1220,12 +1222,13 @@ static int gpmi_ecc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, meta = geo->metadata_size;if (first) { col = meta + (size + ecc_parity_size) * first; - chip->cmdfunc (mtd,NAND_CMD_RNDOUT , col,-1 ); meta =0 ; buf = buf + first * size; } +nand_read_page_op (chip, page, col,NULL ,0 ); +/* Save the old environment */ r1_old = r1_new =readl (bch_regs +HW_BCH_FLASH0LAYOUT0 ); r2_old = r2_new =readl (bch_regs +HW_BCH_FLASH0LAYOUT1 );
@@ -1277,6 +1280,9 @@ static int gpmi_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip,int ret;dev_dbg (this->dev,"ecc write page.\n" ); + +nand_prog_page_begin_op (chip, page,0 ,NULL ,0 ); +if (this->swap_block_mark) {/* * If control arrives here, we're doing block mark swapping.
@@ -1338,7 +1344,10 @@ static int gpmi_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip, payload_virt, payload_phys); } -return 0 ; +if (ret) +return ret; + +return nand_prog_page_end_op (chip); }/*
@@ -1472,8 +1481,8 @@ static int gpmi_ecc_read_page_raw(struct mtd_info *mtd,uint8_t *oob = chip->oob_poi;int step; - chip->read_buf (mtd, tmp_buf, - mtd->writesize + mtd->oobsize); +nand_read_page_op (chip, page,0 , tmp_buf, + mtd->writesize + mtd->oobsize);/* * If required, swap the bad block marker and the data stored in the
@@ -1617,24 +1626,19 @@ static int gpmi_ecc_write_page_raw(struct mtd_info *mtd, tmp_buf[mtd->writesize] = swap; } - chip->write_buf (mtd, tmp_buf, mtd->writesize + mtd->oobsize); - -return 0 ; +return nand_prog_page_op (chip, page,0 , tmp_buf, + mtd->writesize + mtd->oobsize); }static int gpmi_ecc_read_oob_raw (struct mtd_info *mtd,struct nand_chip *chip,int page) { -nand_read_page_op (chip, page,0 ,NULL ,0 ); -return gpmi_ecc_read_page_raw (mtd, chip,NULL ,1 , page); }static int gpmi_ecc_write_oob_raw (struct mtd_info *mtd,struct nand_chip *chip,int page) { -nand_prog_page_begin_op (chip, page,0 ,NULL ,0 ); -return gpmi_ecc_write_page_raw (mtd, chip,NULL ,1 , page); }
@@ -1806,9 +1810,7 @@ static int mx23_write_transcription_stamp(struct gpmi_nand_data *this)/* Write the first page of the current stride. */ dev_dbg (dev,"Writing an NCB fingerprint in page 0x%x\n" , page); -nand_prog_page_begin_op (chip, page,0 ,NULL ,0 ); - chip->ecc.write_page_raw (mtd, chip, buffer,0 , page); - status =nand_prog_page_end_op (chip); + status = chip->ecc.write_page_raw (mtd, chip, buffer,0 , page);if (status)dev_err (dev,"[%s] Write failed.\n" , __func__); }
drivers/mtd/nand/hisi504_nand.c +3 -3
@@ -544,7 +544,7 @@ static int hisi_nand_read_page_hwecc(struct mtd_info *mtd,int max_bitflips =0 , stat =0 , stat_max =0 , status_ecc;int stat_1, stat_2; - chip->read_buf (mtd, buf, mtd->writesize); +nand_read_page_op (chip, page,0 , buf, mtd->writesize); chip->read_buf (mtd, chip->oob_poi, mtd->oobsize);/* errors which can not be corrected by ECC */
@@ -589,11 +589,11 @@ static int hisi_nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int oob_required, int page) { - chip->write_buf(mtd, buf, mtd->writesize); + nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize);if (oob_required) chip->write_buf (mtd, chip->oob_poi, mtd->oobsize); -return 0 ; +return nand_prog_page_end_op (chip); }static void hisi_nfc_host_init (struct hinfc_host *host)
drivers/mtd/nand/lpc32xx_mlc.c +4 -1
@@ -522,6 +522,8 @@ static int lpc32xx_write_page_lowlevel(struct mtd_info *mtd,memcpy (dma_buf, buf, mtd->writesize); } +nand_prog_page_begin_op (chip, page,0 ,NULL ,0 ); +for (i =0 ; i < host->mlcsubpages; i++) {/* Start Encode */ writeb (0x00 ,MLC_ECC_ENC_REG (host->io_base));
@@ -550,7 +552,8 @@ static int lpc32xx_write_page_lowlevel(struct mtd_info *mtd,/* Wait for Controller Ready */ lpc32xx_waitfunc_controller (mtd, chip); } -return 0 ; + +return nand_prog_page_end_op (chip); } static int lpc32xx_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
drivers/mtd/nand/lpc32xx_slc.c +8 -3
@@ -686,6 +686,8 @@ static int lpc32xx_nand_write_page_syndrome(struct mtd_info *mtd,uint8_t *pb;int error; +nand_prog_page_begin_op (chip, page,0 ,NULL ,0 ); +/* Write data, calculate ECC on outbound data */ error =lpc32xx_xfer (mtd, (uint8_t *)buf, chip->ecc.steps,0 );if (error)
@@ -704,7 +706,8 @@ static int lpc32xx_nand_write_page_syndrome(struct mtd_info *mtd,/* Write ECC data to device */ chip->write_buf (mtd, chip->oob_poi, mtd->oobsize); -return 0 ; + +return nand_prog_page_end_op (chip); }/*
@@ -717,9 +720,11 @@ static int lpc32xx_nand_write_page_raw_syndrome(struct mtd_info *mtd, int oob_required, int page) {/* Raw writes can just use the FIFO interface */ - chip->write_buf (mtd, buf, chip->ecc.size * chip->ecc.steps); + nand_prog_page_begin_op(chip, page, 0, buf, + chip->ecc.size * chip->ecc.steps); chip->write_buf (mtd, chip->oob_poi, mtd->oobsize); -return 0 ; + +return nand_prog_page_end_op (chip); }static int lpc32xx_nand_dma_setup (struct lpc32xx_nand_host *host)
drivers/mtd/nand/mtk_nand.c +7 -3
@@ -761,6 +761,8 @@ static int mtk_nfc_write_page(struct mtd_info *mtd, struct nand_chip *chip,u32 reg;int ret; +nand_prog_page_begin_op (chip, page,0 ,NULL ,0 ); +if (!raw) {/* OOB => FDM: from register, ECC: from HW */ reg =nfi_readw (nfc,NFI_CNFG ) |CNFG_AUTO_FMT_EN ;
@@ -794,7 +796,10 @@ static int mtk_nfc_write_page(struct mtd_info *mtd, struct nand_chip *chip,if (!raw)mtk_ecc_disable (nfc->ecc); -return ret; +if (ret) +return ret; + +return nand_prog_page_end_op (chip); } static int mtk_nfc_write_page_hwecc(struct mtd_info *mtd,
@@ -889,8 +894,7 @@ static int mtk_nfc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, len = sectors * chip->ecc.size + (raw ? sectors * spare :0 ); buf = bufpoi + start * chip->ecc.size; -if (column !=0 ) - chip->cmdfunc (mtd,NAND_CMD_RNDOUT , column,-1 ); +nand_read_page_op (chip, page, column,NULL ,0 ); addr =dma_map_single (nfc->dev, buf, len,DMA_FROM_DEVICE ); rc =dma_mapping_error (nfc->dev, addr);
drivers/mtd/nand/nand_base.c +23 -45
153 lines not on this page · raw message
drivers/mtd/nand/nand_micron.c +0 -1
@@ -272,7 +272,6 @@ static int micron_nand_init(struct nand_chip *chip)return -EINVAL ; } - chip->ecc.options =NAND_ECC_CUSTOM_PAGE_ACCESS ; chip->ecc.bytes =8 ; chip->ecc.size =512 ; chip->ecc.strength =4 ;
drivers/mtd/nand/sh_flctl.c +3 -3
@@ -614,7 +614,7 @@ static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_vastatic int flctl_read_page_hwecc (struct mtd_info *mtd,struct nand_chip *chip,uint8_t *buf,int oob_required,int page) { - chip->read_buf (mtd, buf, mtd->writesize); +nand_read_page_op (chip, page,0 , buf, mtd->writesize);if (oob_required) chip->read_buf (mtd, chip->oob_poi, mtd->oobsize);return 0 ;
@@ -624,9 +624,9 @@ static int flctl_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int oob_required, int page) { - chip->write_buf(mtd, buf, mtd->writesize); + nand_prog_page_begin_op(chip, page, 0, buf, mtd->writesize); chip->write_buf (mtd, chip->oob_poi, mtd->oobsize); -return 0 ; +return nand_prog_page_end_op (chip); }static void execmd_read_page_sector (struct mtd_info *mtd,int page_addr)
drivers/mtd/nand/sunxi_nand.c +22 -12
@@ -1245,6 +1245,8 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd,int ret, i, cur_off =0 ;bool raw_mode = false; +nand_read_page_op (chip, page,0 ,NULL ,0 ); +sunxi_nfc_hw_ecc_enable (mtd);for (i =0 ; i < ecc->steps; i++) {
@@ -1278,14 +1280,14 @@ static int sunxi_nfc_hw_ecc_read_page_dma(struct mtd_info *mtd, {int ret; +nand_read_page_op (chip, page,0 ,NULL ,0 ); + ret =sunxi_nfc_hw_ecc_read_chunks_dma (mtd, buf, oob_required, page, chip->ecc.steps);if (ret >=0 )return ret;/* Fallback to PIO mode */ - chip->cmdfunc (mtd,NAND_CMD_RNDOUT ,0 ,-1 ); -return sunxi_nfc_hw_ecc_read_page (mtd, chip, buf, oob_required, page); }
@@ -1298,6 +1300,8 @@ static int sunxi_nfc_hw_ecc_read_subpage(struct mtd_info *mtd,int ret, i, cur_off =0 ;unsigned int max_bitflips =0 ; +nand_read_page_op (chip, page,0 ,NULL ,0 ); +sunxi_nfc_hw_ecc_enable (mtd); for (i = data_offs / ecc->size;
@@ -1329,13 +1333,13 @@ static int sunxi_nfc_hw_ecc_read_subpage_dma(struct mtd_info *mtd,int nchunks =DIV_ROUND_UP (data_offs + readlen, chip->ecc.size);int ret; +nand_read_page_op (chip, page,0 ,NULL ,0 ); + ret =sunxi_nfc_hw_ecc_read_chunks_dma (mtd, buf, false, page, nchunks);if (ret >=0 )return ret;/* Fallback to PIO mode */ - chip->cmdfunc (mtd,NAND_CMD_RNDOUT ,0 ,-1 ); -return sunxi_nfc_hw_ecc_read_subpage (mtd, chip, data_offs, readlen, buf, page); }
@@ -1348,6 +1352,8 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd,struct nand_ecc_ctrl *ecc = &chip->ecc;int ret, i, cur_off =0 ; +nand_prog_page_begin_op (chip, page,0 ,NULL ,0 ); +sunxi_nfc_hw_ecc_enable (mtd);for (i =0 ; i < ecc->steps; i++) {
@@ -1369,7 +1375,7 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd,sunxi_nfc_hw_ecc_disable (mtd); -return 0 ; +return nand_prog_page_end_op (chip); } static int sunxi_nfc_hw_ecc_write_subpage(struct mtd_info *mtd,
@@ -1381,6 +1387,8 @@ static int sunxi_nfc_hw_ecc_write_subpage(struct mtd_info *mtd,struct nand_ecc_ctrl *ecc = &chip->ecc;int ret, i, cur_off =0 ; +nand_prog_page_begin_op (chip, page,0 ,NULL ,0 ); +sunxi_nfc_hw_ecc_enable (mtd); for (i = data_offs / ecc->size;
@@ -1399,7 +1407,7 @@ static int sunxi_nfc_hw_ecc_write_subpage(struct mtd_info *mtd,sunxi_nfc_hw_ecc_disable (mtd); -return 0 ; +return nand_prog_page_end_op (chip); } static int sunxi_nfc_hw_ecc_write_page_dma(struct mtd_info *mtd,
@@ -1429,6 +1437,8 @@ static int sunxi_nfc_hw_ecc_write_page_dma(struct mtd_info *mtd,sunxi_nfc_hw_ecc_set_prot_oob_bytes (mtd, oob, i, !i, page); } +nand_prog_page_begin_op (chip, page,0 ,NULL ,0 ); +sunxi_nfc_hw_ecc_enable (mtd);sunxi_nfc_randomizer_config (mtd, page, false);sunxi_nfc_randomizer_enable (mtd);
@@ -1459,7 +1469,7 @@ static int sunxi_nfc_hw_ecc_write_page_dma(struct mtd_info *mtd,sunxi_nfc_hw_ecc_write_extra_oob (mtd, chip->oob_poi,NULL , page); -return 0 ; +return nand_prog_page_end_op (chip); pio_fallback:return sunxi_nfc_hw_ecc_write_page (mtd, chip, buf, oob_required, page);
@@ -1475,6 +1485,8 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd,int ret, i, cur_off =0 ;bool raw_mode = false; +nand_read_page_op (chip, page,0 ,NULL ,0 ); +sunxi_nfc_hw_ecc_enable (mtd);for (i =0 ; i < ecc->steps; i++) {
@@ -1511,6 +1523,8 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd,struct nand_ecc_ctrl *ecc = &chip->ecc;int ret, i, cur_off =0 ; +nand_prog_page_begin_op (chip, page,0 ,NULL ,0 ); +sunxi_nfc_hw_ecc_enable (mtd);for (i =0 ; i < ecc->steps; i++) {
@@ -1532,15 +1546,13 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd,sunxi_nfc_hw_ecc_disable (mtd); -return 0 ; +return nand_prog_page_end_op (chip); }static int sunxi_nfc_hw_common_ecc_read_oob (struct mtd_info *mtd,struct nand_chip *chip,int page) { -nand_read_page_op (chip, page,0 ,NULL ,0 ); - chip->pagebuf =-1 ;return chip->ecc.read_page (mtd, chip, chip->buffers->databuf,1 , page);
@@ -1552,8 +1564,6 @@ static int sunxi_nfc_hw_common_ecc_write_oob(struct mtd_info *mtd, {int ret; -nand_prog_page_begin_op (chip, page,0 ,NULL ,0 ); - chip->pagebuf =-1 ;memset (chip->buffers->databuf,0xff , mtd->writesize);
drivers/mtd/nand/tango_nand.c +0 -1
@@ -580,7 +580,6 @@ static int chip_init(struct device *dev, struct device_node *np) ecc->write_page = tango_write_page; ecc->read_oob = tango_read_oob; ecc->write_oob = tango_write_oob; - ecc->options =NAND_ECC_CUSTOM_PAGE_ACCESS ; err =nand_scan_tail (mtd);if (err)
drivers/mtd/nand/vf610_nfc.c +3 -3
@@ -560,7 +560,7 @@ static int vf610_nfc_read_page(struct mtd_info *mtd, struct nand_chip *chip,int eccsize = chip->ecc.size;int stat; -vf610_nfc_read_buf (mtd, buf, eccsize); +nand_read_page_op (chip, page,0 , buf, eccsize);if (oob_required)vf610_nfc_read_buf (mtd, chip->oob_poi, mtd->oobsize);
@@ -580,7 +580,7 @@ static int vf610_nfc_write_page(struct mtd_info *mtd, struct nand_chip *chip, {struct vf610_nfc *nfc =mtd_to_nfc (mtd); -vf610_nfc_write_buf (mtd, buf, mtd->writesize); +nand_prog_page_begin_op (chip, page,0 , buf, mtd->writesize);if (oob_required)vf610_nfc_write_buf (mtd, chip->oob_poi, mtd->oobsize);
@@ -588,7 +588,7 @@ static int vf610_nfc_write_page(struct mtd_info *mtd, struct nand_chip *chip, nfc->use_hw_ecc = true; nfc->write_sz = mtd->writesize + mtd->oobsize; -return 0 ; +return nand_prog_page_end_op (chip); }static const struct of_device_id vf610_nfc_dt_ids[] = {
drivers/staging/mt29f_spinand/mt29f_spinand.c +5 -2
@@ -636,9 +636,12 @@ static int spinand_write_page_hwecc(struct mtd_info *mtd,int eccsize = chip->ecc.size;int eccsteps = chip->ecc.steps; +nand_prog_page_begin_op (chip, page,0 ,NULL ,0 ); + enable_hw_ecc =1 ; chip->write_buf (mtd, p, eccsize * eccsteps); -return 0 ; + +return nand_prog_page_end_op (chip); } static int spinand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
@@ -653,7 +656,7 @@ static int spinand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, enable_read_hw_ecc =1 ; - chip->read_buf (mtd, p, eccsize * eccsteps); +nand_read_page_op (chip, page,0 , p, eccsize * eccsteps);if (oob_required) chip->read_buf (mtd, chip->oob_poi, mtd->oobsize);
include/linux/mtd/rawnand.h +0 -11
@@ -133,12 +133,6 @@ enum nand_ecc_algo {*/ #define NAND_ECC_GENERIC_ERASED_CHECK BIT(0)#define NAND_ECC_MAXIMIZE BIT(1) -/* -* If your controller already sends the required NAND commands when -* reading or writing a page, then the framework is not supposed to -* send READ0 and SEQIN/PAGEPROG respectively. -*/ -#define NAND_ECC_CUSTOM_PAGE_ACCESS BIT(2)/* Bit mask for flags passed to do_nand_read_ecc */ #define NAND_GET_DEVICE 0x80
@@ -602,11 +596,6 @@ struct nand_ecc_ctrl { int page); }; -static inline int nand_standard_page_accessors (struct nand_ecc_ctrl *ecc) -{ -return !(ecc->options &NAND_ECC_CUSTOM_PAGE_ACCESS ); -} -/** * struct nand_buffers - buffer structure for read/write * @ecccalc: buffer pointer for calculated ECC, size is oobsize.
Signature · 1 line
2.11.0
RFC 2/12 mtd: nand: force drivers to explicitly send READ/PROG commands
Hi Miquel,
Thanks for the work on this, happy to see the new interface is moving forward. Some comments below.
Quoted 54 lines · Miquel Raynal
From: Boris Brezillon <[email protected]>
The core currently send the READ0 and SEQIN+PAGEPROG commands in nand_do_read/write_ops(). This is inconsistent with ->read/write_oob[_raw]() hooks behavior which are expected to send these commands.
There's already a flag (
NAND_ECC_CUSTOM_PAGE_ACCESS) to inform the core that a specific controller wants to send the READ/SEQIN+PAGEPROG commands on its own, but it's an opt-in flag, and existing drivers are unlikely to be updated to pass it.Moreover, some controllers cannot dissociate the READ/PAGEPROG commands from the associated data transfer and ECC engine activation, and developers have to hack things in their ->cmdfunc() implementation to handle such complex cases, or have to accept the perf penalty of sending twice the same command.
To address this problem we are planning on adding a new interface which is passed all information about a NAND operation (including the amount of data to transfer) and replacing all calls to ->cmdfunc() to calls to this new ->exec_op() hook. But, in order to do that, we need to have all ->cmdfunc() calls placed near their associated ->read/write_buf/byte() calls.Modify the core and relevant drivers to make
NAND_ECC_CUSTOM_PAGE_ACCESSthe default case, and remove this flag.Signed-off-by Boris Brezillon <[email protected]>[miquel.raynal at free-electrons.com: rebased and fixed some conflicts]Signed-off-by Miquel Raynal <[email protected]>--- drivers/mtd/nand/atmel/nand-controller.c | 7 ++- drivers/mtd/nand/bf5xx_nand.c | 6 ++- drivers/mtd/nand/brcmnand/brcmnand.c | 13 +++-- drivers/mtd/nand/cafe_nand.c | 6 +-- drivers/mtd/nand/denali.c | 14 +++++- drivers/mtd/nand/docg4.c | 12 +++-- drivers/mtd/nand/fsl_elbc_nand.c | 6 +-- drivers/mtd/nand/fsl_ifc_nand.c | 6 +-- drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 30 ++++++------ drivers/mtd/nand/hisi504_nand.c | 6 +-- drivers/mtd/nand/lpc32xx_mlc.c | 5 +- drivers/mtd/nand/lpc32xx_slc.c | 11 +++-- drivers/mtd/nand/mtk_nand.c | 10 ++-- drivers/mtd/nand/nand_base.c | 68 +++++++++------------------ drivers/mtd/nand/nand_micron.c | 1 - drivers/mtd/nand/sh_flctl.c | 6 +-- drivers/mtd/nand/sunxi_nand.c | 34 +++++++++----- drivers/mtd/nand/tango_nand.c | 1 - drivers/mtd/nand/vf610_nfc.c | 6 +-- drivers/staging/mt29f_spinand/mt29f_spinand.c | 7 ++- include/linux/mtd/rawnand.h | 11 ----- 21 files changed, 142 insertions(+), 124 deletions(-)
<snip>
Quoted 16 lines
drivers/mtd/nand/gpmi-nand/gpmi-nand.c +2
-0@@ -1043,6 +1043,8 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, unsigned int max_bitflips = 0;int ret; +nand_read_page_op (chip, page,0 ,NULL ,0 ); +dev_dbg (this->dev,"page number is : %d\n" , page); ret = read_page_prepare(this, buf, nfc_geo->payload_size,drivers/mtd/nand/gpmi-nand/gpmi-nand.c +0
-0this->payload_virt, this->payload_phys,
drivers/mtd/nand/gpmi-nand/gpmi-nand.c static int gpmi_ecc_read_subpage(struct · line 1224
Quoted 5 lines
drivers/mtd/nand/gpmi-nand/gpmi-nand.c +0
-1@@ -1220,12 +1222,13 @@ static int gpmi_ecc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, meta = geo->metadata_size;if (first) { col = meta + (size + ecc_parity_size) * first; - chip->cmdfunc (mtd,NAND_CMD_RNDOUT , col,-1 );
Shouldn't we add nand_change_read_column_op here?
Quoted 84 lines
drivers/mtd/nand/gpmi-nand/gpmi-nand.c +5
-0meta =0 ; buf = buf + first * size; } +nand_read_page_op (chip, page, col,NULL ,0 ); +/* Save the old environment */ r1_old = r1_new =readl (bch_regs +HW_BCH_FLASH0LAYOUT0 ); r2_old = r2_new =readl (bch_regs +HW_BCH_FLASH0LAYOUT1 );@@ -1277,6 +1280,9 @@ static int gpmi_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip, int ret;dev_dbg (this->dev,"ecc write page.\n" ); + +nand_prog_page_begin_op (chip, page,0 ,NULL ,0 ); +if (this->swap_block_mark) {/* drivers/mtd/nand/gpmi-nand/gpmi-nand.c +4
-1* If control arrives here, we're doing block mark swapping. @@ -1338,7 +1344,10 @@ static int gpmi_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip, payload_virt, payload_phys); } -return 0 ; +if (ret) +return ret; + +return nand_prog_page_end_op (chip); }/*
drivers/mtd/nand/gpmi-nand/gpmi-nand.c +5
-12@@ -1472,8 +1481,8 @@ static int gpmi_ecc_read_page_raw(struct mtd_info *mtd,uint8_t *oob = chip->oob_poi;int step; - chip->read_buf (mtd, tmp_buf, - mtd->writesize + mtd->oobsize); +nand_read_page_op (chip, page,0 , tmp_buf, + mtd->writesize + mtd->oobsize);/* * If required, swap the bad block marker and the data stored in the @@ -1617,24 +1626,19 @@ static int gpmi_ecc_write_page_raw(struct mtd_info *mtd, tmp_buf[mtd->writesize] = swap; } - chip->write_buf (mtd, tmp_buf, mtd->writesize + mtd->oobsize); - -return 0 ; +return nand_prog_page_op (chip, page,0 , tmp_buf, + mtd->writesize + mtd->oobsize); }static int gpmi_ecc_read_oob_raw (struct mtd_info *mtd,struct nand_chip *chip,int page) { -nand_read_page_op (chip, page,0 ,NULL ,0 ); -return gpmi_ecc_read_page_raw (mtd, chip,NULL ,1 , page); }static int gpmi_ecc_write_oob_raw (struct mtd_info *mtd,struct nand_chip *chip,int page) { -nand_prog_page_begin_op (chip, page,0 ,NULL ,0 ); -return gpmi_ecc_write_page_raw (mtd, chip,NULL ,1 , page); }@@ -1806,9 +1810,7 @@ static int mx23_write_transcription_stamp(struct gpmi_nand_data *this) /* Write the first page of the current stride. */ dev_dbg(dev, "Writing an NCB fingerprint in page 0x%x\n", page); -nand_prog_page_begin_op (chip, page,0 ,NULL ,0 ); - chip->ecc.write_page_raw (mtd, chip, buffer,0 , page); - status =nand_prog_page_end_op (chip); + status = chip->ecc.write_page_raw (mtd, chip, buffer,0 , page);if (status)dev_err (dev,"[%s] Write failed.\n" , __func__);}
<snip>
Quoted 15 lines
drivers/mtd/nand/vf610_nfc.c +1
-1@@ -560,7 +560,7 @@ static int vf610_nfc_read_page(struct mtd_info *mtd, struct nand_chip *chip, int eccsize = chip->ecc.size;int stat; -vf610_nfc_read_buf (mtd, buf, eccsize); +nand_read_page_op (chip, page,0 , buf, eccsize);if (oob_required)vf610_nfc_read_buf (mtd, chip->oob_poi, mtd->oobsize);
drivers/mtd/nand/vf610_nfc.c static int vf610_nfc_write_page(struct mtd_info · line 584
Quoted 5 lines
drivers/mtd/nand/vf610_nfc.c +0
-1@@ -580,7 +580,7 @@ static int vf610_nfc_write_page(struct mtd_info *mtd, struct nand_chip *chip, {struct vf610_nfc *nfc =mtd_to_nfc (mtd); -vf610_nfc_write_buf (mtd, buf, mtd->writesize);
We currently ignore NAND_CMD_SEQIN in ->cmdfunc anyway, so I think this change would not even be necessary here.
This is a NAND controller which will benefit from the new interface. I plan to convert the driver to the new interface, maybe I manage to do that soon so we have a second driver making use of the new interface.
Quoted 13 lines
drivers/mtd/nand/vf610_nfc.c +2
-1+nand_prog_page_begin_op (chip, page,0 , buf, mtd->writesize);if (oob_required)vf610_nfc_write_buf (mtd, chip->oob_poi, mtd->oobsize);@@ -588,7 +588,7 @@ static int vf610_nfc_write_page(struct mtd_info *mtd, struct nand_chip *chip, nfc->use_hw_ecc = true; nfc->write_sz = mtd->writesize + mtd->oobsize; -return 0 ; +return nand_prog_page_end_op (chip); }
Quoted 1 line
static const struct
of_device_idvf610_nfc_dt_ids[] = {
--
Stefan
RFC 2/12 mtd: nand: force drivers to explicitly send READ/PROG commands
Quoted 194 lines · On Fri, 20 Oct 2017 11:29:18 +0200 Stefan Agner <[email protected]> wrote:
Hi Miquel,
Thanks for the work on this, happy to see the new interface is moving forward. Some comments below.
Quoted 54 lines · Miquel Raynal
From: Boris Brezillon <[email protected]>
The core currently send the READ0 and SEQIN+PAGEPROG commands in nand_do_read/write_ops(). This is inconsistent with ->read/write_oob[_raw]() hooks behavior which are expected to send these commands.
There's already a flag (
NAND_ECC_CUSTOM_PAGE_ACCESS) to inform the core that a specific controller wants to send the READ/SEQIN+PAGEPROG commands on its own, but it's an opt-in flag, and existing drivers are unlikely to be updated to pass it.Moreover, some controllers cannot dissociate the READ/PAGEPROG commands from the associated data transfer and ECC engine activation, and developers have to hack things in their ->cmdfunc() implementation to handle such complex cases, or have to accept the perf penalty of sending twice the same command.
To address this problem we are planning on adding a new interface which is passed all information about a NAND operation (including the amount of data to transfer) and replacing all calls to ->cmdfunc() to calls to this new ->exec_op() hook. But, in order to do that, we need to have all ->cmdfunc() calls placed near their associated ->read/write_buf/byte() calls.Modify the core and relevant drivers to make
NAND_ECC_CUSTOM_PAGE_ACCESSthe default case, and remove this flag.Signed-off-by Boris Brezillon <[email protected]>[miquel.raynal at free-electrons.com: rebased and fixed some conflicts]Signed-off-by Miquel Raynal <[email protected]>--- drivers/mtd/nand/atmel/nand-controller.c | 7 ++- drivers/mtd/nand/bf5xx_nand.c | 6 ++- drivers/mtd/nand/brcmnand/brcmnand.c | 13 +++-- drivers/mtd/nand/cafe_nand.c | 6 +-- drivers/mtd/nand/denali.c | 14 +++++- drivers/mtd/nand/docg4.c | 12 +++-- drivers/mtd/nand/fsl_elbc_nand.c | 6 +-- drivers/mtd/nand/fsl_ifc_nand.c | 6 +-- drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 30 ++++++------ drivers/mtd/nand/hisi504_nand.c | 6 +-- drivers/mtd/nand/lpc32xx_mlc.c | 5 +- drivers/mtd/nand/lpc32xx_slc.c | 11 +++-- drivers/mtd/nand/mtk_nand.c | 10 ++-- drivers/mtd/nand/nand_base.c | 68 +++++++++------------------ drivers/mtd/nand/nand_micron.c | 1 - drivers/mtd/nand/sh_flctl.c | 6 +-- drivers/mtd/nand/sunxi_nand.c | 34 +++++++++----- drivers/mtd/nand/tango_nand.c | 1 - drivers/mtd/nand/vf610_nfc.c | 6 +-- drivers/staging/mt29f_spinand/mt29f_spinand.c | 7 ++- include/linux/mtd/rawnand.h | 11 ----- 21 files changed, 142 insertions(+), 124 deletions(-)<snip>
Quoted 16 lines
drivers/mtd/nand/gpmi-nand/gpmi-nand.c +2
-0@@ -1043,6 +1043,8 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, unsigned int max_bitflips = 0;int ret; +nand_read_page_op (chip, page,0 ,NULL ,0 ); +dev_dbg (this->dev,"page number is : %d\n" , page); ret = read_page_prepare(this, buf, nfc_geo->payload_size,drivers/mtd/nand/gpmi-nand/gpmi-nand.c +0
-0this->payload_virt, this->payload_phys,drivers/mtd/nand/gpmi-nand/gpmi-nand.c static int gpmi_ecc_read_subpage(struct · line 1224
Quoted 5 lines
drivers/mtd/nand/gpmi-nand/gpmi-nand.c +0
-1@@ -1220,12 +1222,13 @@ static int gpmi_ecc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, meta = geo->metadata_size;if (first) { col = meta + (size + ecc_parity_size) * first; - chip->cmdfunc (mtd,NAND_CMD_RNDOUT , col,-1 );Shouldn't we add
nand_change_read_column_ophere?Quoted 84 lines
drivers/mtd/nand/gpmi-nand/gpmi-nand.c +5
-0meta =0 ; buf = buf + first * size; } +nand_read_page_op (chip, page, col,NULL ,0 ); +/* Save the old environment */ r1_old = r1_new =readl (bch_regs +HW_BCH_FLASH0LAYOUT0 ); r2_old = r2_new =readl (bch_regs +HW_BCH_FLASH0LAYOUT1 );@@ -1277,6 +1280,9 @@ static int gpmi_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip, int ret;dev_dbg (this->dev,"ecc write page.\n" ); + +nand_prog_page_begin_op (chip, page,0 ,NULL ,0 ); +if (this->swap_block_mark) {/* drivers/mtd/nand/gpmi-nand/gpmi-nand.c +4
-1* If control arrives here, we're doing block mark swapping. @@ -1338,7 +1344,10 @@ static int gpmi_ecc_write_page(struct mtd_info *mtd, struct nand_chip *chip, payload_virt, payload_phys); } -return 0 ; +if (ret) +return ret; + +return nand_prog_page_end_op (chip); }/*
drivers/mtd/nand/gpmi-nand/gpmi-nand.c +5
-12@@ -1472,8 +1481,8 @@ static int gpmi_ecc_read_page_raw(struct mtd_info *mtd,uint8_t *oob = chip->oob_poi;int step; - chip->read_buf (mtd, tmp_buf, - mtd->writesize + mtd->oobsize); +nand_read_page_op (chip, page,0 , tmp_buf, + mtd->writesize + mtd->oobsize);/* * If required, swap the bad block marker and the data stored in the @@ -1617,24 +1626,19 @@ static int gpmi_ecc_write_page_raw(struct mtd_info *mtd, tmp_buf[mtd->writesize] = swap; } - chip->write_buf (mtd, tmp_buf, mtd->writesize + mtd->oobsize); - -return 0 ; +return nand_prog_page_op (chip, page,0 , tmp_buf, + mtd->writesize + mtd->oobsize); }static int gpmi_ecc_read_oob_raw (struct mtd_info *mtd,struct nand_chip *chip,int page) { -nand_read_page_op (chip, page,0 ,NULL ,0 ); -return gpmi_ecc_read_page_raw (mtd, chip,NULL ,1 , page); }static int gpmi_ecc_write_oob_raw (struct mtd_info *mtd,struct nand_chip *chip,int page) { -nand_prog_page_begin_op (chip, page,0 ,NULL ,0 ); -return gpmi_ecc_write_page_raw (mtd, chip,NULL ,1 , page); }@@ -1806,9 +1810,7 @@ static int mx23_write_transcription_stamp(struct gpmi_nand_data *this) /* Write the first page of the current stride. */ dev_dbg(dev, "Writing an NCB fingerprint in page 0x%x\n", page); -nand_prog_page_begin_op (chip, page,0 ,NULL ,0 ); - chip->ecc.write_page_raw (mtd, chip, buffer,0 , page); - status =nand_prog_page_end_op (chip); + status = chip->ecc.write_page_raw (mtd, chip, buffer,0 , page);if (status)dev_err (dev,"[%s] Write failed.\n" , __func__);}
<snip>
Quoted 19 lines
drivers/mtd/nand/vf610_nfc.c +1
-1@@ -560,7 +560,7 @@ static int vf610_nfc_read_page(struct mtd_info *mtd, struct nand_chip *chip, int eccsize = chip->ecc.size;int stat; -vf610_nfc_read_buf (mtd, buf, eccsize); +nand_read_page_op (chip, page,0 , buf, eccsize);if (oob_required)vf610_nfc_read_buf (mtd, chip->oob_poi, mtd->oobsize);@@ -580,7 +580,7 @@ static int vf610_nfc_write_page(struct mtd_info *mtd, struct nand_chip *chip, {struct vf610_nfc *nfc =mtd_to_nfc (mtd);
Quoted 4 lines
drivers/mtd/nand/vf610_nfc.c static int vf610_nfc_write_page(struct mtd_info *mtd, struct nand_chip *chip, · line 583
Quoted 1 line
drivers/mtd/nand/vf610_nfc.c +0
-1-vf610_nfc_write_buf (mtd, buf, mtd->writesize);We currently ignore
NAND_CMD_SEQINin ->cmdfunc anyway, so I think this change would not even be necessary here.
Well, the thing is, we're trying to keep the logic unchanged. Even if for your driver ->cmdfunc(NAND_CMD_SEQIN) is a NOP, I'd prefer to be consistent and still replace this ->write_buf() call by a nand_prog_page_begin_op() one.
Quoted 4 lines
This is a NAND controller which will benefit from the new interface. I plan to convert the driver to the new interface, maybe I manage to do that soon so we have a second driver making use of the new interface.
Great!!! Let us know if you need any help.
Regards,
Boris
Re: [RFC 02/12] mtd: nand: force drivers to explicitly send READ/PROG commands
In reply to Stefan AgnerHi Stefan,
Quoted 2 lines
Thanks for the work on this, happy to see the new interface is moving forward. Some comments below.
Thank you!
Quoted 10 lines
Quoted 10 lines
drivers/mtd/nand/gpmi-nand/gpmi-nand.c +0
-0@@ -1043,6 +1043,8 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, unsigned int max_bitflips = 0;int ret;
Quoted 13 lines
drivers/mtd/nand/gpmi-nand/gpmi-nand.c static int gpmi_ecc_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, · line 1223
Quoted 11 lines
drivers/mtd/nand/gpmi-nand/gpmi-nand.c +2
-0+nand_read_page_op (chip, page,0 ,NULL ,0 ); +dev_dbg (this->dev,"page number is : %d\n" , page); ret = read_page_prepare(this, buf, nfc_geo->payload_size,
this->payload_virt,this->payload_phys, @@ -1220,12 +1222,13 @@ static int gpmi_ecc_read_subpage(structmtd_info*mtd, structnand_chip*chip,drivers/mtd/nand/gpmi-nand/gpmi-nand.c +0
-1meta = geo->metadata_size;if (first) { col = meta + (size + ecc_parity_size) * first; - chip->cmdfunc (mtd,NAND_CMD_RNDOUT , col,-1 );Shouldn't we add
nand_change_read_column_ophere?
The change column operation is done right after with "nand_read_page_op()" that also set the page. I am pretty sure this overall operation could be handled in a cleaner way though.
Could you test this driver with ->exec_op() implementation? If you do I am interested to know if all goes well or not.
Quoted 12 lines
Quoted 12 lines
drivers/mtd/nand/gpmi-nand/gpmi-nand.c +2
-0meta =0 ; buf = buf + first * size; } +nand_read_page_op (chip, page, col,NULL ,0 ); +/* Save the old environment */ r1_old = r1_new =readl (bch_regs +HW_BCH_FLASH0LAYOUT0 ); r2_old = r2_new =readl (bch_regs +HW_BCH_FLASH0LAYOUT1 );
[...]
Quoted 20 lines
Quoted 20 lines
(quoted diff) +0
-0
b/drivers/mtd/nand/vf610_nfc.cindex8037d4b48a05..80d31a58e558100644 ---a/drivers/mtd/nand/vf610_nfc.c
+++b/drivers/mtd/nand/vf610_nfc.cdrivers/mtd/nand/gpmi-nand/gpmi-nand.c +1
-1@@ -560,7 +560,7 @@ static int vf610_nfc_read_page(struct mtd_info *mtd, struct nand_chip *chip, int eccsize = chip->ecc.size;int stat; -vf610_nfc_read_buf (mtd, buf, eccsize); +nand_read_page_op (chip, page,0 , buf, eccsize);if (oob_required) vf610_nfc_read_buf(mtd, chip->oob_poi,
mtd->oobsize);drivers/mtd/nand/gpmi-nand/gpmi-nand.c +0
-0@@ -580,7 +580,7 @@ static int vf610_nfc_write_page(struct mtd_info *mtd, struct nand_chip *chip, {struct vf610_nfc *nfc =mtd_to_nfc (mtd);
Quoted 5 lines
drivers/mtd/nand/gpmi-nand/gpmi-nand.c static int vf610_nfc_write_page(struct mtd_info · line 584
Quoted 1 line
drivers/mtd/nand/gpmi-nand/gpmi-nand.c +0
-1-vf610_nfc_write_buf (mtd, buf, mtd->writesize);We currently ignore
NAND_CMD_SEQINin ->cmdfunc anyway, so I think this change would not even be necessary here.
Boris already answered this one, I will let it as is to remain consistent but thanks for the remark.
Quoted 4 lines
This is a NAND controller which will benefit from the new interface. I plan to convert the driver to the new interface, maybe I manage to do that soon so we have a second driver making use of the new interface.
That would be great! I am writing the kernel-doc aside, I hope it will be ready soon, but in any case do not hesitate if you need help.
Thank you,
Miquèl
RFC 3/12 mtd: nand: use a static data_interface in the nand_chip structure
In reply to Miquel RaynalChange the data_interface field from the nand_chip structure, convert the pointer to a static structure.
Also remove the nand_get_default_data_interface() function that become useless and rename the onfi_init_data_interface() by nand_fill_data_interface(), which is a more appropriate name because applied timings are ONFI, no matter if the NAND actually is one.
This is needed before passing to ->exec_op() to avoid any race that could lead to a panic (null pointer dereference) on the initialization of the timings structure that will be used from the first reset operation if the pointer to data_interface was not referenced yet.
--- drivers/mtd/nand/nand_base.c | 35 ++++++++++------------------------- drivers/mtd/nand/nand_timings.c | 23 ++++++----------------- include/linux/mtd/rawnand.h | 9 ++------- 3 files changed, 18 insertions(+), 49 deletions(-)
drivers/mtd/nand/nand_base.c +10 -25
@@ -814,8 +814,8 @@ static void nand_ccs_delay(struct nand_chip *chip)* Wait tCCS_min if it is correctly defined, otherwise wait 500ns * (which should be safe for all NANDs). */ -if (chip->data_interface && chip->data_interface->timings.sdr.tCCS_min) -ndelay (chip->data_interface->timings.sdr.tCCS_min /1000 ); +if (chip->data_interface.timings.sdr.tCCS_min) +ndelay (chip->data_interface.timings.sdr.tCCS_min /1000 );else ndelay (500 ); }
@@ -1107,7 +1107,6 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)static int nand_reset_data_interface (struct nand_chip *chip,int chipnr) {struct mtd_info *mtd =nand_to_mtd (chip); -const struct nand_data_interface *conf;int ret;if (!chip->setup_data_interface)
@@ -1127,8 +1126,8 @@ static int nand_reset_data_interface(struct nand_chip *chip, int chipnr)* timings to timing mode 0. */ - conf =nand_get_default_data_interface (); - ret = chip->setup_data_interface (mtd, chipnr, conf); +nand_fill_data_interface (chip,0 ); + ret = chip->setup_data_interface (mtd, chipnr, &chip->data_interface);if (ret)pr_err ("Failed to configure data interface to SDR timing mode 0\n" );
@@ -1153,7 +1152,7 @@ static int nand_setup_data_interface(struct nand_chip *chip, int chipnr)struct mtd_info *mtd =nand_to_mtd (chip);int ret; -if (!chip->setup_data_interface || !chip->data_interface) +if (!chip->setup_data_interface)return 0 ;/*
@@ -1174,7 +1173,7 @@ static int nand_setup_data_interface(struct nand_chip *chip, int chipnr) goto err; } - ret = chip->setup_data_interface (mtd, chipnr, chip->data_interface); + ret = chip->setup_data_interface (mtd, chipnr, &chip->data_interface); err:return ret; }
@@ -1214,21 +1213,16 @@ static int nand_init_data_interface(struct nand_chip *chip) modes =GENMASK (chip->onfi_timing_mode_default,0 ); } - chip->data_interface =kzalloc (sizeof (*chip->data_interface), -GFP_KERNEL ); -if (!chip->data_interface) -return -ENOMEM ;for (mode =fls (modes) -1 ; mode >=0 ; mode--) { - ret =onfi_init_data_interface (chip, chip->data_interface, -NAND_SDR_IFACE , mode); + ret =nand_fill_data_interface (chip, mode);if (ret)continue ;/* Pass -1 to only */ ret = chip->setup_data_interface (mtd,NAND_DATA_IFACE_CHECK_ONLY , - chip->data_interface); + &chip->data_interface);if (!ret) { chip->onfi_timing_mode_default = mode;break ;
@@ -1238,11 +1232,6 @@ static int nand_init_data_interface(struct nand_chip *chip)return 0 ; } -static void nand_release_data_interface (struct nand_chip *chip) -{ -kfree (chip->data_interface); -} -/** * nand_read_page_op - Do a READ PAGE operation * @chip: The NAND chip
@@ -5567,7 +5556,7 @@ int nand_scan_tail(struct mtd_info *mtd) chip->select_chip (mtd,-1 );if (ret) - goto err_nand_data_iface_cleanup; + goto err_nand_manuf_cleanup; }/* Check, if we should skip the bad block table scan */
@@ -5577,12 +5566,10 @@ int nand_scan_tail(struct mtd_info *mtd)/* Build bad block table */ ret = chip->scan_bbt (mtd);if (ret) - goto err_nand_data_iface_cleanup; + goto err_nand_manuf_cleanup;return 0 ; -err_nand_data_iface_cleanup: -nand_release_data_interface (chip); err_nand_manuf_cleanup:nand_manufacturer_cleanup (chip);
@@ -5641,8 +5628,6 @@ void nand_cleanup(struct nand_chip *chip) chip->ecc.algo == NAND_ECC_BCH)nand_bch_free ((struct nand_bch_control *)chip->ecc.priv); -nand_release_data_interface (chip); -/* Free bad block table memory */ kfree (chip->bbt);if (!(chip->options &NAND_OWN_BUFFERS ) && chip->buffers) {
drivers/mtd/nand/nand_timings.c +6 -17
@@ -283,17 +283,17 @@ const struct nand_sdr_timings *onfi_async_timing_mode_to_sdr_timings(int mode)EXPORT_SYMBOL (onfi_async_timing_mode_to_sdr_timings);/** -* onfi_init_data_interface - [NAND Interface] Initialize a data interface from +* nand_fill_data_interface - [NAND Interface] Initialize a data interface from * given ONFI mode * @iface: The data interface to be initialized * @mode: The ONFI timing mode */ -int onfi_init_data_interface (struct nand_chip *chip, -struct nand_data_interface *iface, -enum nand_data_interface_type type, +int nand_fill_data_interface (struct nand_chip *chip,int timing_mode) { -if (type !=NAND_SDR_IFACE ) +struct nand_data_interface *iface = &chip->data_interface; + +if (iface->type !=NAND_SDR_IFACE )return -EINVAL ;if (timing_mode <0 || timing_mode >=ARRAY_SIZE (onfi_sdr_timings))
@@ -321,15 +321,4 @@ int onfi_init_data_interface(struct nand_chip *chip,return 0 ; } -EXPORT_SYMBOL (onfi_init_data_interface); - -/** -* nand_get_default_data_interface - [NAND Interface] Retrieve NAND -* data interface for mode 0. This is used as default timing after -* reset. -*/ -const struct nand_data_interface *nand_get_default_data_interface (void ) -{ -return &onfi_sdr_timings[0 ]; -} -EXPORT_SYMBOL (nand_get_default_data_interface); +EXPORT_SYMBOL (nand_fill_data_interface);
include/linux/mtd/rawnand.h +2 -7
@@ -917,7 +917,7 @@ struct nand_chip {u16 max_bb_per_die;u32 blocks_per_die; -struct nand_data_interface *data_interface; +struct nand_data_interface data_interface;int read_retries;
@@ -1214,10 +1214,7 @@ static inline int onfi_get_sync_timing_mode(struct nand_chip *chip)return le16_to_cpu (chip->onfi_params.src_sync_timing_mode); } -int onfi_init_data_interface (struct nand_chip *chip, -struct nand_data_interface *iface, -enum nand_data_interface_type type, -int timing_mode); +int nand_fill_data_interface (struct nand_chip *chip,int timing_mode);/* * Check if it is a SLC nand.
@@ -1258,8 +1255,6 @@ static inline int jedec_feature(struct nand_chip *chip)/* get timing characteristics from ONFI timing mode. */ const struct nand_sdr_timings *onfi_async_timing_mode_to_sdr_timings (int mode); -/* get data interface from ONFI timing mode 0, used after reset. */ -const struct nand_data_interface *nand_get_default_data_interface (void ); int nand_check_erased_ecc_chunk(void *data, int datalen, void *ecc, int ecclen,
Signature · 1 line
2.11.0
RFC 3/12 mtd: nand: use a static data_interface in the nand_chip structure
Quoted 2 lines · On Wed, 18 Oct 2017 16:36:20 +0200 Miquel Raynal…
Change the
data_interfacefield from thenand_chipstructure, convert the pointer to a static structure.
You mean "embed a nand_data_interface object". Not sure the static specifier can be used to describe what you do.
Quoted 4 lines
Also remove the
nand_get_default_data_interface()function that become useless and rename theonfi_init_data_interface()bynand_fill_data_interface(),
I know I'm the one who suggested to rename this function, but I'm not so sure it's a good idea in the end, and I don't like the new prototype where you drop the iface_type parameter.
Quoted 2 lines
which is a more appropriate name because
applied timings are ONFI, no matter if the NAND actually is one.
Well, this is actually a good reason to keep the onfi_ prefix. A NAND driver can decide that its NAND is close to an existing timing mode and re-use this onfi_init_data_interface(), but maybe someday we'll have drivers filling the nand_data_interface object with their own (non-ONFI) timings.
Quoted 2 lines
This is needed before passing to ->exec_op() to avoid any race that
It's not really a race, it's just that you need some timings during NAND detection, and the core is currently creating the nand_data_interface object after the detection step (in nand_scan_tail()).
Quoted 35 lines
could lead to a panic (null pointer dereference) on the initialization of the timings structure that will be used from the first reset operation if the pointer to
data_interfacewas not referenced yet.Signed-off-by Miquel Raynal <[email protected]>--- drivers/mtd/nand/nand_base.c | 35 ++++++++++------------------------- drivers/mtd/nand/nand_timings.c | 23 ++++++----------------- include/linux/mtd/rawnand.h | 9 ++------- 3 files changed, 18 insertions(+), 49 deletions(-)drivers/mtd/nand/nand_base.c +2
-3@@ -814,8 +814,8 @@ static void nand_ccs_delay(struct nand_chip *chip)* Wait tCCS_min if it is correctly defined, otherwise wait 500ns * (which should be safe for all NANDs). */ -if (chip->data_interface && chip->data_interface->timings.sdr.tCCS_min) -ndelay (chip->data_interface->timings.sdr.tCCS_min /1000 ); +if (chip->data_interface.timings.sdr.tCCS_min) +ndelay (chip->data_interface.timings.sdr.tCCS_min /1000 );else ndelay (500 ); }@@ -1107,7 +1107,6 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)static int nand_reset_data_interface (struct nand_chip *chip,int chipnr) {struct mtd_info *mtd =nand_to_mtd (chip); -const struct nand_data_interface *conf;int ret;if (!chip->setup_data_interface)
drivers/mtd/nand/nand_base.c static int nand_reset_data_interface(struct nand_chip *chip, int chipnr) · line 1133
Quoted 10 lines
drivers/mtd/nand/nand_base.c +2
-2@@ -1127,8 +1126,8 @@ static int nand_reset_data_interface(struct nand_chip *chip, int chipnr)* timings to timing mode 0. */ - conf =nand_get_default_data_interface (); - ret = chip->setup_data_interface (mtd, chipnr, conf); +nand_fill_data_interface (chip,0 ); + ret = chip->setup_data_interface (mtd, chipnr, &chip->data_interface);if (ret)pr_err ("Failed to configure data interface to SDR timing mode 0\n" );
So now you're stuck in timing mode 0 as soon as you have nand_reset() called.
Actually, what we should do is:
1/ at the beginning of nand_scan_ident(), call
onfi_init_data_interface(chip, &chip->data_interface, NAND_SDR_IFACE, 0) (or a wrapper that does that) so that data_interface is initialized with the appropriate timings for reset/detection operations
2/ after detection, find the best iface type and timing mode we can use
(what's currently done in nand_init_data_interface())
3/ in nand_reset(), you should save what's in chip->data_interface, in a
local variable, then apply SDR+timing-mode-0 and finally restore the previous settings (the one you stored in your local variable) after the RESET op.
This way you're guaranteed to always end up with the best data iface settings after a RESET.
Quoted 164 lines
drivers/mtd/nand/nand_base.c +6
-20@@ -1153,7 +1152,7 @@ static int nand_setup_data_interface(struct nand_chip *chip, int chipnr)struct mtd_info *mtd =nand_to_mtd (chip);int ret; -if (!chip->setup_data_interface || !chip->data_interface) +if (!chip->setup_data_interface)return 0 ;/* @@ -1174,7 +1173,7 @@ static int nand_setup_data_interface(struct nand_chip *chip, int chipnr) goto err; } - ret = chip->setup_data_interface (mtd, chipnr, chip->data_interface); + ret = chip->setup_data_interface (mtd, chipnr, &chip->data_interface); err:return ret; }@@ -1214,21 +1213,16 @@ static int nand_init_data_interface(struct nand_chip *chip) modes =GENMASK (chip->onfi_timing_mode_default,0 ); } - chip->data_interface =kzalloc (sizeof (*chip->data_interface), -GFP_KERNEL ); -if (!chip->data_interface) -return -ENOMEM ;for (mode =fls (modes) -1 ; mode >=0 ; mode--) { - ret =onfi_init_data_interface (chip, chip->data_interface, -NAND_SDR_IFACE , mode); + ret =nand_fill_data_interface (chip, mode);if (ret)continue ;/* Pass -1 to only */ ret = chip->setup_data_interface (mtd,NAND_DATA_IFACE_CHECK_ONLY , - chip->data_interface); + &chip->data_interface);if (!ret) { chip->onfi_timing_mode_default = mode;break ;@@ -1238,11 +1232,6 @@ static int nand_init_data_interface(struct nand_chip *chip)return 0 ; } -static void nand_release_data_interface (struct nand_chip *chip) -{ -kfree (chip->data_interface); -} -/** * nand_read_page_op - Do a READ PAGE operation * @chip: The NAND chip @@ -5567,7 +5556,7 @@ int nand_scan_tail(struct mtd_info *mtd) chip->select_chip (mtd,-1 );if (ret) - goto err_nand_data_iface_cleanup; + goto err_nand_manuf_cleanup; }/* Check, if we should skip the bad block table scan */ @@ -5577,12 +5566,10 @@ int nand_scan_tail(struct mtd_info *mtd)/* Build bad block table */ ret = chip->scan_bbt (mtd);if (ret) - goto err_nand_data_iface_cleanup; + goto err_nand_manuf_cleanup;return 0 ; -err_nand_data_iface_cleanup: -nand_release_data_interface (chip); err_nand_manuf_cleanup:nand_manufacturer_cleanup (chip);@@ -5641,8 +5628,6 @@ void nand_cleanup(struct nand_chip *chip) chip->ecc.algo == NAND_ECC_BCH)nand_bch_free ((struct nand_bch_control *)chip->ecc.priv); -nand_release_data_interface (chip); -/* Free bad block table memory */ kfree (chip->bbt);if (!(chip->options &NAND_OWN_BUFFERS ) && chip->buffers) {drivers/mtd/nand/nand_timings.c +6
-17@@ -283,17 +283,17 @@ const struct nand_sdr_timings *onfi_async_timing_mode_to_sdr_timings(int mode)EXPORT_SYMBOL (onfi_async_timing_mode_to_sdr_timings);/** -* onfi_init_data_interface - [NAND Interface] Initialize a data interface from +* nand_fill_data_interface - [NAND Interface] Initialize a data interface from * given ONFI mode * @iface: The data interface to be initialized * @mode: The ONFI timing mode */ -int onfi_init_data_interface (struct nand_chip *chip, -struct nand_data_interface *iface, -enum nand_data_interface_type type, +int nand_fill_data_interface (struct nand_chip *chip,int timing_mode) { -if (type !=NAND_SDR_IFACE ) +struct nand_data_interface *iface = &chip->data_interface; + +if (iface->type !=NAND_SDR_IFACE )return -EINVAL ;if (timing_mode <0 || timing_mode >=ARRAY_SIZE (onfi_sdr_timings))@@ -321,15 +321,4 @@ int onfi_init_data_interface(struct nand_chip *chip,return 0 ; } -EXPORT_SYMBOL (onfi_init_data_interface); - -/** -* nand_get_default_data_interface - [NAND Interface] Retrieve NAND -* data interface for mode 0. This is used as default timing after -* reset. -*/ -const struct nand_data_interface *nand_get_default_data_interface (void ) -{ -return &onfi_sdr_timings[0 ]; -} -EXPORT_SYMBOL (nand_get_default_data_interface); +EXPORT_SYMBOL (nand_fill_data_interface);include/linux/mtd/rawnand.h +2
-7@@ -917,7 +917,7 @@ struct nand_chip {u16 max_bb_per_die;u32 blocks_per_die; -struct nand_data_interface *data_interface; +struct nand_data_interface data_interface;int read_retries;@@ -1214,10 +1214,7 @@ static inline int onfi_get_sync_timing_mode(struct nand_chip *chip)return le16_to_cpu (chip->onfi_params.src_sync_timing_mode); } -int onfi_init_data_interface (struct nand_chip *chip, -struct nand_data_interface *iface, -enum nand_data_interface_type type, -int timing_mode); +int nand_fill_data_interface (struct nand_chip *chip,int timing_mode);/* * Check if it is a SLC nand. @@ -1258,8 +1255,6 @@ static inline int jedec_feature(struct nand_chip *chip)/* get timing characteristics from ONFI timing mode. */ const struct nand_sdr_timings *onfi_async_timing_mode_to_sdr_timings (int mode); -/* get data interface from ONFI timing mode 0, used after reset. */ -const struct nand_data_interface *nand_get_default_data_interface (void ); int nand_check_erased_ecc_chunk(void *data, int datalen, void *ecc, int ecclen,
Re: [RFC 03/12] mtd: nand: use a static data_interface in the nand_chip structure
Hi Boris,
Quoted 5 lines
Quoted 2 lines
Change the
data_interfacefield from thenand_chipstructure, convert the pointer to a static structure.You mean "embed a
nand_data_interfaceobject". Not sure the static specifier can be used to describe what you do.
Ok.
Quoted 7 lines
Quoted 3 lines
Also remove the
nand_get_default_data_interface()function that become useless and rename theonfi_init_data_interface()bynand_fill_data_interface(),I know I'm the one who suggested to rename this function, but I'm not so sure it's a good idea in the end, and I don't like the new prototype where you drop the
iface_typeparameter.
No problem, I will just rename it "onfi_fill_data_interface" because there is no more real "initialization", it is just a dumb function that fills the structure with the appropriate timings (I found the name too close to "nand_init_data_interface" that really does something). But ok, let's keep the iface_type parameter.
Quoted 11 lines
Quoted 3 lines
which is a more appropriate name because
applied timings are ONFI, no matter if the NAND actually is one.Well, this is actually a good reason to keep the
onfi_prefix. A NAND driver can decide that its NAND is close to an existing timing mode and re-use thisonfi_init_data_interface(), but maybe someday we'll have drivers filling thenand_data_interfaceobject with their own (non-ONFI) timings.Quoted 1 line
Quoted 7 lines
Quoted 2 lines
This is needed before passing to ->exec_op() to avoid any race that
It's not really a race, it's just that you need some timings during NAND detection, and the core is currently creating the
nand_data_interfaceobject after the detection step (innand_scan_tail()).
I changed the commit message.
Quoted 37 lines
Quoted 19 lines
(quoted diff) +0
-0@@ -1107,7 +1107,6 @@ static int nand_wait(struct mtd_info *mtd,struct
nand_chip*chip) static intnand_reset_data_interface(struct nand_chip *chip, int chipnr){drivers/mtd/nand/nand_base.c +2
-3struct mtd_info *mtd =nand_to_mtd (chip); -const struct nand_data_interface *conf;int ret;if (!chip->setup_data_interface)@@ -1127,8 +1126,8 @@ static int nand_reset_data_interface(structnand_chip *chip, int chipnr) * timings to timing mode 0. */ - conf =nand_get_default_data_interface (); - ret = chip->setup_data_interface (mtd, chipnr, conf); +nand_fill_data_interface (chip,0 ); + ret = chip->setup_data_interface (mtd, chipnr, &chip->data_interface);if (ret) pr_err("Failed to configure data interface to SDRQuoted 1 line
timing mode 0\n");
So now you're stuck in timing mode 0 as soon as you have
nand_reset()called.Actually, what we should do is:
1/ at the beginning of
nand_scan_ident(), call
onfi_init_data_interface(chip,&chip->data_interface,NAND_SDR_IFACE, 0) (or a wrapper that does that) so thatdata_interfaceis initialized with the appropriate timings for reset/detection operations 2/ after detection, find the best iface type and timing mode we can use (what's currently done innand_init_data_interface()) 3/ innand_reset(), you should save what's inchip->data_interface, in a local variable, then apply SDR+timing-mode-0 and finally restore the previous settings (the one you stored in your local variable) after the RESET op.
Quoted 2 lines
This way you're guaranteed to always end up with the best data iface settings after a RESET.
Clear explanation, this is corrected.
Thank you,
Miquèl
RFC 4/12 mtd: nand: add ->exec_op() implementation
In reply to Miquel RaynalIntroduce the new way to control the NAND controller drivers by implementing the ->exec_op() core helpers and allowing new drivers to use it instead of relying on ->cmd_ctrl(), ->cmdfunc() and ->read/write_byte/word/buf().
The logic is now to send to the controller driver a list of instructions. The driver shall use the parser added by this commit to get the matching hook if any. The instructions may be split by the parser in order to comply with the controller constraints filled in an array of supported patterns.
Various helpers are also added to ease NAND controller drivers writing.
This new interface should really ease the support of new vendor specific instructions.
--- drivers/mtd/nand/denali.c | 93 +++- drivers/mtd/nand/nand_base.c | 949 +++++++++++++++++++++++++++++++++++++++-- drivers/mtd/nand/nand_hynix.c | 91 +++- drivers/mtd/nand/nand_micron.c | 32 +- include/linux/mtd/rawnand.h | 366 +++++++++++++++- 5 files changed, 1448 insertions(+), 83 deletions(-)
drivers/mtd/nand/denali.c +71 -22
@@ -652,8 +652,6 @@ static void denali_oob_xfer(struct mtd_info *mtd, struct nand_chip *chip, int page, int write) {struct denali_nand_info *denali =mtd_to_denali (mtd); -unsigned int start_cmd = write ?NAND_CMD_SEQIN :NAND_CMD_READ0 ; -unsigned int rnd_cmd = write ?NAND_CMD_RNDIN :NAND_CMD_RNDOUT ;int writesize = mtd->writesize;int oobsize = mtd->oobsize;uint8_t *bufpoi = chip->oob_poi;
@@ -665,11 +663,22 @@ static void denali_oob_xfer(struct mtd_info *mtd, struct nand_chip *chip,int i, pos, len;/* BBM at the beginning of the OOB area */ - chip->cmdfunc (mtd, start_cmd, writesize, page); -if (write) - chip->write_buf (mtd, bufpoi, oob_skip); -else - chip->read_buf (mtd, bufpoi, oob_skip); +if (chip->exec_op) { +if (write) +nand_prog_page_begin_op (chip, page, writesize, bufpoi, + oob_skip); +else +nand_read_page_op (chip, page, writesize, bufpoi, + oob_skip); + }else { +if (write) { + chip->cmdfunc (mtd,NAND_CMD_SEQIN , writesize, page); + chip->write_buf (mtd, bufpoi, oob_skip); + }else { + chip->cmdfunc (mtd,NAND_CMD_READ0 , writesize, page); + chip->read_buf (mtd, bufpoi, oob_skip); + } + } bufpoi += oob_skip;/* OOB ECC */
@@ -682,30 +691,67 @@ static void denali_oob_xfer(struct mtd_info *mtd, struct nand_chip *chip,else if (pos + len > writesize) len = writesize - pos; - chip->cmdfunc (mtd, rnd_cmd, pos,-1 ); -if (write) - chip->write_buf (mtd, bufpoi, len); -else - chip->read_buf (mtd, bufpoi, len); - bufpoi += len; -if (len < ecc_bytes) { - len = ecc_bytes - len; - chip->cmdfunc (mtd, rnd_cmd, writesize + oob_skip,-1 ); +if (chip->exec_op) {if (write) - chip->write_buf (mtd, bufpoi, len); +nand_change_write_column_op ( + chip, pos, bufpoi, len, false);else +nand_change_read_column_op ( + chip, pos, bufpoi, len, false); + }else { +if (write) { + chip->cmdfunc (mtd,NAND_CMD_RNDIN , pos,-1 ); + chip->write_buf (mtd, bufpoi, len); + }else { + chip->cmdfunc (mtd,NAND_CMD_RNDOUT , pos,-1 ); chip->read_buf (mtd, bufpoi, len); + } + } + bufpoi += len; +if (len < ecc_bytes) { + len = ecc_bytes - len; +if (chip->exec_op) { +if (write) +nand_change_write_column_op ( + chip, writesize + oob_skip, + bufpoi, len, false); +else +nand_change_read_column_op ( + chip, writesize + oob_skip, + bufpoi, len, false); + }else { +if (write) { + chip->cmdfunc (mtd,NAND_CMD_RNDIN , + writesize + oob_skip,-1 ); + chip->write_buf (mtd, bufpoi, len); + }else { + chip->cmdfunc (mtd,NAND_CMD_RNDOUT , + writesize + oob_skip,-1 ); + chip->read_buf (mtd, bufpoi, len); + } + } bufpoi += len; } }/* OOB free */ len = oobsize - (bufpoi - chip->oob_poi); - chip->cmdfunc (mtd, rnd_cmd, size - len,-1 ); -if (write) - chip->write_buf (mtd, bufpoi, len); -else - chip->read_buf (mtd, bufpoi, len); +if (chip->exec_op) { +if (write) +nand_change_write_column_op (chip, size - len, bufpoi, + len, false); +else +nand_change_read_column_op (chip, size - len, bufpoi, + len, false); + }else { +if (write) { + chip->cmdfunc (mtd,NAND_CMD_RNDIN , size - len,-1 ); + chip->write_buf (mtd, bufpoi, len); + }else { + chip->cmdfunc (mtd,NAND_CMD_RNDOUT , size - len,-1 ); + chip->read_buf (mtd, bufpoi, len); + } + } } static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
@@ -803,6 +849,9 @@ static int denali_write_oob(struct mtd_info *mtd, struct nand_chip *chip,denali_oob_xfer (mtd, chip, page,1 ); +if (chip->exec_op) +return nand_prog_page_end_op (chip); + chip->cmdfunc (mtd,NAND_CMD_PAGEPROG ,-1 ,-1 ); status = chip->waitfunc (mtd, chip);
drivers/mtd/nand/nand_base.c +922 -27
1,132 lines not on this page · raw message
drivers/mtd/nand/nand_hynix.c +71 -20
138 lines not on this page · raw message
drivers/mtd/nand/nand_micron.c +28 -4
@@ -117,14 +117,38 @@ micron_nand_read_page_on_die_ecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { - int status; + u8 status;int max_bitflips =0 ;micron_nand_on_die_ecc_setup (chip, true); - chip->cmdfunc (mtd,NAND_CMD_READ0 ,0x00 , page); - chip->cmdfunc (mtd,NAND_CMD_STATUS ,-1 ,-1 ); - status = chip->read_byte (mtd); +if (chip->exec_op) { +u8 addrs[5 ] = {}; +struct nand_op_instr instrs[] = { +NAND_OP_CMD (NAND_CMD_READ0 ,0 ), +NAND_OP_ADDR (4 , addrs,0 ), +NAND_OP_CMD (NAND_CMD_STATUS ,0 ), +NAND_OP_8BIT_DATA_IN (1 , &status,0 ), + }; +struct nand_operation op =NAND_OPERATION (instrs); + +if (nand_fill_column_cycles (chip, addrs,0 )) +return -EINVAL ; + + addrs[2 ] = page; + addrs[3 ] = page >>8 ; +if (chip->options &NAND_ROW_ADDR_3 ) { + addrs[4 ] = page >>16 ; + instrs[1 ].addr.naddrs++; + } + +nand_exec_op (chip, &op); + }else { + chip->cmdfunc (mtd,NAND_CMD_READ0 ,0x00 , page); + chip->cmdfunc (mtd,NAND_CMD_STATUS ,-1 ,-1 ); + status = chip->read_byte (mtd); + } +if (status &NAND_STATUS_FAIL ) mtd->ecc_stats.failed++;/*
include/linux/mtd/rawnand.h +356 -10
397 lines not on this page · raw message
Signature · 1 line
2.11.0
RFC 4/12 mtd: nand: add ->exec_op() implementation
Quoted 4 lines · On Wed, 18 Oct 2017 16:36:21 +0200 Miquel Raynal…
Introduce the new way to control the NAND controller drivers by implementing the ->exec_op() core helpers and allowing new drivers to use it instead of relying on ->cmd_ctrl(), ->cmdfunc() and ->read/write_byte/word/buf().
"
Introduce a new interface to instruct NAND controllers to send specific NAND operations. The new interface takes the form of a single method called ->exec_op(). This method is designed to replace ->cmd_ctrl(), ->cmdfunc() and ->read/write_byte/word/buf() hooks.
"
Quoted 3 lines
The logic is now to send to the controller driver a list of instructions.
"
->exec_op() is passed a set of instructions describing the operation to execute. Each instruction has a type (ADDR, CMD, CYCLE, WAITRDY) and delay. The type is directly matching the description of NAND commands in various NAND datasheet and standards (ONFI, JEDEC), the delay is here to help simple controllers wait enough time between each instruction. Advanced controllers with integrated timings control can ignore these delays.
"
Quoted 2 lines
The driver shall use the parser added by this commit to get the matching hook if any.
That's true only for advanced controllers. For those who are executing each instruction individually (like the gpio-nand driver, or any old style driver where each ADDR/CMD/DATA cycle is controlled independently) this is not needed, and who add extra complexity for no real gain.
Quoted 3 lines
The instructions may be split by the
parser in order to comply with the controller constraints filled in an array of supported patterns.
Given only this description it's hard to tell what this parser is and why it's needed. I think a real example who be helpful to better understand what this is.
Quoted 5 lines
Various helpers are also added to ease NAND controller drivers writing.
This new interface should really ease the support of new vendor specific instructions.
Actually, it's more than easing support for vendor specific operations, it allows the core to check whether the NAND controller will be able to execute a specific operation or not, which before that was impossible. It doesn't mean all controllers will magically support all kind of NAND operations, but at least we'll know when it doesn't.
Quoted 39 lines
Suggested-by Boris Brezillon <[email protected]>Signed-off-by Miquel Raynal <[email protected]>--- drivers/mtd/nand/denali.c | 93 +++- drivers/mtd/nand/nand_base.c | 949 +++++++++++++++++++++++++++++++++++++++-- drivers/mtd/nand/nand_hynix.c | 91 +++- drivers/mtd/nand/nand_micron.c | 32 +- include/linux/mtd/rawnand.h | 366 +++++++++++++++- 5 files changed, 1448 insertions(+), 83 deletions(-)drivers/mtd/nand/denali.c +6
-7@@ -652,8 +652,6 @@ static void denali_oob_xfer(struct mtd_info *mtd, struct nand_chip *chip, int page, int write) {struct denali_nand_info *denali =mtd_to_denali (mtd); -unsigned int start_cmd = write ?NAND_CMD_SEQIN :NAND_CMD_READ0 ; -unsigned int rnd_cmd = write ?NAND_CMD_RNDIN :NAND_CMD_RNDOUT ;int writesize = mtd->writesize;int oobsize = mtd->oobsize;uint8_t *bufpoi = chip->oob_poi;@@ -665,11 +663,22 @@ static void denali_oob_xfer(struct mtd_info *mtd, struct nand_chip *chip,int i, pos, len;/* BBM at the beginning of the OOB area */ - chip->cmdfunc (mtd, start_cmd, writesize, page); -if (write) - chip->write_buf (mtd, bufpoi, oob_skip); -else - chip->read_buf (mtd, bufpoi, oob_skip); +if (chip->exec_op) { +if (write) +nand_prog_page_begin_op (chip, page, writesize, bufpoi, + oob_skip); +else + nand_read_page_op(chip, page, writesize, bufpoi,
drivers/mtd/nand/denali.c static void denali_oob_xfer(struct mtd_info *mtd, struct nand_chip *chip, · line 681
Quoted 10 lines
drivers/mtd/nand/denali.c +10
-0+ oob_skip); + }else { +if (write) { + chip->cmdfunc (mtd,NAND_CMD_SEQIN , writesize, page); + chip->write_buf (mtd, bufpoi, oob_skip); + }else { + chip->cmdfunc (mtd,NAND_CMD_READ0 , writesize, page); + chip->read_buf (mtd, bufpoi, oob_skip); + } + }
Why do we have to keep the ->cmdfunc() logic? nand_prog_page_xxx() already abstracts this for us, right?
Quoted 11 lines
drivers/mtd/nand/denali.c +0
-4bufpoi += oob_skip;/* OOB ECC */ @@ -682,30 +691,67 @@ static void denali_oob_xfer(struct mtd_info *mtd, struct nand_chip *chip,else if (pos + len > writesize) len = writesize - pos; - chip->cmdfunc (mtd, rnd_cmd, pos,-1 ); -if (write) - chip->write_buf (mtd, bufpoi, len); -else
drivers/mtd/nand/denali.c static void denali_oob_xfer(struct mtd_info *mtd, struct nand_chip *chip, · line 697
Quoted 10 lines
drivers/mtd/nand/denali.c +3
-6- chip->read_buf (mtd, bufpoi, len); - bufpoi += len; -if (len < ecc_bytes) { - len = ecc_bytes - len; - chip->cmdfunc (mtd, rnd_cmd, writesize + oob_skip,-1 ); +if (chip->exec_op) {if (write) - chip->write_buf (mtd, bufpoi, len); +nand_change_write_column_op ( + chip, pos, bufpoi, len, false);
Nitpick, but can you try to align things like that:
nand_change_write_column_op(chip, pos, bufpoi, len, false);
drivers/mtd/nand/denali.c static void denali_oob_xfer(struct mtd_info *mtd, struct nand_chip *chip, · line 700
Quoted 3 lines
drivers/mtd/nand/denali.c +2
-0else +nand_change_read_column_op ( + chip, pos, bufpoi, len, false);
Ditto.
drivers/mtd/nand/denali.c static void denali_oob_xfer(struct mtd_info *mtd, struct nand_chip *chip, · line 709
Quoted 9 lines
drivers/mtd/nand/denali.c +8
-0+ }else { +if (write) { + chip->cmdfunc (mtd,NAND_CMD_RNDIN , pos,-1 ); + chip->write_buf (mtd, bufpoi, len); + }else { + chip->cmdfunc (mtd,NAND_CMD_RNDOUT , pos,-1 ); chip->read_buf (mtd, bufpoi, len); + } + }
Smae here: I don't think we need to support both ->cmdfunc() and nand_change_read/write_column().
Quoted 13 lines
drivers/mtd/nand/denali.c +13
-0+ bufpoi += len; +if (len < ecc_bytes) { + len = ecc_bytes - len; +if (chip->exec_op) { +if (write) +nand_change_write_column_op ( + chip, writesize + oob_skip, + bufpoi, len, false); +else +nand_change_read_column_op ( + chip, writesize + oob_skip, + bufpoi, len, false); + }else {
drivers/mtd/nand/denali.c static void denali_oob_xfer(struct mtd_info *mtd, struct nand_chip *chip,
Quoted 10 lines
drivers/mtd/nand/denali.c +10
-0+if (write) { + chip->cmdfunc (mtd,NAND_CMD_RNDIN , + writesize + oob_skip,-1 ); + chip->write_buf (mtd, bufpoi, len); + }else { + chip->cmdfunc (mtd,NAND_CMD_RNDOUT , + writesize + oob_skip,-1 ); + chip->read_buf (mtd, bufpoi, len); + } + .
Ditto.
Quoted 31 lines
drivers/mtd/nand/denali.c +16
-5bufpoi += len; } }/* OOB free */ len = oobsize - (bufpoi - chip->oob_poi); - chip->cmdfunc (mtd, rnd_cmd, size - len,-1 ); -if (write) - chip->write_buf (mtd, bufpoi, len); -else - chip->read_buf (mtd, bufpoi, len); +if (chip->exec_op) { +if (write) +nand_change_write_column_op (chip, size - len, bufpoi, + len, false); +else +nand_change_read_column_op (chip, size - len, bufpoi, + len, false); + }else { +if (write) { + chip->cmdfunc (mtd,NAND_CMD_RNDIN , size - len,-1 ); + chip->write_buf (mtd, bufpoi, len); + }else { + chip->cmdfunc (mtd,NAND_CMD_RNDOUT , size - len,-1 ); + chip->read_buf (mtd, bufpoi, len); + } + } } static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
drivers/mtd/nand/denali.c static int denali_write_oob(struct mtd_info *mtd, struct nand_chip *chip, · line 856
Quoted 8 lines
drivers/mtd/nand/denali.c +3
-0@@ -803,6 +849,9 @@ static int denali_write_oob(struct mtd_info *mtd, struct nand_chip *chip,denali_oob_xfer (mtd, chip, page,1 ); +if (chip->exec_op) +return nand_prog_page_end_op (chip); + chip->cmdfunc (mtd,NAND_CMD_PAGEPROG ,-1 ,-1 ); status = chip->waitfunc (mtd, chip);
All denali changes in this patch should actually be moved to patch 1.
drivers/mtd/nand/nand_base.c static void nand_ccs_delay(struct nand_chip *chip) · line 817
Quoted 11 lines
drivers/mtd/nand/nand_base.c +1
-1@@ -814,7 +814,7 @@ static void nand_ccs_delay(struct nand_chip *chip)* Wait tCCS_min if it is correctly defined, otherwise wait 500ns * (which should be safe for all NANDs). */ -if (chip->data_interface.timings.sdr.tCCS_min) +if (&chip->data_interface.timings.sdr.tCCS_min)
This condition is always true. I think this should be replaced by:
if (chip->setup_data_interface)
And BTW, it should go in patch 3.
Quoted 61 lines
drivers/mtd/nand/nand_base.c +54
-0ndelay (chip->data_interface.timings.sdr.tCCS_min /1000 ); else ndelay(500);@@ -1233,6 +1233,118 @@ static int nand_init_data_interface(struct nand_chip *chip) }/** +* nand_fill_column_cycles - fill the column fields on an address array +* @chip: The NAND chip +* @addrs: Array of address cycles to fill +* @offset_in_page: The offset in the page +* +* Fills the first or the two first bytes of the @addrs field depending +* on the NAND bus width and the page size. +*/ +int nand_fill_column_cycles (struct nand_chip *chip,u8 *addrs, +unsigned int offset_in_page) +{ +struct mtd_info *mtd =nand_to_mtd (chip); + +/* +* The offset in page is expressed in bytes, if the NAND bus is 16-bit +* wide, then it must be divided by 2. +*/ +if (chip->options &NAND_BUSWIDTH_16 ) { +if (offset_in_page %2 ) { +WARN_ON (true); +return -EINVAL ; + } + + offset_in_page /=2 ; + } + + addrs[0 ] = offset_in_page; + +/* Small pages use 1 cycle for the columns, while large page need 2 */ +if (mtd->writesize <=512 ) +return 1 ; + + addrs[1 ] = offset_in_page >>8 ; + +return 2 ; +} +EXPORT_SYMBOL_GPL (nand_fill_column_cycles); + +static int nand_sp_exec_read_page_op (struct nand_chip *chip,unsigned int page, +unsigned int offset_in_page,void *buf, +unsigned int len) +{ +struct mtd_info *mtd =nand_to_mtd (chip); +const struct nand_sdr_timings *sdr = +nand_get_sdr_timings (&chip->data_interface); +u8 addrs[4 ]; +struct nand_op_instr instrs[] = { +NAND_OP_CMD (NAND_CMD_READ0 ,0 ), +NAND_OP_ADDR (3 , addrs, sdr->tWB_max), +NAND_OP_WAIT_RDY (sdr->tR_max, sdr->tRR_min), +NAND_OP_DATA_IN (len, buf,0 ), + }; +struct nand_operation op =NAND_OPERATION (instrs); +
drivers/mtd/nand/nand_base.c static int nand_init_data_interface(struct nand_chip *chip) · line 1299
Quoted 10 lines
drivers/mtd/nand/nand_base.c +10
-0+/* Drop the DATA_OUT instruction if len is set to 0. */ +if (!len) + op.ninstrs--; + +if (offset_in_page >= mtd->writesize) + instrs[0 ].cmd.opcode =NAND_CMD_READOOB ; +else if (offset_in_page >=256 ) + instrs[0 ].cmd.opcode =NAND_CMD_READ1 ; + +if (nand_fill_column_cycles (chip, addrs, offset_in_page) <0 )
Why not returning the retcode of nand_fill_column_cycles() directly?
ret = nand_fill_column_cycles(chip, addrs, offset_in_page) if (ret < 0) return ret;
Quoted 24 lines
drivers/mtd/nand/nand_base.c +24
-0+return -EINVAL ; + + addrs[1 ] = page; + addrs[2 ] = page >>8 ; + +if (chip->options &NAND_ROW_ADDR_3 ) { + addrs[3 ] = page >>16 ; + instrs[1 ].addr.naddrs++; + } + +return nand_exec_op (chip, &op); +} + +static int nand_lp_exec_read_page_op (struct nand_chip *chip,unsigned int page, +unsigned int offset_in_page,void *buf, +unsigned int len) +{ +const struct nand_sdr_timings *sdr = +nand_get_sdr_timings (&chip->data_interface); +u8 addrs[5 ]; +struct nand_op_instr instrs[] = { +NAND_OP_CMD (NAND_CMD_READ0 ,0 ), +NAND_OP_ADDR (4 , addrs,0 ), +NAND_OP_CMD (NAND_CMD_READSTART , sdr->tWB_max),
drivers/mtd/nand/nand_base.c static int nand_init_data_interface(struct nand_chip *chip) · line 1333
Quoted 10 lines
drivers/mtd/nand/nand_base.c +10
-0+NAND_OP_WAIT_RDY (sdr->tR_max, sdr->tRR_min), + NAND_OP_DATA_IN(len, buf, 0), + }; +struct nand_operation op =NAND_OPERATION (instrs); + +/* Drop the DATA_IN instruction if len is set to 0. */ +if (!len) + op.ninstrs--; + +if (nand_fill_column_cycles (chip, addrs, offset_in_page))
Should be
if (nand_fill_column_cycles(chip, addrs, offset_in_page) < 0)
Quoted 18 lines
drivers/mtd/nand/nand_base.c +14
-0+return -EINVAL ; + + addrs[2 ] = page; + addrs[3 ] = page >>8 ; + +if (chip->options &NAND_ROW_ADDR_3 ) { + addrs[4 ] = page >>16 ; + instrs[1 ].addr.naddrs++; + } + +return nand_exec_op (chip, &op); +} + +/** * nand_read_page_op - Do a READ PAGE operation * @chip: The NAND chip * @page: page to read
drivers/mtd/nand/nand_base.c static int nand_init_data_interface(struct nand_chip *chip) · line 1361
Quoted 5 lines
drivers/mtd/nand/nand_base.c +1
-1@@ -1246,17 +1358,26 @@ static int nand_init_data_interface(struct nand_chip *chip)* Returns 0 for success or negative error code otherwise */ int nand_read_page_op (struct nand_chip *chip,unsigned int page, -unsigned int column,void *buf,unsigned int len) +unsigned int offset_in_page,void *buf,unsigned int len)
You should change the parameter name in patch 1 since this function is introduced there.
Quoted 50 lines
drivers/mtd/nand/nand_base.c +30
-2{struct mtd_info *mtd =nand_to_mtd (chip);if (len && !buf)return -EINVAL ; -if (column + len > mtd->writesize + mtd->oobsize) +if (offset_in_page + len > mtd->writesize + mtd->oobsize)return -EINVAL ; - chip->cmdfunc (mtd,NAND_CMD_READ0 , column, page); +if (chip->exec_op) { +if (mtd->writesize >512 ) +return nand_lp_exec_read_page_op ( + chip, page, offset_in_page, buf, len); + +return nand_sp_exec_read_page_op (chip, page, offset_in_page, + buf, len); + } + + chip->cmdfunc (mtd,NAND_CMD_READ0 , offset_in_page, page);if (len) chip->read_buf (mtd, buf, len);@@ -1286,6 +1407,25 @@ static int nand_read_param_page_op(struct nand_chip *chip, u8 page, void *buf,if (len && !buf)return -EINVAL ; +if (chip->exec_op) { +const struct nand_sdr_timings *sdr = +nand_get_sdr_timings (&chip->data_interface); +struct nand_op_instr instrs[] = { +NAND_OP_CMD (NAND_CMD_PARAM ,0 ), +NAND_OP_ADDR (1 , &page, sdr->tWB_max), +NAND_OP_WAIT_RDY (sdr->tR_max, sdr->tRR_min), +NAND_OP_8BIT_DATA_IN (len, buf,0 ), + }; +struct nand_operation op = +NAND_OPERATION (instrs); + +/* Drop the DATA_IN instruction if len is set to 0. */ +if (!len) + op.ninstrs--; + +return nand_exec_op (chip, &op); + } + chip->cmdfunc (mtd,NAND_CMD_PARAM , page,-1 );for (i =0 ; i < len; i++) p[i] = chip->read_byte (mtd);
[...]
Quoted 15 lines
drivers/mtd/nand/nand_base.c +2
-1+/** * nand_prog_page_begin_op - starts a PROG PAGE operation * @chip: The NAND chip @@ -1371,7 +1608,7 @@ EXPORT_SYMBOL_GPL(nand_read_oob_op);* Returns 0 for success or negative error code otherwise */ int nand_prog_page_begin_op (struct nand_chip *chip,unsigned int page, -unsigned int column,const void *buf, +unsigned int offset_in_page,const void *buf,unsigned int len) {struct mtd_info *mtd =nand_to_mtd (chip);@@ -1379,10 +1616,14 @@ int nand_prog_page_begin_op(struct nand_chip *chip, unsigned int page,if (len && !buf)
drivers/mtd/nand/nand_base.c int nand_prog_page_begin_op(struct nand_chip *chip, unsigned int page, · line 1624
Quoted 10 lines
drivers/mtd/nand/nand_base.c +4
-2return -EINVAL ; -if (column + len > mtd->writesize + mtd->oobsize) +if (offset_in_page + len > mtd->writesize + mtd->oobsize)return -EINVAL ; - chip->cmdfunc (mtd,NAND_CMD_SEQIN , column, page); +if (chip->exec_op) +return nand_exec_prog_page_op ( + chip, page, offset_in_page, buf, len, false);
Param alignment please.
Quoted 306 lines
drivers/mtd/nand/nand_base.c +198
-6+ + chip->cmdfunc (mtd,NAND_CMD_SEQIN , offset_in_page, page);if (buf) chip->write_buf (mtd, buf, len);@@ -1405,6 +1646,19 @@ int nand_prog_page_end_op(struct nand_chip *chip)struct mtd_info *mtd =nand_to_mtd (chip);int status; +if (chip->exec_op) { +const struct nand_sdr_timings *sdr = +nand_get_sdr_timings (&chip->data_interface); +struct nand_op_instr instrs[] = { +NAND_OP_CMD (NAND_CMD_PAGEPROG , sdr->tWB_max), +NAND_OP_WAIT_RDY (sdr->tPROG_max,0 ), + }; +struct nand_operation op = +NAND_OPERATION (instrs); + +return nand_exec_op (chip, &op); + } + chip->cmdfunc (mtd,NAND_CMD_PAGEPROG ,-1 ,-1 ); status = chip->waitfunc (mtd, chip);@@ -1429,7 +1683,8 @@ EXPORT_SYMBOL_GPL(nand_prog_page_end_op);* Returns 0 for success or negative error code otherwise */ int nand_prog_page_op (struct nand_chip *chip,unsigned int page, -unsigned int column,const void *buf,unsigned int len) +unsigned int offset_in_page,const void *buf, +unsigned int len) {struct mtd_info *mtd =nand_to_mtd (chip);int status;@@ -1437,10 +1692,14 @@ int nand_prog_page_op(struct nand_chip *chip, unsigned int page,if (!len || !buf)return -EINVAL ; -if (column + len > mtd->writesize + mtd->oobsize) +if (offset_in_page + len > mtd->writesize + mtd->oobsize)return -EINVAL ; - chip->cmdfunc (mtd,NAND_CMD_SEQIN , column, page); +if (chip->exec_op) +return nand_exec_prog_page_op ( + chip, page, offset_in_page, buf, len, true); + + chip->cmdfunc (mtd,NAND_CMD_SEQIN , offset_in_page, page); chip->write_buf (mtd, buf, len); chip->cmdfunc (mtd,NAND_CMD_PAGEPROG ,-1 ,-1 );@@ -1465,7 +1724,8 @@ EXPORT_SYMBOL_GPL(nand_prog_page_op);* * Returns 0 for success or negative error code otherwise */ -int nand_change_write_column_op (struct nand_chip *chip,unsigned int column, +int nand_change_write_column_op (struct nand_chip *chip, +unsigned int offset_in_page,const void *buf,unsigned int len,bool force_8bit) {@@ -1474,10 +1734,38 @@ int nand_change_write_column_op(struct nand_chip *chip, unsigned int column,if (len && !buf)return -EINVAL ; -if (column + len > mtd->writesize + mtd->oobsize) +if (offset_in_page + len > mtd->writesize + mtd->oobsize)return -EINVAL ; - chip->cmdfunc (mtd,NAND_CMD_RNDIN , column,-1 ); +/* Small page NANDs do not support column change. */ +if (mtd->writesize <=512 ) +return -ENOTSUPP ; + +if (chip->exec_op) { +const struct nand_sdr_timings *sdr = +nand_get_sdr_timings (&chip->data_interface); +u8 addrs[2 ]; +struct nand_op_instr instrs[] = { +NAND_OP_CMD (NAND_CMD_RNDIN ,0 ), +NAND_OP_ADDR (2 , addrs, sdr->tCCS_min), +NAND_OP_DATA_OUT (len, buf,0 ), + }; +struct nand_operation op = +NAND_OPERATION (instrs); + +if (nand_fill_column_cycles (chip, addrs, offset_in_page) <0 ) +return -EINVAL ; + + instrs[2 ].data.force_8bit = force_8bit; + +/* Drop the DATA_OUT instruction if len is set to 0. */ +if (!len) + op.ninstrs--; + +return nand_exec_op (chip, &op); + } + + chip->cmdfunc (mtd,NAND_CMD_RNDIN , offset_in_page,-1 );if (len) chip->write_buf (mtd, buf, len);@@ -1508,6 +1796,24 @@ int nand_readid_op(struct nand_chip *chip, u8 addr,if (!len || !buf)return -EINVAL ; +if (chip->exec_op) { +const struct nand_sdr_timings *sdr = +nand_get_sdr_timings (&chip->data_interface); +struct nand_op_instr instrs[] = { +NAND_OP_CMD (NAND_CMD_READID ,0 ), +NAND_OP_ADDR (1 , &addr, sdr->tADL_min), +NAND_OP_8BIT_DATA_IN (len, buf,0 ), + }; +struct nand_operation op = +NAND_OPERATION (instrs); + +/* Drop the DATA_IN instruction if len is set to 0. */ +if (!len) + op.ninstrs--; + +return nand_exec_op (chip, &op); + } + chip->cmdfunc (mtd,NAND_CMD_READID , addr,-1 );for (i =0 ; i < len; i++)@@ -1532,6 +1838,22 @@ int nand_status_op(struct nand_chip *chip, u8 *status) {struct mtd_info *mtd =nand_to_mtd (chip); +if (chip->exec_op) { +const struct nand_sdr_timings *sdr = +nand_get_sdr_timings (&chip->data_interface); +struct nand_op_instr instrs[] = { +NAND_OP_CMD (NAND_CMD_STATUS , sdr->tADL_min), +NAND_OP_8BIT_DATA_IN (1 , status,0 ), + }; +struct nand_operation op = +NAND_OPERATION (instrs); + +if (!status) + op.ninstrs--; + +return nand_exec_op (chip, &op); + } + chip->cmdfunc (mtd,NAND_CMD_STATUS ,-1 ,-1 );if (status) *status = chip->read_byte (mtd);@@ -1558,6 +1880,25 @@ int nand_erase_op(struct nand_chip *chip, unsigned int eraseblock) (chip->phys_erase_shift - chip->page_shift);int status; +if (chip->exec_op) { +const struct nand_sdr_timings *sdr = +nand_get_sdr_timings (&chip->data_interface); +u8 addrs[3 ] = { page, page >>8 , page >>16 }; +struct nand_op_instr instrs[] = { +NAND_OP_CMD (NAND_CMD_ERASE1 ,0 ), +NAND_OP_ADDR (2 , addrs,0 ), +NAND_OP_CMD (NAND_CMD_ERASE2 , sdr->tWB_max), +NAND_OP_WAIT_RDY (sdr->tBERS_max,0 ), + }; +struct nand_operation op = +NAND_OPERATION (instrs); + +if (chip->options &NAND_ROW_ADDR_3 ) + instrs[1 ].addr.naddrs++; + +return nand_exec_op (chip, &op); + } + chip->cmdfunc (mtd,NAND_CMD_ERASE1 ,-1 , page); chip->cmdfunc (mtd,NAND_CMD_ERASE2 ,-1 ,-1 );@@ -1591,6 +1932,22 @@ static int nand_set_features_op(struct nand_chip *chip, u8 feature,const u8 *params = data;int i, status; +if (chip->exec_op) { +const struct nand_sdr_timings *sdr = +nand_get_sdr_timings (&chip->data_interface); +struct nand_op_instr instrs[] = { +NAND_OP_CMD (NAND_CMD_SET_FEATURES ,0 ), +NAND_OP_ADDR (1 , &feature, sdr->tADL_min), +NAND_OP_8BIT_DATA_OUT (ONFI_SUBFEATURE_PARAM_LEN , data, + sdr->tWB_max), +NAND_OP_WAIT_RDY (sdr->tFEAT_max,0 ), + }; +struct nand_operation op = +NAND_OPERATION (instrs); + +return nand_exec_op (chip, &op); + } + chip->cmdfunc (mtd,NAND_CMD_SET_FEATURES , feature,-1 );for (i =0 ; i <ONFI_SUBFEATURE_PARAM_LEN ; ++i) chip->write_byte (mtd, params[i]);@@ -1621,6 +1978,22 @@ static int nand_get_features_op(struct nand_chip *chip, u8 feature,u8 *params = data;int i; +if (chip->exec_op) { +const struct nand_sdr_timings *sdr = +nand_get_sdr_timings (&chip->data_interface); +struct nand_op_instr instrs[] = { +NAND_OP_CMD (NAND_CMD_GET_FEATURES ,0 ), +NAND_OP_ADDR (1 , &feature, sdr->tWB_max), +NAND_OP_WAIT_RDY (sdr->tFEAT_max, sdr->tRR_min), +NAND_OP_8BIT_DATA_IN (ONFI_SUBFEATURE_PARAM_LEN , + data,0 ), + }; +struct nand_operation op = +NAND_OPERATION (instrs); + +return nand_exec_op (chip, &op); + } + chip->cmdfunc (mtd,NAND_CMD_GET_FEATURES , feature,-1 );for (i =0 ; i <ONFI_SUBFEATURE_PARAM_LEN ; ++i) params[i] = chip->read_byte (mtd);@@ -1642,6 +2015,19 @@ int nand_reset_op(struct nand_chip *chip) {struct mtd_info *mtd =nand_to_mtd (chip); +if (chip->exec_op) { +const struct nand_sdr_timings *sdr = +nand_get_sdr_timings (&chip->data_interface); +struct nand_op_instr instrs[] = { +NAND_OP_CMD (NAND_CMD_RESET , sdr->tWB_max), +NAND_OP_WAIT_RDY (sdr->tRST_max,0 ), + }; +struct nand_operation op = +NAND_OPERATION (instrs); + +return nand_exec_op (chip, &op); + } + chip->cmdfunc (mtd,NAND_CMD_RESET ,-1 ,-1 );return 0 ;@@ -1669,6 +2055,18 @@ int nand_read_data_op(struct nand_chip *chip, void *buf, unsigned int len,if (!len || !buf)return -EINVAL ; +if (chip->exec_op) { +struct nand_op_instr instrs[] = { +NAND_OP_DATA_IN (len, buf,0 ), + }; +struct nand_operation op = +NAND_OPERATION (instrs); + + instrs[0 ].data.force_8bit = force_8bit; + +return nand_exec_op (chip, &op); + } +if (force_8bit) {u8 *p = buf;unsigned int i;@@ -1704,6 +2102,18 @@ int nand_write_data_op(struct nand_chip *chip, const void *buf,if (!len || !buf)return -EINVAL ; +if (chip->exec_op) { +struct nand_op_instr instrs[] = { +NAND_OP_DATA_OUT (len, buf,0 ), + }; +struct nand_operation op = +NAND_OPERATION (instrs); + + instrs[0 ].data.force_8bit = force_8bit; + +return nand_exec_op (chip, &op); + } +if (force_8bit) {const u8 *p = buf;unsigned int i;@@ -1719,6 +2129,471 @@ int nand_write_data_op(struct nand_chip *chip, const void *buf,EXPORT_SYMBOL_GPL (nand_write_data_op);/** +* struct nand_op_parser_ctx - Context used by the parser +* @instrs: array of all the instructions that must be addressed +* @ninstrs: length of the @instrs array +* @instr_idx: index of the instruction in the @instrs array that matches the +* first instruction of the subop structure +* @instr_start_off: offset at which the first instruction of the subop +* structure must start if it is and address or a data +* instruction +* +* This structure is used by the core to handle splitting lengthy instructions +* into sub-operations. +*/ +struct nand_op_parser_ctx { +const struct nand_op_instr *instrs; +unsigned int ninstrs; +unsigned int instr_idx; +unsigned int instr_start_off; +struct nand_subop subop; +}; + +/**
drivers/mtd/nand/nand_base.c int nand_write_data_op(struct nand_chip *chip, const void *buf, · line 2162
Quoted 10 lines
drivers/mtd/nand/nand_base.c +10
-0+* nand_op_parser_must_split_instr - Checks if an instruction must be split +* @pat: the parser pattern that match +* @instr: the instruction array to check +* @start_offset: the offset from which to start in the first instruction of the +* @instr array +* +* Some NAND controllers are limited and cannot send X address cycles with a +* unique operation, or cannot read/write more than Y bytes at the same time. +* In this case, reduce the scope of this set of operation, and trigger another +* instruction with the rest.
"
In this case, split the instruction that does not fit in a single controller-operation into two or more chunks.
"
drivers/mtd/nand/nand_base.c int nand_write_data_op(struct nand_chip *chip, const void *buf, · line 2164
Quoted 2 lines
drivers/mtd/nand/nand_base.c +2
-0+* +* Returns true if the array of instruction must be split, false otherwise.
s/array of//
drivers/mtd/nand/nand_base.c int nand_write_data_op(struct nand_chip *chip, const void *buf, · line 2166
Quoted 2 lines
drivers/mtd/nand/nand_base.c +2
-0+* The @start_offset parameter is also updated to the offset at which the first +* instruction of the next bundle of instruction must start (if an address or a
"
The @start_offset parameter is also updated to the offset at which the the next bundle of instructions must start ...
"
Quoted 147 lines
drivers/mtd/nand/nand_base.c +147
-0+* data instruction). +*/ +static bool +nand_op_parser_must_split_instr (const struct nand_op_parser_pattern_elem *pat, +const struct nand_op_instr *instr, +unsigned int *start_offset) +{ +switch (pat->type) { +case NAND_OP_ADDR_INSTR : +if (!pat->addr.maxcycles) +break ; + +if (instr->addr.naddrs - *start_offset > pat->addr.maxcycles) { + *start_offset += pat->addr.maxcycles; +return true; + } +break ; + +case NAND_OP_DATA_IN_INSTR : +case NAND_OP_DATA_OUT_INSTR : +if (!pat->data.maxlen) +break ; + +if (instr->data.len - *start_offset > pat->data.maxlen) { + *start_offset += pat->data.maxlen; +return true; + } +break ; + +default : +break ; + } + +return false; +} + +/** +* nand_op_parser_match_pat - Checks a pattern +* @pat: the parser pattern to check if it matches +* @ctx: the context structure to match with the pattern @pat +* +* Check if *one* given pattern matches the given sequence of instructions +*/ +static bool +nand_op_parser_match_pat (const struct nand_op_parser_pattern *pat, +struct nand_op_parser_ctx *ctx) +{ +unsigned int i, j, boundary_off = ctx->instr_start_off; + + ctx->subop.ninstrs =0 ; + +for (i = ctx->instr_idx, j =0 ; i < ctx->ninstrs && j < pat->nelems;) { +const struct nand_op_instr *instr = &ctx->instrs[i]; + +/* +* The pattern instruction does not match the operation +* instruction. If the instruction is marked optional in the +* pattern definition, we skip the pattern element and continue +* to the next one. If the element is mandatory, there's no +* match and we can return false directly. +*/ +if (instr->type != pat->elems[j].type) { +if (!pat->elems[j].optional) +return false; + + j++; +continue ; + } + +/* +* Now check the pattern element constraints. If the pattern is +* not able to handle the whole instruction in a single step, +* we'll have to break it down into several instructions. +* The *boudary_off value comes back updated to point to the +* limit between the split instruction (the end of the original +* chunk, the start of new next one). +*/ +if (nand_op_parser_must_split_instr (&pat->elems[j], instr, + &boundary_off)) { + ctx->subop.ninstrs++; + j++; +break ; + } + + ctx->subop.ninstrs++; + i++; + j++; + boundary_off =0 ; + } + +/* +* This can happen if all instructions of a pattern are optional. +* Still, if there's not at least one instruction handled by this +* pattern, this is not a match, and we should try the next one (if +* any). +*/ +if (!ctx->subop.ninstrs) +return false; + +/* +* We had a match on the pattern head, but the pattern may be longer +* than the instructions we're asked to execute. We need to make sure +* there's no mandatory elements in the pattern tail. +* +* The case where all the operations of a pattern have been checked but +* the number of instructions is bigger is handled right after this by +* returning true on the pattern match, which will order the execution +* of the subset of instructions later defined, while updating the +* context ids to the next chunk of instructions. +*/ +for (; j < pat->nelems; j++) { +if (!pat->elems[j].optional) +return false; + } + +/* +* We have a match: update the ctx and return true. The subop structure +* will be used by the pattern's ->exec() function. +*/ + ctx->subop.instrs = &ctx->instrs[ctx->instr_idx]; + ctx->subop.first_instr_start_off = ctx->instr_start_off; + ctx->subop.last_instr_end_off = boundary_off; + +/* +* Update the pointers so the calling function will be able to recall +* this one with a new subset of instructions. +* +* In the case where the last operation of this set is split, point to +* the last unfinished job, knowing the starting offset. +*/ + ctx->instr_idx = i; + ctx->instr_start_off = boundary_off; + +return true; +} + +#if IS_ENABLED (CONFIG_DYNAMIC_DEBUG ) || defined(DEBUG ) +static void nand_op_parser_trace (const struct nand_op_parser_ctx *ctx, +bool success) +{ +const struct nand_op_instr *instr; +bool in_subop = false; +char *is_in =" ->" , *is_out =" " , *prefix; +char *buf; +unsigned int len, off =0 ; +int i, j; +
drivers/mtd/nand/nand_base.c int nand_write_data_op(struct nand_chip *chip, const void *buf, · line 2323
Quoted 10 lines
drivers/mtd/nand/nand_base.c +10
-0+if (success) +pr_debug ("executing subop:\n" ); +else +pr_debug ("pattern not found:\n" ); + +for (i =0 ; i < ctx->ninstrs; i++) { + instr = &ctx->instrs[i]; + +/* +* ctx->instr_idx is not reliable because it may already had
have
drivers/mtd/nand/nand_base.c int nand_write_data_op(struct nand_chip *chip, const void *buf, · line 2327
Quoted 4 lines
drivers/mtd/nand/nand_base.c +4
-0+* been updated by the parser. Use pointers comparison instead. +*/ +if (instr == &ctx->subop.instrs[0 ]) + in_subop = true;
It seems in_subop is only used to select the prefix, so you can just get rid of it and do
if (instr == &ctx->subop.instrs[0]) prefix = " ->";
BTW, if success is false, you should not even try to change the prefix, right?
Quoted 36 lines
drivers/mtd/nand/nand_base.c +36
-0+ +if (in_subop) + prefix = is_in; +else + prefix = is_out; + +switch (instr->type) { +case NAND_OP_CMD_INSTR : +pr_debug ("%sCMD [0x%02x]\n" , prefix, + instr->cmd.opcode); +break ; +case NAND_OP_ADDR_INSTR : +/* +* A log line is much less than 50 bytes, plus 5 bytes +* per address cycle to display. +*/ + len =50 +5 * instr->addr.naddrs; + buf =kmalloc (len,GFP_KERNEL ); +if (!buf) +return ; +memset (buf,0 , len); + off +=snprintf (buf, len,"ADDR [%d cyc:" , + instr->addr.naddrs); +for (j =0 ; j < instr->addr.naddrs; j++) + off +=snprintf (&buf[off], len - off," 0x%02x" , + instr->addr.addrs[j]); +pr_debug ("%s%s]\n" , prefix, buf); +break ; +case NAND_OP_DATA_IN_INSTR : +pr_debug ("%sDATA_IN [%d B%s]\n" , prefix, + instr->data.len, + instr->data.force_8bit ?", force 8-bit" :"" ); +break ; +case NAND_OP_DATA_OUT_INSTR : + pr_debug("%sDATA_OUT [%d B%s]\n", prefix, + instr->data.len,
drivers/mtd/nand/nand_base.c int nand_write_data_op(struct nand_chip *chip, const void *buf, · line 2373
Quoted 10 lines
drivers/mtd/nand/nand_base.c +10
-0+ instr->data.force_8bit ? ", force 8-bit" : ""); +break ; +case NAND_OP_WAITRDY_INSTR : +pr_debug ("%sWAITRDY [max %d ms]\n" , prefix, + instr->waitrdy.timeout_ms); +break ; + } + +if (instr == &ctx->subop.instrs[ctx->subop.ninstrs -1 ]) + in_subop = false;
if (instr == &ctx->subop.instrs[ctx->subop.ninstrs - 1]) prefix = " ";
and initialize prefix to " " at the beginning of the function.
Quoted 38 lines
drivers/mtd/nand/nand_base.c +38
-0+ } +} +#else +static void nand_op_parser_trace (const struct nand_op_parser_ctx *ctx, +bool success) +{ +/* NOP */ +} +#endif + +/** +* nand_op_parser_exec_op - exec_op parser +* @chip: the NAND chip +* @parser: the parser to use given by the controller driver +* @op: the NAND operation to address +* @check_only: flag asking if the entire operation could be handled +* +* Function that must be called by each driver that implement the "exec_op API" +* in their own ->exec_op() implementation. +* +* The function iterates on all the instructions asked and make use of internal +* parsers to find matches between the instruction list and the handled patterns +* filled by the controller drivers inside the @parser structure. If needed, the +* instructions could be split into sub-operations and be executed sequentially. +*/ +int nand_op_parser_exec_op (struct nand_chip *chip, +const struct nand_op_parser *parser, +const struct nand_operation *op,bool check_only) +{ +struct nand_op_parser_ctx ctx = { + .instrs = op->instrs, + .ninstrs = op->ninstrs, + }; +unsigned int i; + +while (ctx.instr_idx < op->ninstrs) { +bool pattern_found = false; +int ret;
drivers/mtd/nand/nand_base.c int nand_write_data_op(struct nand_chip *chip, const void *buf, · line 2421
Quoted 10 lines
drivers/mtd/nand/nand_base.c +10
-0+ +for (i =0 ; i < parser->npatterns; i++) { +const struct nand_op_parser_pattern *pattern; + + pattern = &parser->patterns[i]; +if (!nand_op_parser_match_pat (pattern, &ctx)) +continue ; + +nand_op_parser_trace (&ctx, true); +
pattern_found should be set to true here. But I'm not even sure you really need this variable.
drivers/mtd/nand/nand_base.c int nand_write_data_op(struct nand_chip *chip, const void *buf, · line 2432
Quoted 11 lines
drivers/mtd/nand/nand_base.c +11
-0+if (check_only) +break ; + + ret = pattern->exec (chip, &ctx.subop); +if (ret) +return ret; + + pattern_found = true; + } + +if (!pattern_found) {
Just test
if (i == parser->npatterns) {and you should be good.
Quoted 7 lines
drivers/mtd/nand/nand_base.c +7
-0+nand_op_parser_trace (&ctx, false); +return -ENOTSUPP ; + } + } + +return 0 ; +}
drivers/mtd/nand/nand_base.c int nand_write_data_op(struct nand_chip *chip, const void *buf, · line 2449
Quoted 10 lines
drivers/mtd/nand/nand_base.c +10
-0+EXPORT_SYMBOL_GPL (nand_op_parser_exec_op); + +static bool nand_instr_is_data (const struct nand_op_instr *instr) +{ +return instr && (instr->type ==NAND_OP_DATA_IN_INSTR || + instr->type ==NAND_OP_DATA_OUT_INSTR ); +} + +static bool nand_subop_instr_is_valid (const struct nand_subop *subop, +unsigned int op_id)
s/op_id/instr_idx/
drivers/mtd/nand/nand_base.c int nand_write_data_op(struct nand_chip *chip, const void *buf, · line 2455
Quoted 6 lines
drivers/mtd/nand/nand_base.c +6
-0+{ +return subop && op_id < subop->ninstrs; +} + +static int nand_subop_get_start_off (const struct nand_subop *subop, +unsigned int op_id)
Ditto.
drivers/mtd/nand/nand_base.c int nand_write_data_op(struct nand_chip *chip, const void *buf, · line 2466
Quoted 11 lines
drivers/mtd/nand/nand_base.c +11
-0+{ +if (op_id) +return 0 ; + +return subop->first_instr_start_off; +} + +/** +* nand_subop_get_addr_start_off - Get the start offset in an address array +* @subop: The entire sub-operation +* @op_id: Index of the instruction inside the sub-operation
instr_idx.
drivers/mtd/nand/nand_base.c int nand_write_data_op(struct nand_chip *chip, const void *buf, · line 2476
Quoted 10 lines
drivers/mtd/nand/nand_base.c +10
-0+* +* Instructions arrays may be split by the parser between instructions, +* and also in the middle of an address instruction if the number of cycles +* to assert in one operation is not supported by the controller. +* +* For this, instead of using the first index of the ->addr.addrs field from the +* address instruction, the NAND controller driver must use this helper that +* will either return 0 if the index does not point to the first instruction of +* the sub-operation, or the offset of the next starting offset inside the +* address cycles.
I think you can drop this paragraph which IMO brings more confusion to what this function does.
drivers/mtd/nand/nand_base.c int nand_write_data_op(struct nand_chip *chip, const void *buf, · line 2479
Quoted 3 lines
drivers/mtd/nand/nand_base.c +3
-0+* +* Returns the offset of the first address cycle to assert from the pointed +* address instruction.
This part is definitely clearer.
Quoted 6 lines
drivers/mtd/nand/nand_base.c +6
-0+*/ +int nand_subop_get_addr_start_off (const struct nand_subop *subop, +unsigned int op_id) +{ +if (!nand_subop_instr_is_valid (subop, op_id) || + subop->instrs[op_id].type !=NAND_OP_ADDR_INSTR )
drivers/mtd/nand/nand_base.c int nand_write_data_op(struct nand_chip *chip, const void *buf, · line 2495
Quoted 10 lines
drivers/mtd/nand/nand_base.c +10
-0+return -EINVAL ; + +return nand_subop_get_start_off (subop, op_id); +} +EXPORT_SYMBOL_GPL (nand_subop_get_addr_start_off); + +/** +* nand_subop_get_num_addr_cyc - Get the remaining address cycles to assert +* @subop: The entire sub-operation +* @op_id: Index of the instruction inside the sub-operation
instr_idx
Quoted 38 lines
drivers/mtd/nand/nand_base.c +38
-0+* +* Instructions arrays may be split by the parser between instructions, +* and also in the middle of an address instruction if the number of cycles +* to assert in one operation is not supported by the controller. +* +* For this, instead of using the ->addr.naddrs fields of the address +* instruction, the NAND controller driver must use this helper that will +* return the actual number of cycles to assert between the first and last +* offset asked for this particular instruction. +* +* Returns the number of address cycles to assert from the pointed address +* instruction. +*/ +int nand_subop_get_num_addr_cyc (const struct nand_subop *subop, +unsigned int op_id) +{ +int start_off, end_off; + +if (!nand_subop_instr_is_valid (subop, op_id) || + subop->instrs[op_id].type !=NAND_OP_ADDR_INSTR ) +return -EINVAL ; + + start_off =nand_subop_get_addr_start_off (subop, op_id); + +if (op_id == subop->ninstrs -1 && + subop->last_instr_end_off) + end_off = subop->last_instr_end_off; +else + end_off = subop->instrs[op_id].addr.naddrs; + +return end_off - start_off; +} +EXPORT_SYMBOL_GPL (nand_subop_get_num_addr_cyc); + +/** +* nand_subop_get_data_start_off - Get the start offset in a data array +* @subop: The entire sub-operation +* @op_id: Index of the instruction inside the sub-operation
drivers/mtd/nand/nand_base.c int nand_write_data_op(struct nand_chip *chip, const void *buf, · line 2543
Quoted 10 lines
drivers/mtd/nand/nand_base.c +10
-0+* +* Instructions arrays may be split by the parser between instructions, +* and also in the middle of a data instruction if the number of bytes to access +* in one operation is greater that the controller limit. +* +* For this, instead of using the first index of the ->data.buf field from the +* data instruction, the NAND controller driver must use this helper that +* will either return 0 if the index does not point to the first instruction of +* the sub-operation, or the offset of the next starting offset inside the +* data buffer.
Same as for nand_subop_get_addr_start_off(), the explanation is confusing. I think we can drop it.
Quoted 112 lines
drivers/mtd/nand/nand_base.c +80
-7+* +* Returns the data offset inside the pointed data instruction buffer from which +* to start. +*/ +int nand_subop_get_data_start_off (const struct nand_subop *subop, +unsigned int op_id) +{ +if (!nand_subop_instr_is_valid (subop, op_id) || + !nand_instr_is_data (&subop->instrs[op_id])) +return -EINVAL ; + +return nand_subop_get_start_off (subop, op_id); +} +EXPORT_SYMBOL_GPL (nand_subop_get_data_start_off); + +/** +* nand_subop_get_data_len - Get the number of bytes to retrieve +* @subop: The entire sub-operation +* @op_id: Index of the instruction inside the sub-operation +* +* Instructions arrays may be split by the parser between instructions, +* and also in the middle of a data instruction if the number of bytes to access +* in one operation is greater that the controller limit. +* +* For this, instead of using the ->data.len field from the data instruction, +* the NAND controller driver must use this helper that will return the actual +* length of data to move between the first and last offset asked for this +* particular instruction. +* +* Returns the length of the data to move from the pointed data instruction. +*/ +int nand_subop_get_data_len (const struct nand_subop *subop, +unsigned int op_id) +{ +int start_off =0 , end_off; + +if (!nand_subop_instr_is_valid (subop, op_id) || + !nand_instr_is_data (&subop->instrs[op_id])) +return -EINVAL ; + + start_off =nand_subop_get_data_start_off (subop, op_id); + +if (op_id == subop->ninstrs -1 && + subop->last_instr_end_off) + end_off = subop->last_instr_end_off; +else + end_off = subop->instrs[op_id].data.len; + +return end_off - start_off; +} +EXPORT_SYMBOL_GPL (nand_subop_get_data_len); + +/** * nand_reset - Reset and initialize a NAND device * @chip: The NAND chip * @chipnr: Internal die id @@ -3940,7 +4815,7 @@ static void nand_set_defaults(struct nand_chip *chip) chip->chip_delay =20 ;/* check, if a user supplied command function given */ -if (chip->cmdfunc ==NULL ) +if (chip->cmdfunc ==NULL && !chip->exec_op) chip->cmdfunc = nand_command;/* check, if a user supplied wait function given */ @@ -4823,15 +5698,35 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips,if (!mtd->name && mtd->dev.parent) mtd->name =dev_name (mtd->dev.parent); -if ((!chip->cmdfunc || !chip->select_chip) && !chip->cmd_ctrl) { +/* +* ->cmdfunc() is legacy and will only be used if ->exec_op() is not +* populated. +*/ +if (chip->exec_op) {/* -* Default functions assigned for chip_select() and -* cmdfunc() both expect cmd_ctrl() to be populated, -* so we need to check that that's the case +* The implementation of ->exec_op() heavily relies on timings +* to be accessible through the nand_data_interface structure. +* Thus, the ->setup_data_interface() hook must be provided. The +* controller driver will be noticed of delays it must apply +* after each ->exec_op() instruction (if any) through the +* .delay_ns field. The driver will then choose to handle the +* delays manually if the controller cannot do it natively. */ -pr_err ("chip.cmd_ctrl() callback is not provided" ); -return -EINVAL ; +if (!chip->setup_data_interface) { +pr_err ("->setup_data_interface() should be provided\n" ); +return -EINVAL ; + } + }else { +/* +* Default functions assigned for ->cmdfunc() and +* ->select_chip() both expect ->cmd_ctrl() to be populated. +*/ +if ((!chip->cmdfunc || !chip->select_chip) && !chip->cmd_ctrl) { +pr_err ("->cmd_ctrl() should be provided\n" ); +return -EINVAL ; + } } +/* Set the default functions */ nand_set_defaults (chip);drivers/mtd/nand/nand_hynix.c +0
-0
drivers/mtd/nand/nand_hynix.c static bool hynix_nand_has_valid_jedecid(struct nand_chip *chip) · line 77
Quoted 4 lines
drivers/mtd/nand/nand_hynix.c +1
-0@@ -74,19 +74,36 @@ static bool hynix_nand_has_valid_jedecid(struct nand_chip *chip)return !strncmp ("JEDEC" , jedecid,sizeof (jedecid)); } +static int hynix_nand_cmd_op (struct nand_chip *chip,u8 cmd)
Maybe you can introduce the helper in patch 1
drivers/mtd/nand/nand_hynix.c static bool hynix_nand_has_valid_jedecid(struct nand_chip *chip) · line 87
Quoted 10 lines
drivers/mtd/nand/nand_hynix.c +10
-0+{ +struct mtd_info *mtd =nand_to_mtd (chip); + +if (chip->exec_op) { +struct nand_op_instr instrs[] = { +NAND_OP_CMD (cmd,0 ), + }; +struct nand_operation op =NAND_OPERATION (instrs); + +return nand_exec_op (chip, &op);
And then ass this block of code in this patch.
Quoted 25 lines
drivers/mtd/nand/nand_hynix.c +8
-2+ } + + chip->cmdfunc (mtd, cmd,-1 ,-1 ); + +return 0 ; +} +static int hynix_nand_setup_read_retry (struct mtd_info *mtd,int retry_mode) {struct nand_chip *chip =mtd_to_nand (mtd);struct hynix_nand *hynix =nand_get_manufacturer_data (chip);const u8 *values; -int status;int i; values = hynix->read_retry->values + (retry_mode * hynix->read_retry->nregs);/* Enter 'Set Hynix Parameters' mode */ - chip->cmdfunc (mtd,NAND_HYNIX_CMD_SET_PARAMS ,-1 ,-1 ); +hynix_nand_cmd_op (chip,NAND_HYNIX_CMD_SET_PARAMS );/* * Configure the NAND in the requested read-retry mode.
drivers/mtd/nand/nand_hynix.c static int hynix_nand_setup_read_retry(struct mtd_info *mtd, int retry_mode) · line 123
Quoted 8 lines
drivers/mtd/nand/nand_hynix.c +3
-2@@ -101,16 +118,17 @@ static int hynix_nand_setup_read_retry(struct mtd_info *mtd, int retry_mode)int column = hynix->read_retry->regs[i]; column |= column <<8 ; - chip->cmdfunc (mtd,NAND_CMD_NONE , column,-1 ); - chip->write_byte (mtd, values[i]); +if (chip->exec_op) { +nand_change_write_column_op (chip, column, + &values[i],1 , true);
This is not a nand_change_write_column_op() op, here the cmd cycle is set to NAND_CMD_NONE. You should have your own operation defined to handle this sequence.
drivers/mtd/nand/nand_hynix.c static int hynix_nand_setup_read_retry(struct mtd_info *mtd, int retry_mode) · line 131
Quoted 13 lines
drivers/mtd/nand/nand_hynix.c +5
-5+ }else { + chip->cmdfunc (mtd,NAND_CMD_NONE , column,-1 ); + chip->write_byte (mtd, values[i]); + } }/* Apply the new settings. */ - chip->cmdfunc (mtd,NAND_HYNIX_CMD_APPLY_PARAMS ,-1 ,-1 ); - - status = chip->waitfunc (mtd, chip); -if (status &NAND_STATUS_FAIL ) -return -EIO ; +hynix_nand_cmd_op (chip,NAND_HYNIX_CMD_APPLY_PARAMS );
No, it's wrong, you miss the WAITRDY instruction to be compatible with what was done before.
Quoted 13 lines
drivers/mtd/nand/nand_hynix.c +1
-1return 0 ; }@@ -173,32 +191,65 @@ static int hynix_read_rr_otp(struct nand_chip *chip,nand_reset_op (chip); - chip->cmdfunc (mtd,NAND_HYNIX_CMD_SET_PARAMS ,-1 ,-1 ); +hynix_nand_cmd_op (chip,NAND_HYNIX_CMD_SET_PARAMS );for (i =0 ; i < info->nregs; i++) {int column = info->regs[i];
drivers/mtd/nand/nand_hynix.c static int hynix_read_rr_otp(struct nand_chip *chip, · line 206
Quoted 10 lines
drivers/mtd/nand/nand_hynix.c +7
-2column |= column <<8 ; - chip->cmdfunc (mtd,NAND_CMD_NONE , column,-1 ); - chip->write_byte (mtd, info->values[i]); +if (chip->exec_op) { +nand_change_write_column_op (chip, column, + &info->values[i],1 , true); + }else { + chip->cmdfunc (mtd,NAND_CMD_NONE , column,-1 ); + chip->write_byte (mtd, info->values[i]); + }
Same comments apply here.
Quoted 13 lines
drivers/mtd/nand/nand_hynix.c +5
-4} - chip->cmdfunc (mtd,NAND_HYNIX_CMD_APPLY_PARAMS ,-1 ,-1 ); +hynix_nand_cmd_op (chip,NAND_HYNIX_CMD_APPLY_PARAMS );/* Sequence to enter OTP mode? */ - chip->cmdfunc (mtd,0x17 ,-1 ,-1 ); - chip->cmdfunc (mtd,0x04 ,-1 ,-1 ); - chip->cmdfunc (mtd,0x19 ,-1 ,-1 ); +if (chip->exec_op) { +struct nand_op_instr instrs[] = { +NAND_OP_CMD (0x17 ,0 ), +NAND_OP_CMD (0x04 ,0 ),
drivers/mtd/nand/nand_hynix.c static int hynix_read_rr_otp(struct nand_chip *chip, · line 225
Quoted 10 lines
drivers/mtd/nand/nand_hynix.c +10
-0+ NAND_OP_CMD(0x19, 0), + }; +struct nand_operation op =NAND_OPERATION (instrs); + +nand_exec_op (chip, &op); + }else { + chip->cmdfunc (mtd,0x17 ,-1 ,-1 ); + chip->cmdfunc (mtd,0x04 ,-1 ,-1 ); + chip->cmdfunc (mtd,0x19 ,-1 ,-1 ); + }
Why not creating a hynix_nand_enter_otp_mode_op() for that?
Quoted 21 lines
drivers/mtd/nand/nand_hynix.c +11
-4/* Now read the page */ nand_read_page_op (chip, info->page,0 , buf, info->size);/* Put everything back to normal */ nand_reset_op (chip); - chip->cmdfunc (mtd,NAND_HYNIX_CMD_SET_PARAMS ,0x38 ,-1 ); - chip->write_byte (mtd,0x0 ); - chip->cmdfunc (mtd,NAND_HYNIX_CMD_APPLY_PARAMS ,-1 ,-1 ); - chip->cmdfunc (mtd,NAND_CMD_READ0 ,0x0 ,-1 ); +if (chip->exec_op) { +const struct nand_sdr_timings *sdr = +nand_get_sdr_timings (&chip->data_interface); +u8 byte =0 ; +u8 addr =0x38 ; +struct nand_op_instr instrs[] = { +NAND_OP_CMD (NAND_HYNIX_CMD_SET_PARAMS ,0 ), +NAND_OP_ADDR (1 , &addr, sdr->tCCS_min), +NAND_OP_8BIT_DATA_OUT (1 , &byte,0 ), +NAND_OP_CMD (NAND_HYNIX_CMD_APPLY_PARAMS ,0 ), +NAND_OP_CMD (NAND_CMD_READ0 ,0 ),
drivers/mtd/nand/nand_hynix.c static int hynix_read_rr_otp(struct nand_chip *chip, · line 252
Quoted 10 lines
drivers/mtd/nand/nand_hynix.c +10
-0+ }; +struct nand_operation op =NAND_OPERATION (instrs); + +nand_exec_op (chip, &op); + }else { + chip->cmdfunc (mtd,NAND_HYNIX_CMD_SET_PARAMS ,0x38 ,-1 ); + chip->write_byte (mtd,0x0 ); + chip->cmdfunc (mtd,NAND_HYNIX_CMD_APPLY_PARAMS ,-1 ,-1 ); + chip->cmdfunc (mtd,NAND_CMD_READ0 ,0x0 ,-1 ); + }
Same here.
Quoted 21 lines
drivers/mtd/nand/nand_hynix.c +0
-0return 0 ; }drivers/mtd/nand/nand_micron.c +2
-4@@ -117,14 +117,38 @@ micron_nand_read_page_on_die_ecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { - int status; + u8 status;int max_bitflips =0 ;micron_nand_on_die_ecc_setup (chip, true); - chip->cmdfunc (mtd,NAND_CMD_READ0 ,0x00 , page); - chip->cmdfunc (mtd,NAND_CMD_STATUS ,-1 ,-1 ); - status = chip->read_byte (mtd); +if (chip->exec_op) {
drivers/mtd/nand/nand_micron.c micron_nand_read_page_on_die_ecc(struct mtd_info *mtd, struct nand_chip *chip, · line 135
Quoted 10 lines
drivers/mtd/nand/nand_micron.c +10
-0+u8 addrs[5 ] = {}; +struct nand_op_instr instrs[] = { +NAND_OP_CMD (NAND_CMD_READ0 ,0 ), +NAND_OP_ADDR (4 , addrs,0 ), +NAND_OP_CMD (NAND_CMD_STATUS ,0 ), +NAND_OP_8BIT_DATA_IN (1 , &status,0 ), + }; +struct nand_operation op =NAND_OPERATION (instrs); + +if (nand_fill_column_cycles (chip, addrs,0 ))
if (nand_fill_column_cycles(chip, addrs, 0) < 0)
Quoted 64 lines
drivers/mtd/nand/nand_micron.c +16
-0+return -EINVAL ; + + addrs[2 ] = page; + addrs[3 ] = page >>8 ; +if (chip->options &NAND_ROW_ADDR_3 ) { + addrs[4 ] = page >>16 ; + instrs[1 ].addr.naddrs++; + } + +nand_exec_op (chip, &op); + }else { + chip->cmdfunc (mtd,NAND_CMD_READ0 ,0x00 , page); + chip->cmdfunc (mtd,NAND_CMD_STATUS ,-1 ,-1 ); + status = chip->read_byte (mtd); + } +if (status &NAND_STATUS_FAIL ) mtd->ecc_stats.failed++;/* include/linux/mtd/rawnand.h +37
-0@@ -751,6 +751,337 @@ struct nand_manufacturer_ops { };/** +* struct nand_op_cmd_instr - Definition of a command instruction +* @opcode: the command to assert in one cycle +*/ +struct nand_op_cmd_instr { +u8 opcode; +}; + +/** +* struct nand_op_addr_instr - Definition of an address instruction +* @naddrs: length of the @addrs array +* @addrs: array containing the address cycles to assert +*/ +struct nand_op_addr_instr { +unsigned int naddrs; +const u8 *addrs; +}; + +/** +* struct nand_op_data_instr - Definition of a data instruction +* @len: number of data bytes to move +* @in: buffer to fill when reading from the NAND chip +* @out: buffer to read from when writing to the NAND chip +* @force_8bit: force 8-bit access +* +* Please note that "in" and "out" are inverted from the ONFI specification +* and are from the controller perspective, so a "in" is a read from the NAND +* chip while a "out" is a write to the NAND chip. +*/ +struct nand_op_data_instr { +unsigned int len; +union { +void *in; +const void *out; + }; +bool force_8bit; +}; +
include/linux/mtd/rawnand.h struct nand_manufacturer_ops { · line 800
Quoted 10 lines
include/linux/mtd/rawnand.h +10
-0+/** +* struct nand_op_waitrdy_instr - Definition of a wait ready instruction +* @timeout_ms: maximum delay while waiting for the ready/busy pin in ms +*/ +struct nand_op_waitrdy_instr { +unsigned int timeout_ms; +}; + +/** +* enum nand_op_instr_type - Enumeration of all the instructions
*of all instruction types
Quoted 4 lines
include/linux/mtd/rawnand.h +4
-0+* +* Please note that data instructions are separated into DATA_IN and DATA_OUT +* instructions. +*/
include/linux/mtd/rawnand.h struct nand_manufacturer_ops { · line 814
Quoted 10 lines
include/linux/mtd/rawnand.h +10
-0+enum nand_op_instr_type { +NAND_OP_CMD_INSTR , +NAND_OP_ADDR_INSTR , +NAND_OP_DATA_IN_INSTR , +NAND_OP_DATA_OUT_INSTR , +NAND_OP_WAITRDY_INSTR , +}; + +/** +* struct nand_op_instr - Generic definition of and instruction
s/and/an/
include/linux/mtd/rawnand.h struct nand_manufacturer_ops { · line 817
Quoted 3 lines
include/linux/mtd/rawnand.h +3
-0+* @type: an enumeration of the instruction type +* @cmd/@addr/@data/@waitrdy: the actual instruction to refer depending on the +* value of @type
"
extra data associated to the instruction. You'll have to use the appropriate element depending on @type"
Quoted 14 lines
include/linux/mtd/rawnand.h +14
-0+* @delay_ns: delay to apply by the controller after the instruction has been +* actually executed (most of them are directly handled by the +* controllers once the timings negociation has been done) +*/ +struct nand_op_instr { +enum nand_op_instr_type type; +union { +struct nand_op_cmd_instr cmd; +struct nand_op_addr_instr addr; +struct nand_op_data_instr data; +struct nand_op_waitrdy_instr waitrdy; + }; +unsigned int delay_ns; +};
include/linux/mtd/rawnand.h struct nand_manufacturer_ops { · line 841
Quoted 10 lines
include/linux/mtd/rawnand.h +10
-0+ +/* +* Special handling must be done for the WAITRDY timeout parameter as it usually +* is either tPROG (after a prog), tR (before a read), tRST (during a reset) or +* tBERS (during an erase) which all of them are u64 values that cannot be +* divided by usual kernel macros and must be handled with the special +* DIV_ROUND_UP_ULL() macro. +*/ +#define PSEC_TO_NSEC (x) DIV_ROUND_UP(x, 10^3) +#define PSEC_TO_MSEC (x) DIV_ROUND_UP_ULL(x, 10^9)
Hm, that's a bit of a mess to let each macro decide which converter they should use, because we don't know at this level whether the timing is stored in an u64 or u32. How about doing the conversion in the call-sites instead, because there you should know what you manipulate.
Something like
NAND_OP_CMD(FOO, PSEC_TO_NSEC_UL(sdr->tXX)) NAND_OP_WAITRDY(PSEC_TO_MSEC_ULL(sdr->tXX), PSEC_TO_NSEC_UL(sdr->tYY))
Quoted 80 lines
include/linux/mtd/rawnand.h +80
-0+ +#define NAND_OP_CMD (id, delay_ps) \ + { \ + .type = NAND_OP_CMD_INSTR, \ + .cmd.opcode = id, \ + .delay_ns = PSEC_TO_NSEC(delay_ps), \ + } + +#define NAND_OP_ADDR (ncycles, cycles, delay_ps) \ + { \ + .type = NAND_OP_ADDR_INSTR, \ + .addr = { \ + .naddrs = ncycles, \ + .addrs = cycles, \ + }, \ + .delay_ns = PSEC_TO_NSEC(delay_ps), \ + } + +#define NAND_OP_DATA_IN (l, buf, delay_ps) \ + { \ + .type = NAND_OP_DATA_IN_INSTR, \ + .data = { \ + .len = l, \ + .in = buf, \ + .force_8bit = false, \ + }, \ + .delay_ns = PSEC_TO_NSEC(delay_ps), \ + } + +#define NAND_OP_DATA_OUT (l, buf, delay_ps) \ + { \ + .type = NAND_OP_DATA_OUT_INSTR, \ + .data = { \ + .len = l, \ + .out = buf, \ + .force_8bit = false, \ + }, \ + .delay_ns = PSEC_TO_NSEC(delay_ps), \ + } + +#define NAND_OP_8BIT_DATA_IN (l, buf, delay_ps) \ + { \ + .type = NAND_OP_DATA_IN_INSTR, \ + .data = { \ + .len = l, \ + .in = buf, \ + .force_8bit = true, \ + }, \ + .delay_ns = PSEC_TO_NSEC(delay_ps), \ + } + +#define NAND_OP_8BIT_DATA_OUT (l, buf, delay_ps) \ + { \ + .type = NAND_OP_DATA_OUT_INSTR, \ + .data = { \ + .len = l, \ + .out = buf, \ + .force_8bit = true, \ + }, \ + .delay_ns = PSEC_TO_NSEC(delay_ps), \ + } + +#define NAND_OP_WAIT_RDY (tout_ps, delay_ps) \ + { \ + .type = NAND_OP_WAITRDY_INSTR, \ + .waitrdy.timeout_ms = PSEC_TO_MSEC(tout_ps), \ + .delay_ns = PSEC_TO_NSEC(delay_ps), \ + } + +/** +* struct nand_subop - a sub operation +* @instrs: array of instructions +* @ninstrs: length of the @instrs array +* @first_instr_start_off: offset to start from for the first instruction +* of the sub-operation +* @last_instr_end_off: offset to end at (excluded) for the last instruction +* of the sub-operation +* +* When an operation cannot be handled as is by the NAND controller, it will +* be split by the parser and the remaining pieces will be handled as
include/linux/mtd/rawnand.h struct nand_manufacturer_ops { · line 931
Quoted 10 lines
include/linux/mtd/rawnand.h +10
-0+* sub-operations. +*/ +struct nand_subop { +const struct nand_op_instr *instrs; +unsigned int ninstrs; +/* +* Specific offset for the first and the last instructions of the subop. +* Applies for the address cycles in the case of address, or for data +* offset in the case of data transfers. Otherwise, it is irrelevant. +*/
Can you move that in the kernel header doc?
Quoted 19 lines
include/linux/mtd/rawnand.h +19
-0+unsigned int first_instr_start_off; +unsigned int last_instr_end_off; +}; + +int nand_subop_get_addr_start_off (const struct nand_subop *subop, +unsigned int op_id); +int nand_subop_get_num_addr_cyc (const struct nand_subop *subop, +unsigned int op_id); +int nand_subop_get_data_start_off (const struct nand_subop *subop, +unsigned int op_id); +int nand_subop_get_data_len (const struct nand_subop *subop, +unsigned int op_id); + +/** +* struct nand_op_parser_addr_constraints - Constraints for address instructions +* @maxcycles: maximum number of cycles that the controller can assert by +* instruction +*/ +struct nand_op_parser_addr_constraints {
include/linux/mtd/rawnand.h struct nand_manufacturer_ops { · line 960
Quoted 10 lines
include/linux/mtd/rawnand.h +10
-0+unsigned int maxcycles; +}; + +/** +* struct nand_op_parser_data_constraints - Constraints for data instructions +* @maxlen: maximum data length that the controller can handle with one +* instruction +*/ +struct nand_op_parser_data_constraints { +unsigned int maxlen;
At some point we should probably add minlen and alignment fields, but let's keep that for later.
Quoted 51 lines
include/linux/mtd/rawnand.h +51
-0+}; + +/** +* struct nand_op_parser_pattern_elem - One element of a pattern +* @type: the instructuction type +* @optional: if this element of the pattern is optional or mandatory +* @addr/@data: address or data constraint (number of cycles or data length) +*/ +struct nand_op_parser_pattern_elem { +enum nand_op_instr_type type; +bool optional; +union { +struct nand_op_parser_addr_constraints addr; +struct nand_op_parser_data_constraints data; + }; +}; + +#define NAND_OP_PARSER_PAT_CMD_ELEM (_opt) \ + { \ + .type = NAND_OP_CMD_INSTR, \ + .optional = _opt, \ + } + +#define NAND_OP_PARSER_PAT_ADDR_ELEM (_opt, _maxcycles) \ + { \ + .type = NAND_OP_ADDR_INSTR, \ + .optional = _opt, \ + .addr.maxcycles = _maxcycles, \ + } + +#define NAND_OP_PARSER_PAT_DATA_IN_ELEM (_opt, _maxlen) \ + { \ + .type = NAND_OP_DATA_IN_INSTR, \ + .optional = _opt, \ + .data.maxlen = _maxlen, \ + } + +#define NAND_OP_PARSER_PAT_DATA_OUT_ELEM (_opt, _maxlen) \ + { \ + .type = NAND_OP_DATA_OUT_INSTR, \ + .optional = _opt, \ + .data.maxlen = _maxlen, \ + } + +#define NAND_OP_PARSER_PAT_WAITRDY_ELEM (_opt) \ + { \ + .type = NAND_OP_WAITRDY_INSTR, \ + .optional = _opt, \ + } + +/**
include/linux/mtd/rawnand.h struct nand_manufacturer_ops { · line 1021
Quoted 10 lines
include/linux/mtd/rawnand.h +10
-0+* struct nand_op_parser_pattern - A complete pattern +* @elems: array of pattern elements +* @nelems: number of pattern elements in @elems array +* @exec: the function that will actually execute this pattern, written in the +* controller driver +* +* This is a complete pattern that is a list of elements, each one reprensenting +* one instruction with its constraints. Controller drivers must declare as much +* patterns as they support and give the list of the supported patterns (created +* with the help of the following macro) at the call to nand_op_parser_exec_op
s/at the call to nand_op_parser_exec_op/when calling nand_op_parser_exec_op()/
include/linux/mtd/rawnand.h struct nand_manufacturer_ops { · line 1023
Quoted 2 lines
include/linux/mtd/rawnand.h +2
-0+* which shall be the main thing to do in the driver implementation of +* ->exec_op().
I'd be more lax than that. Yes the parser is the preferred approach when you deal with a complex controller. But for simple controllers it's not.
Quoted 2 lines
Once there is a match between the pattern and an operation, the
+ * core calls the @exec function to actually do the operation.
Not necessarily. The plan is still to ask the controller which operation it supports before actually executing them. So, in this case (when check_only param is true), nand_op_parser_exec_op() will never call ->exec(), it will just make sure the operation can be handled with the provided patterns.
Quoted 11 lines
+ */
include/linux/mtd/rawnand.h +10
-0+struct nand_op_parser_pattern { +const struct nand_op_parser_pattern_elem *elems; +unsigned int nelems; +int (*exec)(struct nand_chip *chip,const struct nand_subop *subop); +}; + +#define NAND_OP_PARSER_PATTERN (_exec, ...) \ + { \ + .exec = _exec, \ + .elems = (struct nand_op_parser_pattern_elem[]) { __VA_ARGS__ }, \
include/linux/mtd/rawnand.h struct nand_manufacturer_ops { · line 1045
Quoted 10 lines
include/linux/mtd/rawnand.h +10
-0+ .nelems = sizeof((struct nand_op_parser_pattern_elem[]) { __VA_ARGS__ }) / \ + sizeof(struct nand_op_parser_pattern_elem), \ + } + +/** +* struct nand_op_parser - The actual parser +* @patterns: array of patterns +* @npatterns: length of the @patterns array +* +* The actual parser structure wich is an array of supported patterns.
It's worth mentioning that patterns will be tested in their declaration order, and the first match will be taken, so it's important to oder patterns appropriately so that simple/inefficient patterns are placed at the end of the list. Usually, this is where you put single instruction patterns.
Quoted 9 lines
+ */
include/linux/mtd/rawnand.h +8
-0+struct nand_op_parser { +const struct nand_op_parser_pattern *patterns; +unsigned int npatterns; +}; + +#define NAND_OP_PARSER (...) \ + { \ + .patterns = (struct nand_op_parser_pattern[]) { __VA_ARGS__ }, \
include/linux/mtd/rawnand.h struct nand_manufacturer_ops { · line 1064
Quoted 10 lines
include/linux/mtd/rawnand.h +10
-0+ .npatterns = sizeof((struct nand_op_parser_pattern[]) { __VA_ARGS__ }) / \ + sizeof(struct nand_op_parser_pattern), \ + } + +/** +* struct nand_operation - The actual operation +* @instrs: array of instructions to execute +* @ninstrs: length of the @instrs array +* +* The actual operation structure that will be given to the parser.
Not only the parser, it will be passed to chip->exep_op() as well.
Quoted 86 lines
+ */
include/linux/mtd/rawnand.h +44
-10+struct nand_operation { +const struct nand_op_instr *instrs; +unsigned int ninstrs; +}; + +#define NAND_OPERATION (_instrs) \ + { \ + .instrs = _instrs, \ + .ninstrs = ARRAY_SIZE(_instrs), \ + } + +int nand_fill_column_cycles (struct nand_chip *chip,u8 *addrs, +unsigned int offset_in_page); + +int nand_op_parser_exec_op (struct nand_chip *chip, +const struct nand_op_parser *parser, +const struct nand_operation *op,bool check_only); + +/** * struct nand_chip - NAND Private Flash Chip Data * @mtd: MTD device registered to the MTD framework * @IO_ADDR_R: [BOARDSPECIFIC] address to read the 8 I/O lines of the @@ -885,6 +1216,9 @@ struct nand_chip {int (*setup_data_interface)(struct mtd_info *mtd,int chipnr,const struct nand_data_interface *conf); +int (*exec_op)(struct nand_chip *chip, +const struct nand_operation *op, +bool check_only);int chip_delay;unsigned int options;@@ -945,6 +1279,15 @@ struct nand_chip { } manufacturer; }; +static inline int nand_exec_op (struct nand_chip *chip, +const struct nand_operation *op) +{ +if (!chip->exec_op) +return -ENOTSUPP ; + +return chip->exec_op (chip, op, false); +} +extern const struct mtd_ooblayout_ops nand_ooblayout_sp_ops;extern const struct mtd_ooblayout_ops nand_ooblayout_lp_ops;@@ -1307,23 +1650,26 @@ int nand_readid_op(struct nand_chip *chip, u8 addr, void *buf,int nand_status_op (struct nand_chip *chip,u8 *status);int nand_erase_op (struct nand_chip *chip,unsigned int eraseblock);int nand_read_page_op (struct nand_chip *chip,unsigned int page, -unsigned int column,void *buf,unsigned int len); -int nand_change_read_column_op (struct nand_chip *chip,unsigned int column, -void *buf,unsigned int len,bool force_8bit); +unsigned int offset_in_page,void *buf,unsigned int len); +int nand_change_read_column_op (struct nand_chip *chip, +unsigned int offset_in_page,void *buf, +unsigned int len,bool force_8bit);int nand_read_oob_op (struct nand_chip *chip,unsigned int page, -unsigned int column,void *buf,unsigned int len); +unsigned int offset_in_page,void *buf,unsigned int len);int nand_prog_page_begin_op (struct nand_chip *chip,unsigned int page, -unsigned int column,const void *buf, +unsigned int offset_in_page,const void *buf,unsigned int len);int nand_prog_page_end_op (struct nand_chip *chip);int nand_prog_page_op (struct nand_chip *chip,unsigned int page, -unsigned int column,const void *buf,unsigned int len); -int nand_change_write_column_op (struct nand_chip *chip,unsigned int column, -const void *buf,unsigned int len,bool force_8bit); +unsigned int offset_in_page,const void *buf, +unsigned int len); +int nand_change_write_column_op (struct nand_chip *chip, +unsigned int offset_in_page,const void *buf, +unsigned int len,bool force_8bit);int nand_read_data_op (struct nand_chip *chip,void *buf,unsigned int len, -bool force_8bits); +bool force_8bit);int nand_write_data_op (struct nand_chip *chip,const void *buf, -unsigned int len,bool force_8bits); +unsigned int len,bool force_8bit);/* Free resources held by the NAND device */ void nand_cleanup (struct nand_chip *chip);
Re: [RFC 04/12] mtd: nand: add ->exec_op() implementation
Hi Boris,
Quoted 11 lines
Quoted 4 lines
Introduce the new way to control the NAND controller drivers by implementing the ->exec_op() core helpers and allowing new drivers to use it instead of relying on ->cmd_ctrl(), ->cmdfunc() and ->read/write_byte/word/buf().
"
Introduce a new interface to instruct NAND controllers to send specific NAND operations. The new interface takes the form of a single method called ->exec_op(). This method is designed to replace ->cmd_ctrl(), ->cmdfunc() and ->read/write_byte/word/buf() hooks. "
Changed.
Quoted 5 lines
Quoted 4 lines
The logic is now to send to the controller driver a list of instructions.
Quoted 9 lines
"
->exec_op() is passed a set of instructions describing the operation to execute. Each instruction has a type (ADDR, CMD, CYCLE, WAITRDY) and delay. The type is directly matching the description of NAND commands in various NAND datasheet and standards (ONFI, JEDEC), the delay is here to help simple controllers wait enough time between each instruction. Advanced controllers with integrated timings control can ignore these delays.
"
Changed with s/CYCLE/DATA/.
Quoted 9 lines
Quoted 3 lines
The driver shall use the parser added by this commit to get the matching hook if any.
That's true only for advanced controllers. For those who are executing each instruction individually (like the gpio-nand driver, or any old style driver where each ADDR/CMD/DATA cycle is controlled independently) this is not needed, and who add extra complexity for no real gain.
Mention about advanced vs dump controllers added.
Quoted 8 lines
Quoted 4 lines
The instructions may be split by the
parser in order to comply with the controller constraints filled in an array of supported patterns.Given only this description it's hard to tell what this parser is and why it's needed. I think a real example who be helpful to better understand what this is.
Added an example.
Quoted 8 lines
Quoted 7 lines
Various helpers are also added to ease NAND controller drivers writing.
This new interface should really ease the support of new vendor specific instructions.
Quoted 6 lines
Actually, it's more than easing support for vendor specific operations, it allows the core to check whether the NAND controller will be able to execute a specific operation or not, which before that was impossible. It doesn't mean all controllers will magically support all kind of NAND operations, but at least we'll know when it doesn't.
Added a note about that.
Quoted 13 lines
Quoted 13 lines
Suggested-by Boris Brezillon <[email protected]>Signed-off-by Miquel Raynal <[email protected]>--- drivers/mtd/nand/denali.c | 93 +++- drivers/mtd/nand/nand_base.c | 949 +++++++++++++++++++++++++++++++++++++++-- drivers/mtd/nand/nand_hynix.c | 91 +++- drivers/mtd/nand/nand_micron.c | 32 +- include/linux/mtd/rawnand.h | 366 +++++++++++++++- 5 fileschanged, 1448 insertions(+), 83 deletions(-)
...
Quoted 24 lines
Quoted 24 lines
drivers/mtd/nand/denali.c +12
-5@@ -665,11 +663,22 @@ static void denali_oob_xfer(struct mtd_info *mtd, struct nand_chip *chip, int i, pos, len;/* BBM at the beginning of the OOB area */ - chip->cmdfunc (mtd, start_cmd, writesize, page); -if (write) - chip->write_buf (mtd, bufpoi, oob_skip); -else - chip->read_buf (mtd, bufpoi, oob_skip); +if (chip->exec_op) { +if (write) +nand_prog_page_begin_op (chip, page, writesize, bufpoi, + oob_skip); +else +nand_read_page_op (chip, page, writesize, bufpoi, + oob_skip); + }else { +if (write) { + chip->cmdfunc (mtd,NAND_CMD_SEQIN , writesize, page); + chip->write_buf (mtd, bufpoi, oob_skip); + }else {
Quoted 8 lines
drivers/mtd/nand/denali.c static void denali_oob_xfer(struct mtd_info *mtd, struct nand_chip *chip, · line 681
Quoted 5 lines
(quoted diff) +1
-0+ chip->cmdfunc(mtd, NAND_CMD_READ0,writesize, page);
drivers/mtd/nand/denali.c +3
-0+ chip->read_buf (mtd, bufpoi, oob_skip); + } + }Why do we have to keep the ->cmdfunc() logic?
nand_prog_page_xxx()already abstracts this for us, right?
Right! Changed here and after.
Quoted 27 lines
Quoted 14 lines
drivers/mtd/nand/denali.c +0
-6bufpoi += oob_skip;/* OOB ECC */ @@ -682,30 +691,67 @@ static void denali_oob_xfer(struct mtd_info *mtd, struct nand_chip *chip, else if (pos + len > writesize) len = writesize - pos; - chip->cmdfunc (mtd, rnd_cmd, pos,-1 ); -if (write) - chip->write_buf (mtd, bufpoi, len); -else - chip->read_buf (mtd, bufpoi, len); - bufpoi += len;drivers/mtd/nand/denali.c static void denali_oob_xfer(struct mtd_info · line 699
Quoted 10 lines
drivers/mtd/nand/denali.c +3
-4-if (len < ecc_bytes) { - len = ecc_bytes - len; - chip->cmdfunc (mtd, rnd_cmd, writesize + oob_skip, -1); +if (chip->exec_op) {if (write) - chip->write_buf (mtd, bufpoi, len); +nand_change_write_column_op ( + chip, pos, bufpoi, len, false);Nitpick, but can you try to align things like that:
Quoted 3 lines
nand_change_write_column_op(chip, pos,
bufpoi,
len, false);
Changed here and after.
Quoted 97 lines
drivers/mtd/nand/denali.c static void denali_oob_xfer(struct mtd_info *mtd, struct nand_chip *chip,
Quoted 5 lines
drivers/mtd/nand/denali.c +2
-0else +nand_change_read_column_op ( + chip, pos, bufpoi, len, false);Ditto.
drivers/mtd/nand/denali.c static void denali_oob_xfer(struct mtd_info *mtd, struct nand_chip *chip, · line 732
Quoted 10 lines
drivers/mtd/nand/denali.c +8
-0+ }else { +if (write) { + chip->cmdfunc (mtd,NAND_CMD_RNDIN , pos,-1 ); + chip->write_buf (mtd, bufpoi, len); + }else { + chip->cmdfunc (mtd,NAND_CMD_RNDOUT , pos,-1 ); chip->read_buf (mtd, bufpoi, len); + } + }Smae here: I don't think we need to support both ->cmdfunc() and nand_change_read/write_column().
Quoted 32 lines
drivers/mtd/nand/denali.c +6
-0+ bufpoi += len; +if (len < ecc_bytes) { + len = ecc_bytes - len; +if (chip->exec_op) { +if (write) +nand_change_write_column_op(
(quoted diff) +1
-0+ chip, writesize +
oob_skip,(quoted diff) +1
-0+ bufpoi, len,false);
drivers/mtd/nand/denali.c +3
-0+else + nand_change_read_column_op( + chip, writesize +
oob_skip,(quoted diff) +1
-0+ bufpoi, len,false);
drivers/mtd/nand/denali.c +3
-0+ }else { +if (write) { + chip->cmdfunc(mtd,
NAND_CMD_RNDIN,(quoted diff) +1
-0+ writesize +
oob_skip, -1);(quoted diff) +1
-0+ chip->write_buf(mtd,bufpoi, len);
(quoted diff) +2
-0+ } else { + chip->cmdfunc(mtd,
NAND_CMD_RNDOUT,(quoted diff) +1
-0+ writesize +
oob_skip, -1);(quoted diff) +1
-0+ chip->read_buf(mtd,bufpoi, len);
Review
Quoted 2 lines
(quoted diff) +2
-0+ } + .Ditto.
Quoted 38 lines
drivers/mtd/nand/denali.c +3
-5bufpoi += len; } }/* OOB free */ len = oobsize - (bufpoi - chip->oob_poi); - chip->cmdfunc (mtd, rnd_cmd, size - len,-1 ); -if (write) - chip->write_buf (mtd, bufpoi, len); -else - chip->read_buf (mtd, bufpoi, len); +if (chip->exec_op) { +if (write) + nand_change_write_column_op(chip, size -len, bufpoi,
drivers/mtd/nand/denali.c +3
-0+ len, false); + else + nand_change_read_column_op(chip, size -len, bufpoi,
drivers/mtd/nand/denali.c +4
-0+ len, false); + }else { +if (write) { + chip->cmdfunc(mtd, NAND_CMD_RNDIN, size -len, -1);
drivers/mtd/nand/denali.c +3
-0+ chip->write_buf (mtd, bufpoi, len); + }else { + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, size -len, -1);
drivers/mtd/nand/denali.c +3
-0+ chip->read_buf (mtd, bufpoi, len); + } + } } static int denali_read_page_raw(struct mtd_info *mtd, struct
nand_chip*chip, @@ -803,6 +849,9 @@ static int denali_write_oob(structmtd_info*mtd, structnand_chip*chip,
denali_oob_xfer(mtd, chip, page, 1);
Quoted 7 lines
drivers/mtd/nand/denali.c static int denali_write_oob(struct mtd_info *mtd, struct nand_chip *chip, · line 856
Quoted 5 lines
drivers/mtd/nand/denali.c +3
-0+if (chip->exec_op) +return nand_prog_page_end_op (chip); + chip->cmdfunc (mtd,NAND_CMD_PAGEPROG ,-1 ,-1 ); status = chip->waitfunc (mtd, chip);All denali changes in this patch should actually be moved to patch 1.
Ok.
Quoted 17 lines
Quoted 10 lines
(quoted diff) +0
-0
b/drivers/mtd/nand/nand_base.cindexbef20e06f0db..737f19bd2f83100644 ---a/drivers/mtd/nand/nand_base.c
+++b/drivers/mtd/nand/nand_base.c(quoted diff) +0
-0@@ -814,7 +814,7 @@ static void nand_ccs_delay(struct nand_chip *chip) * Wait tCCS_min if it is correctly defined, otherwisewait 500ns
drivers/mtd/nand/nand_base.c static void nand_ccs_delay(struct nand_chip *chip) · line 817
Quoted 4 lines
drivers/mtd/nand/nand_base.c +1
-1* (which should be safe for all NANDs). */ -if (chip->data_interface.timings.sdr.tCCS_min) +if (&chip->data_interface.timings.sdr.tCCS_min)This condition is always true. I think this should be replaced by:
Quoted 1 line
if (chip->setup_data_interface)
Yes.
Quoted 2 lines
And BTW, it should go in patch 3.
Ok.
Quoted 82 lines
Quoted 78 lines
ndelay(chip->data_interface.timings.sdr.tCCS_min /
1000); elsedrivers/mtd/nand/nand_base.c +1
-0ndelay (500 );@@ -1233,6 +1233,118 @@ static int nand_init_data_interface(struct nand_chip *chip) }/** +* nand_fill_column_cycles - fill the column fields on an address array
+ * @chip: The NAND chip
+ * @addrs: Array of address cycles to fill
+ * @offset_in_page: The offset in the page
+ *
+ * Fills the first or the two first bytes of the @addrs field depending
+ * on the NAND bus width and the page size.
+ */drivers/mtd/nand/nand_base.c +7
-0+int nand_fill_column_cycles (struct nand_chip *chip,u8 *addrs, +unsigned int offset_in_page) +{ +struct mtd_info *mtd =nand_to_mtd (chip); + +/* +* The offset in page is expressed in bytes, if the NAND bus is 16-bit
drivers/mtd/nand/nand_base.c +14
-0+* wide, then it must be divided by 2. +*/ +if (chip->options &NAND_BUSWIDTH_16 ) { +if (offset_in_page %2 ) { +WARN_ON (true); +return -EINVAL ; + } + + offset_in_page /=2 ; + } + + addrs[0 ] = offset_in_page; + +/* Small pages use 1 cycle for the columns, while large page need 2 */
drivers/mtd/nand/nand_base.c +10
-0+if (mtd->writesize <=512 ) +return 1 ; + + addrs[1 ] = offset_in_page >>8 ; + +return 2 ; +} +EXPORT_SYMBOL_GPL (nand_fill_column_cycles); + +static int nand_sp_exec_read_page_op(struct nand_chip *chip,unsigned int page,
drivers/mtd/nand/nand_base.c +1
-0+ unsigned int offset_in_page,void *buf,
drivers/mtd/nand/nand_base.c +24
-0+ unsigned int len) +{ +struct mtd_info *mtd =nand_to_mtd (chip); +const struct nand_sdr_timings *sdr = +nand_get_sdr_timings (&chip->data_interface); +u8 addrs[4 ]; +struct nand_op_instr instrs[] = { +NAND_OP_CMD (NAND_CMD_READ0 ,0 ), +NAND_OP_ADDR (3 , addrs, sdr->tWB_max), +NAND_OP_WAIT_RDY (sdr->tR_max, sdr->tRR_min), +NAND_OP_DATA_IN (len, buf,0 ), + }; +struct nand_operation op =NAND_OPERATION (instrs); + +/* Drop the DATA_OUT instruction if len is set to 0. */ +if (!len) + op.ninstrs--; + +if (offset_in_page >= mtd->writesize) + instrs[0 ].cmd.opcode =NAND_CMD_READOOB ; +else if (offset_in_page >=256 ) + instrs[0 ].cmd.opcode =NAND_CMD_READ1 ; + + if (nand_fill_column_cycles(chip, addrs, offset_in_page) <Quoted 1 line
0)
Why not returning the retcode of
nand_fill_column_cycles()directly?
Quoted 3 lines
ret = nand_fill_column_cycles(chip, addrs, offset_in_page) if (ret < 0) return ret;
Changed.
Quoted 40 lines
Quoted 27 lines
drivers/mtd/nand/nand_base.c +14
-0+return -EINVAL ; + + addrs[1 ] = page; + addrs[2 ] = page >>8 ; + +if (chip->options &NAND_ROW_ADDR_3 ) { + addrs[3 ] = page >>16 ; + instrs[1 ].addr.naddrs++; + } + +return nand_exec_op (chip, &op); +} + +static int nand_lp_exec_read_page_op(struct nand_chip *chip,unsigned int page,
drivers/mtd/nand/nand_base.c +1
-0+ unsigned int offset_in_page,void *buf,
drivers/mtd/nand/nand_base.c +9
-0+ unsigned int len) +{ +const struct nand_sdr_timings *sdr = +nand_get_sdr_timings (&chip->data_interface); +u8 addrs[5 ]; +struct nand_op_instr instrs[] = { +NAND_OP_CMD (NAND_CMD_READ0 ,0 ), +NAND_OP_ADDR (4 , addrs,0 ), +NAND_OP_CMD (NAND_CMD_READSTART , sdr->tWB_max),drivers/mtd/nand/nand_base.c static int nand_init_data_interface(struct nand_chip *chip) · line 1333
Quoted 10 lines
drivers/mtd/nand/nand_base.c +10
-0+NAND_OP_WAIT_RDY (sdr->tR_max, sdr->tRR_min), + NAND_OP_DATA_IN(len, buf, 0), + }; +struct nand_operation op =NAND_OPERATION (instrs); + +/* Drop the DATA_IN instruction if len is set to 0. */ +if (!len) + op.ninstrs--; + +if (nand_fill_column_cycles (chip, addrs, offset_in_page))Should be
Quoted 1 line
if (
nand_fill_column_cycles(chip, addrs, offset_in_page)< 0)
+ use of ret =, if (ret < 0)...
Quoted 22 lines
Quoted 22 lines
drivers/mtd/nand/nand_base.c +14
-0+return -EINVAL ; + + addrs[2 ] = page; + addrs[3 ] = page >>8 ; + +if (chip->options &NAND_ROW_ADDR_3 ) { + addrs[4 ] = page >>16 ; + instrs[1 ].addr.naddrs++; + } + +return nand_exec_op (chip, &op); +} + +/** * nand_read_page_op - Do a READ PAGE operation * @chip: The NAND chip * @page: page to read @@ -1246,17 +1358,26 @@ static int nand_init_data_interface(structnand_chip *chip) * Returns 0 for success or negative error code otherwise */
Quoted 8 lines
Review static int nand_init_data_interface(struct · line 1364
Quoted 5 lines
(quoted diff) +1
-1int nand_read_page_op(struct nand_chip *chip, unsigned int page, - unsigned int column, void *buf, unsigned int len) + unsigned int offset_in_page, void *buf, unsigned int len)You should change the parameter name in patch 1 since this function is introduced there.
Ok, done in other places as well.
Quoted 80 lines
Quoted 55 lines
drivers/mtd/nand/nand_base.c +5
-2{struct mtd_info *mtd =nand_to_mtd (chip);if (len && !buf)return -EINVAL ; -if (column + len > mtd->writesize + mtd->oobsize) +if (offset_in_page + len > mtd->writesize + mtd->oobsize)return -EINVAL ; - chip->cmdfunc (mtd,NAND_CMD_READ0 , column, page); +if (chip->exec_op) { +if (mtd->writesize >512 ) + return nand_lp_exec_read_page_op( + chip, page, offset_in_page, buf,len); +
(quoted diff) +1
-0+ return nand_sp_exec_read_page_op(chip, page,
offset_in_page,drivers/mtd/nand/nand_base.c +17
-0+ buf, len); + } + + chip->cmdfunc (mtd,NAND_CMD_READ0 , offset_in_page, page);if (len) chip->read_buf (mtd, buf, len);@@ -1286,6 +1407,25 @@ static int nand_read_param_page_op(struct nand_chip *chip, u8 page, void *buf, if (len && !buf) return -EINVAL; +if (chip->exec_op) { +const struct nand_sdr_timings *sdr = +nand_get_sdr_timings (&chip->data_interface); +struct nand_op_instr instrs[] = { +NAND_OP_CMD (NAND_CMD_PARAM ,0 ), +NAND_OP_ADDR (1 , &page, sdr->tWB_max), +NAND_OP_WAIT_RDY (sdr->tR_max, sdr->tRR_min), +NAND_OP_8BIT_DATA_IN (len, buf,0 ), + }; +struct nand_operation op = +NAND_OPERATION (instrs); + +/* Drop the DATA_IN instruction if len is set to 0. */
drivers/mtd/nand/nand_base.c +6
-0+if (!len) + op.ninstrs--; + +return nand_exec_op (chip, &op); + } + chip->cmdfunc (mtd,NAND_CMD_PARAM , page,-1 );for (i =0 ; i < len; i++) p[i] = chip->read_byte (mtd);[...]
Quoted 22 lines
drivers/mtd/nand/nand_base.c +2
-1+/** * nand_prog_page_begin_op - starts a PROG PAGE operation * @chip: The NAND chip @@ -1371,7 +1608,7 @@ EXPORT_SYMBOL_GPL(nand_read_oob_op);* Returns 0 for success or negative error code otherwise */ int nand_prog_page_begin_op (struct nand_chip *chip,unsigned int page, -unsigned int column,const void *buf, +unsigned int offset_in_page,const void *buf,unsigned int len) {drivers/mtd/nand/nand_base.c +1
-1struct mtd_info *mtd =nand_to_mtd (chip);@@ -1379,10 +1616,14 @@ int nand_prog_page_begin_op(struct nand_chip *chip, unsigned int page, if (len && !buf) return -EINVAL; -if (column + len > mtd->writesize + mtd->oobsize) +if (offset_in_page + len > mtd->writesize + mtd->oobsize)return -EINVAL ;
Quoted 7 lines
drivers/mtd/nand/nand_base.c int nand_prog_page_begin_op(struct nand_chip *chip, unsigned int page,
Quoted 5 lines
drivers/mtd/nand/nand_base.c +3
-1- chip->cmdfunc (mtd,NAND_CMD_SEQIN , column, page); +if (chip->exec_op) +return nand_exec_prog_page_op ( + chip, page, offset_in_page, buf, len, false);Param alignment please.
Done
Quoted 28 lines
Quoted 28 lines
drivers/mtd/nand/nand_base.c +2
-0+ + chip->cmdfunc (mtd,NAND_CMD_SEQIN , offset_in_page, page);if (buf)drivers/mtd/nand/nand_base.c +13
-0chip->write_buf (mtd, buf, len);@@ -1405,6 +1646,19 @@ int nand_prog_page_end_op(struct nand_chip *chip) struct mtd_info *mtd = nand_to_mtd(chip);int status; +if (chip->exec_op) { +const struct nand_sdr_timings *sdr = +nand_get_sdr_timings (&chip->data_interface); +struct nand_op_instr instrs[] = { +NAND_OP_CMD (NAND_CMD_PAGEPROG , sdr->tWB_max), +NAND_OP_WAIT_RDY (sdr->tPROG_max,0 ), + }; +struct nand_operation op = +NAND_OPERATION (instrs); + +return nand_exec_op (chip, &op); + } + chip->cmdfunc (mtd,NAND_CMD_PAGEPROG ,-1 ,-1 );status =
chip->waitfunc(mtd, chip);
[...]
Quoted 17 lines
Quoted 8 lines
+/**
+ *nand_op_parser_must_split_instr- Checks if an instruction must be split
+ * @pat: the parser pattern that match
+ * @instr: the instruction array to check
+ * @start_offset: the offset from which to start in the first instruction of the
+ * @instr arrayQuoted 8 lines
+ *
+ * Some NAND controllers are limited and cannot send X address cycles with a
+ * unique operation, or cannot read/write more than Y bytes at the same time.
+ * In this case, reduce the scope of this set of operation, and trigger another
+ * instruction with the rest.
Quoted 4 lines
"
In this case, split the instruction that does not fit in a single controller-operation into two or more chunks.
"
Ok.
Quoted 6 lines
Quoted 4 lines
+ *
+ * Returns true if the array of instruction must be split, false otherwise.s/array of//
Right.
Quoted 10 lines
Quoted 5 lines
+ * The @start_offset parameter is also updated to the offset at which the first
+ * instruction of the next bundle of instruction must start (if an address or a"
The @start_offset parameter is also updated to the offset at which the the next bundle of instructions must start ...
"
Ok.
Quoted 9 lines
Quoted 9 lines
+ * data instruction).
+ */(quoted diff) +2
-0+static bool +nand_op_parser_must_split_instr(const struct
nand_op_parser_pattern_elem*pat,drivers/mtd/nand/nand_base.c +3
-0+const struct nand_op_instr *instr, + unsigned int *start_offset) +{
[...]
Quoted 17 lines
Quoted 17 lines
(quoted diff) +2
-0+#if IS_ENABLED(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG) +static void nand_op_parser_trace(const struct nand_op_parser_ctx*ctx,
drivers/mtd/nand/nand_base.c +14
-0+ bool success) +{ +const struct nand_op_instr *instr; +bool in_subop = false; +char *is_in =" ->" , *is_out =" " , *prefix; +char *buf; +unsigned int len, off =0 ; +int i, j; + +if (success) +pr_debug ("executing subop:\n" ); +else +pr_debug ("pattern not found:\n" ); +
Quoted 8 lines
Quoted 6 lines
drivers/mtd/nand/nand_base.c +5
-0+for (i =0 ; i < ctx->ninstrs; i++) { + instr = &ctx->instrs[i]; + +/* +* ctx->instr_idx is not reliable because it may already had
have
Corrected.
Quoted 10 lines
drivers/mtd/nand/nand_base.c int nand_write_data_op(struct nand_chip *chip, const void *buf, · line 2327
Quoted 6 lines
(quoted diff) +1
-0+ * been updated by the parser. Use pointerscomparison instead.
drivers/mtd/nand/nand_base.c +3
-0+*/ +if (instr == &ctx->subop.instrs[0 ]) + in_subop = true;It seems
in_subopis only used to select the prefix, so you can just get rid of it and do
Quoted 2 lines
if (instr == &ctx->subop.instrs[0]) prefix = " ->";
Ok, changed.
Quoted 3 lines
BTW, if success is false, you should not even try to change the prefix, right?
I do not agree, there is no point in not showing what bundle of instructions failed!
Quoted 135 lines
Quoted 48 lines
drivers/mtd/nand/nand_base.c +14
-0+ +if (in_subop) + prefix = is_in; +else + prefix = is_out; + +switch (instr->type) { +case NAND_OP_CMD_INSTR : +pr_debug ("%sCMD [0x%02x]\n" , prefix, + instr->cmd.opcode); +break ; +case NAND_OP_ADDR_INSTR : +/* +* A log line is much less than 50 bytes, plus 5 bytes
drivers/mtd/nand/nand_base.c +8
-0+* per address cycle to display. +*/ + len =50 +5 * instr->addr.naddrs; + buf =kmalloc (len,GFP_KERNEL ); +if (!buf) +return ; +memset (buf,0 , len); + off += snprintf(buf, len, "ADDR [%dcyc:",
drivers/mtd/nand/nand_base.c +3
-0+ instr->addr.naddrs); +for (j =0 ; j < instr->addr.naddrs; j++) + off += snprintf(&buf[off], len -off, " 0x%02x",
+
instr->addr.addrs[j]);drivers/mtd/nand/nand_base.c +6
-0+pr_debug ("%s%s]\n" , prefix, buf); +break ; +case NAND_OP_DATA_IN_INSTR : + pr_debug("%sDATA_IN [%d B%s]\n", prefix, + instr->data.len, + instr->data.force_8bit ? ", force8-bit" : "");
drivers/mtd/nand/nand_base.c +5
-0+break ; +case NAND_OP_DATA_OUT_INSTR : + pr_debug("%sDATA_OUT [%d B%s]\n", prefix, + instr->data.len, + instr->data.force_8bit ? ", force8-bit" : "");
drivers/mtd/nand/nand_base.c +3
-0+break ; +case NAND_OP_WAITRDY_INSTR : + pr_debug("%sWAITRDY [max %d ms]\n",prefix,
drivers/mtd/nand/nand_base.c int nand_write_data_op(struct nand_chip *chip, const void *buf, · line 2373
Quoted 7 lines
drivers/mtd/nand/nand_base.c +6
-1+ instr->waitrdy.timeout_ms); +break ; + } + + if (instr == &ctx->subop.instrs[ctx->subop.ninstrs - 1]) + in_subop = false;if (instr == &ctx->subop.instrs[ctx->subop.ninstrs -1]) prefix = " ";
and initialize prefix to " " at the beginning of the function.
Quoted 52 lines
drivers/mtd/nand/nand_base.c +4
-0+ } +} +#else +static void nand_op_parser_trace(const struct nand_op_parser_ctx*ctx,
drivers/mtd/nand/nand_base.c +12
-0+ bool success) +{ +/* NOP */ +} +#endif + +/** +* nand_op_parser_exec_op - exec_op parser +* @chip: the NAND chip +* @parser: the parser to use given by the controller driver +* @op: the NAND operation to address +* @check_only: flag asking if the entire operation could be handled
+ *
+ * Function that must be called by each driver that implement the "exec_opAPI"
+ * in their own ->exec_op() implementation.
+ *
+ * The function iterates on all the instructions asked and make use of internal
+ * parsers to find matches between the instruction list and the handled patterns
+ * filled by the controller drivers inside the @parser structure. If needed, the
+ * instructions could be split into sub-operations and be executed sequentially.
+ */drivers/mtd/nand/nand_base.c +3
-0+int nand_op_parser_exec_op (struct nand_chip *chip, +const struct nand_op_parser *parser, + const struct nand_operation *op, bool
check_only) +{drivers/mtd/nand/nand_base.c +12
-0+struct nand_op_parser_ctx ctx = { + .instrs = op->instrs, + .ninstrs = op->ninstrs, + }; +unsigned int i; + +while (ctx.instr_idx < op->ninstrs) { +bool pattern_found = false; +int ret; + +for (i =0 ; i < parser->npatterns; i++) { + const struct nand_op_parser_pattern*pattern; +
(quoted diff) +2
-0+ pattern = &parser->patterns[i]; + if (!nand_op_parser_match_pat(pattern,&ctx))
drivers/mtd/nand/nand_base.c int nand_write_data_op(struct nand_chip *chip, const void *buf, · line 2421
Quoted 4 lines
drivers/mtd/nand/nand_base.c +4
-0+continue ; + +nand_op_parser_trace (&ctx, true); +
pattern_foundshould be set to true here. But I'm not even sure you really need this variable.drivers/mtd/nand/nand_base.c int nand_write_data_op(struct nand_chip *chip, const void *buf, · line 2432
Quoted 11 lines
drivers/mtd/nand/nand_base.c +11
-0+if (check_only) +break ; + + ret = pattern->exec (chip, &ctx.subop); +if (ret) +return ret; + + pattern_found = true; + } + +if (!pattern_found) {Just test
Quoted 3 lines
if (i == parser->npatterns) {and you should be good.
Indeed it works.
Quoted 13 lines
Quoted 13 lines
drivers/mtd/nand/nand_base.c +12
-0+nand_op_parser_trace (&ctx, false); +return -ENOTSUPP ; + } + } + +return 0 ; +} +EXPORT_SYMBOL_GPL (nand_op_parser_exec_op); + +static bool nand_instr_is_data (const struct nand_op_instr *instr) +{ + return instr && (instr->type == NAND_OP_DATA_IN_INSTR ||
Quoted 8 lines
Review
Quoted 6 lines
drivers/mtd/nand/nand_base.c +4
-0+ instr->type == NAND_OP_DATA_OUT_INSTR); +} + +static bool nand_subop_instr_is_valid(const struct nand_subop*subop,
(quoted diff) +1
-0+ unsigned int op_id)s/op_id/instr_idx/
Changed everywhere.
Quoted 45 lines
drivers/mtd/nand/nand_base.c int nand_write_data_op(struct nand_chip *chip, const void *buf, · line 2455
Quoted 7 lines
drivers/mtd/nand/nand_base.c +6
-0+{ +return subop && op_id < subop->ninstrs; +} + +static int nand_subop_get_start_off (const struct nand_subop *subop, +unsigned int op_id)Ditto.
Quoted 9 lines
drivers/mtd/nand/nand_base.c +9
-0+{ +if (op_id) +return 0 ; + +return subop->first_instr_start_off; +} + +/** +* nand_subop_get_addr_start_off - Get the start offset in an Quoted 3 lines
address array
+ * @subop: The entire sub-operation
+ * @op_id: Index of the instruction inside the sub-operation
instr_idx.Quoted 8 lines
+ *
+ * Instructions arrays may be split by the parser between instructions,
+ * and also in the middle of an address instruction if the number of cycles
+ * to assert in one operation is not supported by the controller.
+ *
+ * For this, instead of using the first index of the ->addr.addrsQuoted 8 lines
field from the
+ * address instruction, the NAND controller driver must use this helper that
+ * will either return 0 if the index does not point to the first instruction of
+ * the sub-operation, or the offset of the next starting offset inside the
+ * address cycles.I think you can drop this paragraph which IMO brings more confusion to what this function does.
Quoted 6 lines
Quoted 4 lines
+ *
+ * Returns the offset of the first address cycle to assert from the pointed
+ * address instruction.This part is definitely clearer.
Ok.
Quoted 77 lines
Quoted 15 lines
+ */
drivers/mtd/nand/nand_base.c +13
-0+int nand_subop_get_addr_start_off (const struct nand_subop *subop, +unsigned int op_id) +{ +if (!nand_subop_instr_is_valid (subop, op_id) || + subop->instrs[op_id].type !=NAND_OP_ADDR_INSTR ) +return -EINVAL ; + +return nand_subop_get_start_off (subop, op_id); +} +EXPORT_SYMBOL_GPL (nand_subop_get_addr_start_off); + +/** +* nand_subop_get_num_addr_cyc - Get the remaining address cycles Quoted 3 lines
to assert
+ * @subop: The entire sub-operation
+ * @op_id: Index of the instruction inside the sub-operation
instr_idxQuoted 56 lines
+ *
+ * Instructions arrays may be split by the parser between instructions,
+ * and also in the middle of an address instruction if the number of cycles
+ * to assert in one operation is not supported by the controller.
+ *
+ * For this, instead of using the ->addr.naddrs fields of the address
+ * instruction, the NAND controller driver must use this helper that will
+ * return the actual number of cycles to assert between the first and last
+ * offset asked for this particular instruction.
+ *
+ * Returns the number of address cycles to assert from the pointed address
+ * instruction.
+ */drivers/mtd/nand/nand_base.c +23
-0+int nand_subop_get_num_addr_cyc (const struct nand_subop *subop, +unsigned int op_id) +{ +int start_off, end_off; + +if (!nand_subop_instr_is_valid (subop, op_id) || + subop->instrs[op_id].type !=NAND_OP_ADDR_INSTR ) +return -EINVAL ; + + start_off =nand_subop_get_addr_start_off (subop, op_id); + +if (op_id == subop->ninstrs -1 && + subop->last_instr_end_off) + end_off = subop->last_instr_end_off; +else + end_off = subop->instrs[op_id].addr.naddrs; + +return end_off - start_off; +} +EXPORT_SYMBOL_GPL (nand_subop_get_num_addr_cyc); + +/** +* nand_subop_get_data_start_off - Get the start offset in a data array
+ * @subop: The entire sub-operation
+ * @op_id: Index of the instruction inside the sub-operation
+ *
+ * Instructions arrays may be split by the parser between instructions,
+ * and also in the middle of a data instruction if the number of bytes to access
+ * in one operation is greater that the controller limit.
+ *
+ * For this, instead of using the first index of the ->data.buf field from the
+ * data instruction, the NAND controller driver must use this helper that
Quoted 8 lines
Quoted 5 lines
+ * will either return 0 if the index does not point to the first instruction of
+ * the sub-operation, or the offset of the next starting offset inside the
+ * data buffer.Same as for
nand_subop_get_addr_start_off(), the explanation is confusing. I think we can drop it.
Dropped then.
Quoted 136 lines
Quoted 136 lines
+ *
+ * Returns the data offset inside the pointed data instruction buffer from which
+ * to start.
+ */drivers/mtd/nand/nand_base.c +17
-0+int nand_subop_get_data_start_off (const struct nand_subop *subop, +unsigned int op_id) +{ +if (!nand_subop_instr_is_valid (subop, op_id) || + !nand_instr_is_data (&subop->instrs[op_id])) +return -EINVAL ; + +return nand_subop_get_start_off (subop, op_id); +} +EXPORT_SYMBOL_GPL (nand_subop_get_data_start_off); + +/** +* nand_subop_get_data_len - Get the number of bytes to retrieve +* @subop: The entire sub-operation +* @op_id: Index of the instruction inside the sub-operation +* +* Instructions arrays may be split by the parser between instructions,
+ * and also in the middle of a data instruction if the number of bytes to access
+ * in one operation is greater that the controller limit.
+ *
+ * For this, instead of using the ->data.len field from the data instruction,
+ * the NAND controller driver must use this helper that will return the actual
+ * length of data to move between the first and last offset asked for this
+ * particular instruction.
+ *
+ * Returns the length of the data to move from the pointed data instruction.
+ */drivers/mtd/nand/nand_base.c +29
-6+int nand_subop_get_data_len (const struct nand_subop *subop, +unsigned int op_id) +{ +int start_off =0 , end_off; + +if (!nand_subop_instr_is_valid (subop, op_id) || + !nand_instr_is_data (&subop->instrs[op_id])) +return -EINVAL ; + + start_off =nand_subop_get_data_start_off (subop, op_id); + +if (op_id == subop->ninstrs -1 && + subop->last_instr_end_off) + end_off = subop->last_instr_end_off; +else + end_off = subop->instrs[op_id].data.len; + +return end_off - start_off; +} +EXPORT_SYMBOL_GPL (nand_subop_get_data_len); + +/** * nand_reset - Reset and initialize a NAND device * @chip: The NAND chip * @chipnr: Internal die id @@ -3940,7 +4815,7 @@ static void nand_set_defaults(struct nand_chip *chip) chip->chip_delay = 20;/* check, if a user supplied command function given */ -if (chip->cmdfunc ==NULL ) +if (chip->cmdfunc ==NULL && !chip->exec_op) chip->cmdfunc = nand_command;/* check, if a user supplied wait function given */ @@ -4823,15 +5698,35 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips, if (!mtd->name && mtd->dev.parent) mtd->name =dev_name (mtd->dev.parent); -if ((!chip->cmdfunc || !chip->select_chip) && !chip->cmd_ctrl) { +/* +* ->cmdfunc() is legacy and will only be used if ->exec_op() is not +* populated. +*/ +if (chip->exec_op) {/* -* Default functions assigned for chip_select() and -* cmdfunc() both expect cmd_ctrl() to be populated, -* so we need to check that that's the case +* The implementation of ->exec_op() heavily relies on timings
(quoted diff) +1
-0+ * to be accessible through the
nand_data_interfacestructure.(quoted diff) +1
-0+ * Thus, the ->setup_data_interface() hook must beprovided. The
(quoted diff) +1
-0+ * controller driver will be noticed of delays itmust apply
(quoted diff) +1
-0+ * after each ->exec_op() instruction (if any)through the
(quoted diff) +1
-0+ * .delay_ns field. The driver will then choose tohandle the
(quoted diff) +1
-0+ * delays manually if the controller cannot do itnatively. */
drivers/mtd/nand/nand_base.c +2
-2-pr_err ("chip.cmd_ctrl() callback is not provided" ); -return -EINVAL ; +if (!chip->setup_data_interface) { + pr_err("->setup_data_interface() should beprovided\n");
drivers/mtd/nand/nand_base.c +6
-0+return -EINVAL ; + } + }else { +/* +* Default functions assigned for ->cmdfunc() and +* ->select_chip() both expect ->cmd_ctrl() to be populated.
(quoted diff) +2
-0+ */ + if ((!chip->cmdfunc || !chip->select_chip)&& !chip->cmd_ctrl) {
(quoted diff) +1
-0+ pr_err("->cmd_ctrl() should beprovided\n");
drivers/mtd/nand/nand_base.c +3
-0+return -EINVAL ; + } } +/* Set the default functions */ nand_set_defaults (chip);(quoted diff) +0
-0
b/drivers/mtd/nand/nand_hynix.cindex04e3ab7a476c..81c382f24513100644 ---a/drivers/mtd/nand/nand_hynix.c
+++b/drivers/mtd/nand/nand_hynix.c(quoted diff) +0
-0@@ -74,19 +74,36 @@ static bool hynix_nand_has_valid_jedecid(struct nand_chip *chip) return !strncmp("JEDEC", jedecid, sizeof(jedecid)); }
Quoted 3 lines
drivers/mtd/nand/nand_hynix.c static bool hynix_nand_has_valid_jedecid(struct nand_chip *chip) · line 77
Quoted 1 line
drivers/mtd/nand/nand_hynix.c +1
-0+static int hynix_nand_cmd_op (struct nand_chip *chip,u8 cmd)Maybe you can introduce the helper in patch 1
Done.
Quoted 13 lines
drivers/mtd/nand/nand_hynix.c static bool hynix_nand_has_valid_jedecid(struct nand_chip *chip) · line 87
Quoted 11 lines
drivers/mtd/nand/nand_hynix.c +10
-0+{ +struct mtd_info *mtd =nand_to_mtd (chip); + +if (chip->exec_op) { +struct nand_op_instr instrs[] = { +NAND_OP_CMD (cmd,0 ), + }; +struct nand_operation op =NAND_OPERATION (instrs); + +return nand_exec_op (chip, &op);And then ass this block of code in this patch.
Sure.
Quoted 23 lines
Quoted 23 lines
drivers/mtd/nand/nand_hynix.c +8
-2+ } + + chip->cmdfunc (mtd, cmd,-1 ,-1 ); + +return 0 ; +} +static int hynix_nand_setup_read_retry (struct mtd_info *mtd,int retry_mode) {struct nand_chip *chip =mtd_to_nand (mtd);struct hynix_nand *hynix =nand_get_manufacturer_data (chip);const u8 *values; -int status;int i; values = hynix->read_retry->values + (retry_mode * hynix->read_retry->nregs);/* Enter 'Set Hynix Parameters' mode */ - chip->cmdfunc (mtd,NAND_HYNIX_CMD_SET_PARAMS ,-1 ,-1 ); +hynix_nand_cmd_op (chip,NAND_HYNIX_CMD_SET_PARAMS );
Quoted 16 lines
Quoted 11 lines
drivers/mtd/nand/nand_hynix.c +0
-0/* * Configure the NAND in the requested read-retry mode. @@ -101,16 +118,17 @@ static int hynix_nand_setup_read_retry(struct
mtd_info*mtd, intretry_mode) int column =
hynix->read_retry->regs[i];drivers/mtd/nand/nand_hynix.c +3
-2column |= column <<8 ; - chip->cmdfunc (mtd,NAND_CMD_NONE , column,-1 ); - chip->write_byte (mtd, values[i]); +if (chip->exec_op) { + nand_change_write_column_op(chip, column, + &values[i], 1,Quoted 1 line
true);
This is not a
nand_change_write_column_op()op, here the cmd cycle is set toNAND_CMD_NONE. You should have your own operation defined to handle this sequence.
That is right.
However, this is not handled correctly in nand_command_lp(), I will send a patch very soon to fix it.
Quoted 8 lines
Quoted 8 lines
drivers/mtd/nand/nand_hynix.c +4
-1+ }else { + chip->cmdfunc (mtd,NAND_CMD_NONE , column, -1); + chip->write_byte(mtd, values[i]); + } }
Quoted 10 lines
drivers/mtd/nand/nand_hynix.c static int hynix_nand_setup_read_retry(struct mtd_info *mtd, int retry_mode) · line 131
Quoted 7 lines
drivers/mtd/nand/nand_hynix.c +1
-5/* Apply the new settings. */ - chip->cmdfunc (mtd,NAND_HYNIX_CMD_APPLY_PARAMS ,-1 ,-1 ); - - status = chip->waitfunc (mtd, chip); -if (status &NAND_STATUS_FAIL ) -return -EIO ; +hynix_nand_cmd_op (chip,NAND_HYNIX_CMD_APPLY_PARAMS );No, it's wrong, you miss the WAITRDY instruction to be compatible with what was done before.
Re-added.
Quoted 46 lines
Quoted 16 lines
drivers/mtd/nand/nand_hynix.c +1
-2return 0 ; }@@ -173,32 +191,65 @@ static int hynix_read_rr_otp(struct nand_chip *chip,nand_reset_op (chip); - chip->cmdfunc (mtd,NAND_HYNIX_CMD_SET_PARAMS ,-1 ,-1 ); +hynix_nand_cmd_op (chip,NAND_HYNIX_CMD_SET_PARAMS );for (i =0 ; i < info->nregs; i++) {int column = info->regs[i]; column |= column <<8 ; - chip->cmdfunc (mtd,NAND_CMD_NONE , column,-1 );drivers/mtd/nand/nand_hynix.c static int hynix_read_rr_otp(struct nand_chip · line 207
Quoted 10 lines
drivers/mtd/nand/nand_hynix.c +7
-2- chip->write_byte (mtd, info->values[i]); +if (chip->exec_op) { +nand_change_write_column_op (chip, column, + &info->values[i],1 , true); + }else { + chip->cmdfunc (mtd,NAND_CMD_NONE , column, -1); + chip->write_byte(mtd, info->values[i]); + }Same comments apply here.
Quoted 17 lines
drivers/mtd/nand/nand_hynix.c +9
-4} - chip->cmdfunc (mtd,NAND_HYNIX_CMD_APPLY_PARAMS ,-1 ,-1 ); +hynix_nand_cmd_op (chip,NAND_HYNIX_CMD_APPLY_PARAMS );/* Sequence to enter OTP mode? */ - chip->cmdfunc (mtd,0x17 ,-1 ,-1 ); - chip->cmdfunc (mtd,0x04 ,-1 ,-1 ); - chip->cmdfunc (mtd,0x19 ,-1 ,-1 ); +if (chip->exec_op) { +struct nand_op_instr instrs[] = { +NAND_OP_CMD (0x17 ,0 ), +NAND_OP_CMD (0x04 ,0 ), +NAND_OP_CMD (0x19 ,0 ), + }; +struct nand_operation op =NAND_OPERATION (instrs); +
Quoted 8 lines
drivers/mtd/nand/nand_hynix.c static int hynix_read_rr_otp(struct nand_chip *chip, · line 225
Quoted 6 lines
drivers/mtd/nand/nand_hynix.c +6
-0+nand_exec_op (chip, &op); + }else { + chip->cmdfunc (mtd,0x17 ,-1 ,-1 ); + chip->cmdfunc (mtd,0x04 ,-1 ,-1 ); + chip->cmdfunc (mtd,0x19 ,-1 ,-1 ); + }Why not creating a
hynix_nand_enter_otp_mode_op()for that?
I think this file needs some kind of rework and may really benefit from ->exec_op() new API, but this series is maybe not the best place to do it. Let's keep it this way for now.
Quoted 56 lines
Quoted 31 lines
drivers/mtd/nand/nand_hynix.c +17
-4/* Now read the page */ nand_read_page_op (chip, info->page,0 , buf, info->size);/* Put everything back to normal */ nand_reset_op (chip); - chip->cmdfunc (mtd,NAND_HYNIX_CMD_SET_PARAMS ,0x38 ,-1 ); - chip->write_byte (mtd,0x0 ); - chip->cmdfunc (mtd,NAND_HYNIX_CMD_APPLY_PARAMS ,-1 ,-1 ); - chip->cmdfunc (mtd,NAND_CMD_READ0 ,0x0 ,-1 ); +if (chip->exec_op) { +const struct nand_sdr_timings *sdr = +nand_get_sdr_timings (&chip->data_interface); +u8 byte =0 ; +u8 addr =0x38 ; +struct nand_op_instr instrs[] = { +NAND_OP_CMD (NAND_HYNIX_CMD_SET_PARAMS ,0 ), +NAND_OP_ADDR (1 , &addr, sdr->tCCS_min), +NAND_OP_8BIT_DATA_OUT (1 , &byte,0 ), +NAND_OP_CMD (NAND_HYNIX_CMD_APPLY_PARAMS ,0 ), +NAND_OP_CMD (NAND_CMD_READ0 ,0 ), + }; +struct nand_operation op =NAND_OPERATION (instrs); + +nand_exec_op (chip, &op); + }else { + chip->cmdfunc(mtd, NAND_HYNIX_CMD_SET_PARAMS,
0x38, -1);drivers/mtd/nand/nand_hynix.c static int hynix_read_rr_otp(struct nand_chip *chip, · line 252
Quoted 5 lines
drivers/mtd/nand/nand_hynix.c +4
-1+ chip->write_byte (mtd,0x0 ); + chip->cmdfunc (mtd,NAND_HYNIX_CMD_APPLY_PARAMS , -1, -1); + chip->cmdfunc(mtd, NAND_CMD_READ0, 0x0, -1); + }Same here.
Quoted 17 lines
(quoted diff) +0
-0return 0; }(quoted diff) +0
-0
b/drivers/mtd/nand/nand_micron.cindex543352380ffa..109d8003e33d100644 ---a/drivers/mtd/nand/nand_micron.c
+++b/drivers/mtd/nand/nand_micron.c(quoted diff) +0
-0@@ -117,14 +117,38 @@ micron_nand_read_page_on_die_ecc(struct
mtd_info*mtd, structnand_chip*chip,uint8_t*buf, intoob_required, int page)
{drivers/mtd/nand/nand_micron.c +1
-1-int status; +u8 status;int max_bitflips =0 ;micron_nand_on_die_ecc_setup (chip, true);
Quoted 16 lines
Quoted 4 lines
drivers/mtd/nand/nand_micron.c +1
-3- chip->cmdfunc (mtd,NAND_CMD_READ0 ,0x00 , page); - chip->cmdfunc (mtd,NAND_CMD_STATUS ,-1 ,-1 ); - status = chip->read_byte (mtd); +if (chip->exec_op) {drivers/mtd/nand/nand_micron.c micron_nand_read_page_on_die_ecc(struct mtd_info *mtd, struct nand_chip *chip, · line 135
Quoted 10 lines
drivers/mtd/nand/nand_micron.c +10
-0+u8 addrs[5 ] = {}; +struct nand_op_instr instrs[] = { +NAND_OP_CMD (NAND_CMD_READ0 ,0 ), +NAND_OP_ADDR (4 , addrs,0 ), +NAND_OP_CMD (NAND_CMD_STATUS ,0 ), +NAND_OP_8BIT_DATA_IN (1 , &status,0 ), + }; +struct nand_operation op =NAND_OPERATION (instrs); + +if (nand_fill_column_cycles (chip, addrs,0 ))if (
nand_fill_column_cycles(chip, addrs, 0)< 0)
Yes.
Quoted 73 lines
Quoted 73 lines
drivers/mtd/nand/nand_micron.c +16
-0+return -EINVAL ; + + addrs[2 ] = page; + addrs[3 ] = page >>8 ; +if (chip->options &NAND_ROW_ADDR_3 ) { + addrs[4 ] = page >>16 ; + instrs[1 ].addr.naddrs++; + } + +nand_exec_op (chip, &op); + }else { + chip->cmdfunc (mtd,NAND_CMD_READ0 ,0x00 , page); + chip->cmdfunc (mtd,NAND_CMD_STATUS ,-1 ,-1 ); + status = chip->read_byte (mtd); + } +if (status &NAND_STATUS_FAIL ) mtd->ecc_stats.failed++;/* (quoted diff) +0
-0
b/include/linux/mtd/rawnand.hindex1acc26ed0e91..302f231df65e100644 ---a/include/linux/mtd/rawnand.h
+++b/include/linux/mtd/rawnand.hinclude/linux/mtd/rawnand.h +25
-0@@ -751,6 +751,337 @@ struct nand_manufacturer_ops { };/** +* struct nand_op_cmd_instr - Definition of a command instruction +* @opcode: the command to assert in one cycle +*/ +struct nand_op_cmd_instr { +u8 opcode; +}; + +/** +* struct nand_op_addr_instr - Definition of an address instruction +* @naddrs: length of the @addrs array +* @addrs: array containing the address cycles to assert +*/ +struct nand_op_addr_instr { +unsigned int naddrs; +const u8 *addrs; +}; + +/** +* struct nand_op_data_instr - Definition of a data instruction +* @len: number of data bytes to move +* @in: buffer to fill when reading from the NAND chip +* @out: buffer to read from when writing to the NAND chip +* @force_8bit: force 8-bit access +* +* Please note that "in" and "out" are inverted from the ONFI specification
+ * and are from the controller perspective, so a "in" is a read from the NAND
+ * chip while a "out" is a write to the NAND chip.
+ */include/linux/mtd/rawnand.h +11
-0+struct nand_op_data_instr { +unsigned int len; +union { +void *in; +const void *out; + }; +bool force_8bit; +}; + +/** +* struct nand_op_waitrdy_instr - Definition of a wait ready instruction
+ * @timeout_ms: maximum delay while waiting for the ready/busy pin in ms
+ */
Quoted 8 lines
include/linux/mtd/rawnand.h struct nand_manufacturer_ops { · line 800
Quoted 6 lines
include/linux/mtd/rawnand.h +6
-0+struct nand_op_waitrdy_instr { +unsigned int timeout_ms; +}; + +/** +* enum nand_op_instr_type - Enumeration of all the instructions *of all instruction types
Changed.
Quoted 10 lines
Quoted 10 lines
+ *
+ * Please note that data instructions are separated intoDATA_INandDATA_OUT
+ * instructions.
+ */include/linux/mtd/rawnand.h +4
-0+enum nand_op_instr_type { +NAND_OP_CMD_INSTR , +NAND_OP_ADDR_INSTR , +NAND_OP_DATA_IN_INSTR ,
Quoted 8 lines
include/linux/mtd/rawnand.h struct nand_manufacturer_ops { · line 814
Quoted 6 lines
include/linux/mtd/rawnand.h +6
-0+NAND_OP_DATA_OUT_INSTR , + NAND_OP_WAITRDY_INSTR, +}; + +/** +* struct nand_op_instr - Generic definition of and instruction s/and/an/
Corrected.
Quoted 9 lines
Quoted 5 lines
+ * @type: an enumeration of the instruction type
+ * @cmd/@addr/@data/@waitrdy: the actual instruction to refer depending on the
+ * value of @type"
extra data associated to the instruction. You'll have to use the appropriate element depending on @type"
Changed.
Quoted 39 lines
Quoted 30 lines
+ * @delay_ns: delay to apply by the controller after the instruction has been
+ * actually executed (most of them are directly
handled by the
+ * controllers once the timings negociation has been
done)
+ */include/linux/mtd/rawnand.h +13
-0+struct nand_op_instr { +enum nand_op_instr_type type; +union { +struct nand_op_cmd_instr cmd; +struct nand_op_addr_instr addr; +struct nand_op_data_instr data; +struct nand_op_waitrdy_instr waitrdy; + }; +unsigned int delay_ns; +}; + +/* +* Special handling must be done for the WAITRDY timeout parameter as it usually
+ * is either tPROG (after a prog), tR (before a read), tRST (during a reset) or
+ * tBERS (during an erase) which all of them are u64 values that cannot be
+ * divided by usual kernel macros and must be handled with the special
+ *DIV_ROUND_UP_ULL()macro.
+ */include/linux/mtd/rawnand.h struct nand_manufacturer_ops { · line 841
Quoted 2 lines
include/linux/mtd/rawnand.h +2
-0+#define PSEC_TO_NSEC (x) DIV_ROUND_UP(x, 10^3) +#define PSEC_TO_MSEC (x) DIV_ROUND_UP_ULL(x, 10^9)Hm, that's a bit of a mess to let each macro decide which converter they should use, because we don't know at this level whether the timing is stored in an u64 or u32. How about doing the conversion in the call-sites instead, because there you should know what you manipulate.
Quoted 5 lines
Something like
NAND_OP_CMD(FOO, PSEC_TO_NSEC_UL(sdr->tXX)) NAND_OP_WAITRDY(PSEC_TO_MSEC_ULL(sdr->tXX), PSEC_TO_NSEC_UL(sdr->tYY))
That is a bit messy but okay. I have added to the core a "__DIVIDE" macro that uses the size of the dividend to decide if the *_ULL version must be used, so the drivers can safely use PSEC_TO_NSEC and PSEC_TO_MSEC without knowing it the timings are stored in a u32 or a u64.
Quoted 148 lines
Quoted 148 lines
(quoted diff) +2
-0+ +#define NAND_OP_CMD(id,
delay_ps) \
+
{ \(quoted diff) +1
-0+ .type =
NAND_OP_CMD_INSTR, \(quoted diff) +1
-0+ .cmd.opcode =id, \
(quoted diff) +1
-0+ .delay_ns =
PSEC_TO_NSEC(delay_ps), \drivers/mtd/nand/nand_base.c +3
-0+ } + +#define NAND_OP_ADDR(ncycles, cycles,
delay_ps) \
+
{ \(quoted diff) +1
-0+ .type =
NAND_OP_ADDR_INSTR, \(quoted diff) +1
-0+ .addr ={ \
(quoted diff) +1
-0+ .naddrs =ncycles, \
(quoted diff) +1
-0+ .addrs =cycles, \
(quoted diff) +1
-0+ },\
(quoted diff) +1
-0+ .delay_ns =
PSEC_TO_NSEC(delay_ps), \drivers/mtd/nand/nand_base.c +3
-0+ } + +#define NAND_OP_DATA_IN(l, buf,
delay_ps) \
+
{ \(quoted diff) +1
-0+ .type =
NAND_OP_DATA_IN_INSTR, \(quoted diff) +1
-0+ .data ={ \
(quoted diff) +1
-0+ .len =l, \
(quoted diff) +1
-0+ .in =buf, \
(quoted diff) +1
-0+ .force_8bit =false, \
(quoted diff) +1
-0+ },\
(quoted diff) +1
-0+ .delay_ns =
PSEC_TO_NSEC(delay_ps), \drivers/mtd/nand/nand_base.c +3
-0+ } + +#define NAND_OP_DATA_OUT(l, buf,
delay_ps) \
+
{ \(quoted diff) +1
-0+ .type =
NAND_OP_DATA_OUT_INSTR, \(quoted diff) +1
-0+ .data ={ \
(quoted diff) +1
-0+ .len =l, \
(quoted diff) +1
-0+ .out =buf, \
(quoted diff) +1
-0+ .force_8bit =false, \
(quoted diff) +1
-0+ },\
(quoted diff) +1
-0+ .delay_ns =
PSEC_TO_NSEC(delay_ps), \drivers/mtd/nand/nand_base.c +3
-0+ } + +#define NAND_OP_8BIT_DATA_IN(l, buf,
delay_ps) \
+
{ \(quoted diff) +1
-0+ .type =
NAND_OP_DATA_IN_INSTR, \(quoted diff) +1
-0+ .data ={ \
(quoted diff) +1
-0+ .len =l, \
(quoted diff) +1
-0+ .in =buf, \
(quoted diff) +1
-0+ .force_8bit =true, \
(quoted diff) +1
-0+ },\
(quoted diff) +1
-0+ .delay_ns =
PSEC_TO_NSEC(delay_ps), \drivers/mtd/nand/nand_base.c +3
-0+ } + +#define NAND_OP_8BIT_DATA_OUT(l, buf,
delay_ps) \
+
{ \(quoted diff) +1
-0+ .type =
NAND_OP_DATA_OUT_INSTR, \(quoted diff) +1
-0+ .data ={ \
(quoted diff) +1
-0+ .len =l, \
(quoted diff) +1
-0+ .out =buf, \
(quoted diff) +1
-0+ .force_8bit =true, \
(quoted diff) +1
-0+ },\
(quoted diff) +1
-0+ .delay_ns =
PSEC_TO_NSEC(delay_ps), \drivers/mtd/nand/nand_base.c +3
-0+ } + +#define NAND_OP_WAIT_RDY(tout_ps,
delay_ps) \
+
{ \(quoted diff) +1
-0+ .type =
NAND_OP_WAITRDY_INSTR, \(quoted diff) +1
-0+ .waitrdy.timeout_ms =
PSEC_TO_MSEC(tout_ps), \(quoted diff) +1
-0+ .delay_ns =
PSEC_TO_NSEC(delay_ps), \include/linux/mtd/rawnand.h +7
-0+ } + +/** +* struct nand_subop - a sub operation +* @instrs: array of instructions +* @ninstrs: length of the @instrs array +* @first_instr_start_off: offset to start from for the first instruction
+ * of the sub-operation
+ * @last_instr_end_off: offset to end at (excluded) for the last instruction
+ * of the sub-operation
+ *
+ * When an operation cannot be handled as is by the NAND controller, it will
+ * be split by the parser and the remaining pieces will be handled as
+ * sub-operations.
+ */include/linux/mtd/rawnand.h +5
-0+struct nand_subop { +const struct nand_op_instr *instrs; +unsigned int ninstrs; +/* +* Specific offset for the first and the last instructions
Quoted 8 lines
Review
Quoted 6 lines
of the subop.
(quoted diff) +1
-0+ * Applies for the address cycles in the case of address,or for data
(quoted diff) +1
-0+ * offset in the case of data transfers. Otherwise, it isirrelevant.
(quoted diff) +1
-0+ */Can you move that in the kernel header doc?
Sure.
Quoted 112 lines
Quoted 32 lines
include/linux/mtd/rawnand.h +15
-0+unsigned int first_instr_start_off; +unsigned int last_instr_end_off; +}; + +int nand_subop_get_addr_start_off (const struct nand_subop *subop, +unsigned int op_id); +int nand_subop_get_num_addr_cyc (const struct nand_subop *subop, +unsigned int op_id); +int nand_subop_get_data_start_off (const struct nand_subop *subop, +unsigned int op_id); +int nand_subop_get_data_len (const struct nand_subop *subop, +unsigned int op_id); + +/** +* struct nand_op_parser_addr_constraints - Constraints for address instructions
+ * @maxcycles: maximum number of cycles that the controller can assert by
+ * instruction
+ */include/linux/mtd/rawnand.h +6
-0+struct nand_op_parser_addr_constraints { +unsigned int maxcycles; +}; + +/** +* struct nand_op_parser_data_constraints - Constraints for data instructions
+ * @maxlen: maximum data length that the controller can handle with one
+ * instruction
+ */include/linux/mtd/rawnand.h struct nand_manufacturer_ops { · line 960
Quoted 2 lines
include/linux/mtd/rawnand.h +2
-0+struct nand_op_parser_data_constraints { +unsigned int maxlen;At some point we should probably add minlen and alignment fields, but let's keep that for later.
Quoted 74 lines
include/linux/mtd/rawnand.h +6
-0+}; + +/** +* struct nand_op_parser_pattern_elem - One element of a pattern +* @type: the instructuction type +* @optional: if this element of the pattern is optional or mandatory
+ * @addr/@data: address or data constraint (number of cycles or data length)
+ */include/linux/mtd/rawnand.h +16
-0+struct nand_op_parser_pattern_elem { +enum nand_op_instr_type type; +bool optional; +union { +struct nand_op_parser_addr_constraints addr; +struct nand_op_parser_data_constraints data; + }; +}; + +#define NAND_OP_PARSER_PAT_CMD_ELEM (_opt) \ + { \ + .type = NAND_OP_CMD_INSTR, \ + .optional = _opt, \ + } + +#define NAND_OP_PARSER_PAT_ADDR_ELEM(_opt,
_maxcycles) \(quoted diff) +2
-0+ { \ + .type = NAND_OP_ADDR_INSTR,\
(quoted diff) +2
-0+ .optional = _opt, \ + .addr.maxcycles =
_maxcycles, \drivers/mtd/nand/nand_base.c +3
-0+ } + +#define NAND_OP_PARSER_PAT_DATA_IN_ELEM(_opt,
_maxlen) \(quoted diff) +2
-0+ { \ + .type =
NAND_OP_DATA_IN_INSTR, \(quoted diff) +2
-0+ .optional = _opt, \ + .data.maxlen =
_maxlen, \drivers/mtd/nand/nand_base.c +3
-0+ } + +#define NAND_OP_PARSER_PAT_DATA_OUT_ELEM(_opt,
_maxlen) \(quoted diff) +2
-0+ { \ + .type =
NAND_OP_DATA_OUT_INSTR, \(quoted diff) +2
-0+ .optional = _opt, \ + .data.maxlen =
_maxlen, \drivers/mtd/nand/nand_base.c +3
-0+ } + +#define
NAND_OP_PARSER_PAT_WAITRDY_ELEM(_opt)\(quoted diff) +2
-0+ { \ + .type =
NAND_OP_WAITRDY_INSTR, \include/linux/mtd/rawnand.h +8
-0+ .optional = _opt, \ + } + +/** +* struct nand_op_parser_pattern - A complete pattern +* @elems: array of pattern elements +* @nelems: number of pattern elements in @elems array +* @exec: the function that will actually execute this pattern, written in the
+ * controller driver
+ *
+ * This is a complete pattern that is a list of elements, each one reprensenting
+ * one instruction with its constraints. Controller drivers must
Quoted 8 lines
Quoted 5 lines
declare as much
+ * patterns as they support and give the list of the supported patterns (created
+ * with the help of the following macro) at the call tonand_op_parser_exec_ops/at the call to nand_op_parser_exec_op/when calling nand_op_parser_exec_op()/
Ok.
Quoted 9 lines
Quoted 4 lines
+ * which shall be the main thing to do in the driver implementation of
+ * ->exec_op().I'd be more lax than that. Yes the parser is the preferred approach when you deal with a complex controller. But for simple controllers it's not.
Quoted 8 lines
Quoted 2 lines
Once there is a match between the pattern and an operation, the
+ * core calls the @exec function to actually do the operation.Not necessarily. The plan is still to ask the controller which operation it supports before actually executing them. So, in this case (when
check_onlyparam is true),nand_op_parser_exec_op()will never call ->exec(), it will just make sure the operation can be handled with the provided patterns.
I changed that §, see next version.
Quoted 30 lines
Quoted 30 lines
+ */
include/linux/mtd/rawnand.h +4
-0+struct nand_op_parser_pattern { +const struct nand_op_parser_pattern_elem *elems; +unsigned int nelems; + int (*exec)(struct nand_chip *chip, const struct
nand_subop*subop); +};(quoted diff) +2
-0+ +#define
NAND_OP_PARSER_PATTERN(_exec, ...)
\
+
{
\(quoted diff) +1
-0+ .exec =
_exec,
\(quoted diff) +1
-0+ .elems = (struct nand_op_parser_pattern_elem[]){
__VA_ARGS__}, \(quoted diff) +1
-0+ .nelems = sizeof((structnand_op_parser_pattern_elem[]) {
__VA_ARGS__}) / \(quoted diff) +1
-0+ sizeof(struct
nand_op_parser_pattern_elem), \include/linux/mtd/rawnand.h +7
-0+ } + +/** +* struct nand_op_parser - The actual parser +* @patterns: array of patterns +* @npatterns: length of the @patterns array +*
Quoted 8 lines
Quoted 2 lines
+ * The actual parser structure wich is an array of supported patterns.
It's worth mentioning that patterns will be tested in their declaration order, and the first match will be taken, so it's important to oder patterns appropriately so that simple/inefficient patterns are placed at the end of the list. Usually, this is where you put single instruction patterns.
Nice explanation, added.
Quoted 22 lines
Quoted 22 lines
+ */
include/linux/mtd/rawnand.h +6
-0+struct nand_op_parser { +const struct nand_op_parser_pattern *patterns; +unsigned int npatterns; +}; + +#define
NAND_OP_PARSER(...)
\
+
{
\(quoted diff) +1
-0+ .patterns = (struct nand_op_parser_pattern[]){
__VA_ARGS__}, \(quoted diff) +1
-0+ .npatterns = sizeof((structnand_op_parser_pattern[]) {
__VA_ARGS__}) / \(quoted diff) +1
-0+ sizeof(struct
nand_op_parser_pattern), \drivers/mtd/nand/nand_base.c +3
-0+ } + +/**
Quoted 8 lines
Quoted 6 lines
+ * struct
nand_operation- The actual operation
+ * @instrs: array of instructions to execute
+ * @ninstrs: length of the @instrs array
+ *
+ * The actual operation structure that will be given to the parser.Not only the parser, it will be passed to
chip->exep_op()as well.
Yes.
Quoted 98 lines
Quoted 98 lines
+ */
include/linux/mtd/rawnand.h +6
-0+struct nand_operation { +const struct nand_op_instr *instrs; +unsigned int ninstrs; +}; + +#define
NAND_OPERATION(_instrs)\include/linux/mtd/rawnand.h +3
-0+ { \ + .instrs = _instrs, \ + .ninstrs =
ARRAY_SIZE(_instrs), \include/linux/mtd/rawnand.h +8
-0+ } + +int nand_fill_column_cycles (struct nand_chip *chip,u8 *addrs, +unsigned int offset_in_page); + +int nand_op_parser_exec_op (struct nand_chip *chip, +const struct nand_op_parser *parser, + const struct nand_operation *op, bool
check_only); +
+/**
* structnand_chip- NAND Private Flash Chip Data
* @mtd: MTD device registered to the MTD framework
* @IO_ADDR_R: [BOARDSPECIFIC] address to read the
8 I/O lines of the @@ -885,6 +1216,9 @@ structnand_chip{
int (*setup_data_interface)(structmtd_info*mtd, int
chipnr, const structnand_data_interface*conf);include/linux/mtd/rawnand.h +12
-0+int (*exec_op)(struct nand_chip *chip, +const struct nand_operation *op, +bool check_only);int chip_delay;unsigned int options;@@ -945,6 +1279,15 @@ struct nand_chip { } manufacturer; }; +static inline int nand_exec_op (struct nand_chip *chip, +const struct nand_operation *op) +{ +if (!chip->exec_op) +return -ENOTSUPP ; + +return chip->exec_op (chip, op, false); +} +extern const struct mtd_ooblayout_ops nand_ooblayout_sp_ops;extern const struct mtd_ooblayout_ops nand_ooblayout_lp_ops;@@ -1307,23 +1650,26 @@ int nand_readid_op(struct nand_chip *chip,u8 addr, void *buf, int
nand_status_op(struct nand_chip *chip, u8 *status); intnand_erase_op(struct nand_chip *chip, unsigned int eraseblock); int nand_read_page_op(structnand_chip*chip, unsigned int page,(quoted diff) +0
-1- unsigned int column, void *buf, unsigned intlen); -int nand_change_read_column_op(struct
nand_chip*chip, unsigned int column,(quoted diff) +0
-1- void *buf, unsigned int len, bool
force_8bit);(quoted diff) +1
-0+ unsigned int offset_in_page, void *buf,unsigned int len); +int nand_change_read_column_op(struct
nand_chip*chip,(quoted diff) +1
-0+ unsigned int offset_in_page, void*buf,
include/linux/mtd/rawnand.h +1
-1+ unsigned int len, bool force_8bit); int nand_read_oob_op(struct nand_chip *chip, unsigned int page, - unsigned int column, void *buf, unsigned intlen);
(quoted diff) +1
-0+ unsigned int offset_in_page, void *buf,unsigned int len); int nand_prog_page_begin_op(struct
nand_chip*chip, unsigned int page,(quoted diff) +1
-1- unsigned int column, const void *buf, + unsigned int offset_in_page, constvoid *buf, unsigned int len);
intnand_prog_page_end_op(struct nand_chip *chip);
int nand_prog_page_op(structnand_chip*chip, unsigned int page,(quoted diff) +0
-1- unsigned int column, const void *buf,unsigned int len); -int nand_change_write_column_op(struct
nand_chip*chip, unsigned int column,(quoted diff) +0
-1- const void *buf, unsigned int len,bool
force_8bit);include/linux/mtd/rawnand.h +4
-0+ unsigned int offset_in_page, const void *buf, + unsigned int len); +int nand_change_write_column_op(struct nand_chip *chip, + unsigned int offset_in_page, constvoid *buf,
(quoted diff) +1
-0+ unsigned int len, bool force_8bit); int nand_read_data_op(struct nand_chip *chip, void *buf, unsignedint len,
include/linux/mtd/rawnand.h +2
-2- bool force_8bits); + bool force_8bit);int nand_write_data_op (struct nand_chip *chip,const void *buf, -unsigned int len,bool force_8bits); +unsigned int len,bool force_8bit);
Quoted 3 lines
Quoted 2 lines
/* Free resources held by the NAND device */
voidnand_cleanup(struct nand_chip *chip);
Thanks for the detailed review,
Miquèl
RFC 5/12 dt-bindings: mtd: add Marvell NAND controller documentation
In reply to Miquel RaynalDocument the bindings for the legacy and the new bindings relative to Marvell NAND controller driver rework.
--- .../devicetree/bindings/mtd/marvell-nand.txt | 95 ++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 Documentation/devicetree/bindings/mtd/marvell-nand.txt
Documentation/devicetree/bindings/mtd/marvell-nand.txt new file +95 -0
@@ -0,0 +1,95 @@ +Marvell NAND Flash Controller (NFC) + +Required properties: +C'est faux, t'en a rajout? un y a pas longtps :). +Je conseille de mettre ?a sous forme de liste, genre + +- compatible: can be one of the following: + * "marvell,armada-8k-nand-controller" + * "marvell,armada370-nand-controller" + * "marvell,pxa3xx-nand-controller" + * "marvell,armada-8k-nand" (deprecated) + * "marvell,armada370-nand" (deprecated) + * "marvell,pxa3xx-nand" (deprecated) +- reg: shall contain registers location and length for data and reg. +- #address-cells: shall be set to 1. Encode the nand CS. +- #size-cells: shall be set to 0. +- interrupts: shall define the nand controller interrupt. +- clocks: shall reference nand controller clocks. +- marvell,system-controller: Set to retrieve the syscon node that handles + NAND controller related registers (only required with the + "marvell,armada-8k-nand[-controller]" compatibles). + +Optional properties: +- dmas: shall reference DMA channel associated to the NAND controller. +- dma-names: shall be "rxtx". + +Optional children nodes: +Children nodes represent the available NAND chips. + +Required properties: +- reg: shall contain the native Chip Select ids (0-3) +- marvell,rb: shall contain the native Ready/Busy ids (0-1) + +Optional properties: +- marvell,nand-keep-config: orders the driver not to take the timings + from the core and leaving them completely untouched. Bootloader + timings will then be used. +- marvell,nand-enable-arbiter: only useful for PXA platforms, will + enable bus arbiter between NFC and DFI bus (must be enabled for + NFC operation) +- nand-on-flash-bbt: speed up the boot process by not discovering all + the bad blocks at each boot and reading directly an on flash table. +- nand-ecc-mode: one of the supported ECC modes ("none", "soft", + "hw"). If not specified, hardware ECC will be used. +- nand-ecc-algo: algorithm to use if previous choice was "soft" + ("hamming" or "bch). This property may be added for hardware ECC for + clarification but will be ignored by the driver because ECC mode is + chosen depending on the page size and the strength required by the + NAND chip. This value may be overwritten with the nand-ecc-strength + property. +- nand-ecc-strength: desired ECC strength. +- nand-ecc-step-size: indication on the ECC step size. This has no + effect and will be ignored by the driver when using hardware + ECC. Because Marvell's NAND flash controller does use fixed strength + (1-bit for Hamming, 16-bit for BCH), the step size will shrink or + grown in order to fit the required strength and the value + updated. Step sizes are not completely random for all and follow + certain patterns described in AN-379, "Marvell SoC NFC ECC". + +See Documentation/devicetree/bindings/mtd/nand.txt for more details on +generic bindings. + + +Example: +nand_controller: nand-controller at d0000 { + compatible = "marvell,armada370-nand-controller"; + reg = <0xd0000 0x54>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&coredivclk 0>; + status = "okay"; + + nand at 0 { + reg = <0>; + marvell,rb = <0>; + nand-ecc-mode = "hw"; + marvell,nand-keep-config; + marvell,nand-enable-arbiter; + nand-on-flash-bbt; + nand-ecc-strength = <4>; + nand-ecc-step-size = <512>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition at 0 { + label = "Rootfs"; + reg = <0x00000000 0x40000000>; + }; + }; + }; +};
Signature · 1 line
2.11.0
RFC 5/12 dt-bindings: mtd: add Marvell NAND controller documentation
Quoted 15 lines · On Wed, 18 Oct 2017 16:36:22 +0200 Miquel Raynal…
Document the bindings for the legacy and the new bindings relative to Marvell NAND controller driver rework.
Signed-off-by Miquel Raynal <[email protected]>--- .../devicetree/bindings/mtd/marvell-nand.txt | 95 ++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 Documentation/devicetree/bindings/mtd/marvell-nand.txtDocumentation/devicetree/bindings/mtd/marvell-nand.txt new file +0
-0
Documentation/devicetree/bindings/mtd/marvell-nand.txt line 5
Quoted 5 lines
Documentation/devicetree/bindings/mtd/marvell-nand.txt new file +5
-0@@ -0,0 +1,95 @@ +Marvell NAND Flash Controller (NFC) + +Required properties: +C'est faux, t'en a rajout? un y a pas longtps :). +Je conseille de mettre ?a sous forme de liste, genre
Oops! French words slipped in :-).
Quoted 90 lines
Documentation/devicetree/bindings/mtd/marvell-nand.txt new file +90
-0+ +- compatible: can be one of the following: + * "marvell,armada-8k-nand-controller" + * "marvell,armada370-nand-controller" + * "marvell,pxa3xx-nand-controller" + * "marvell,armada-8k-nand" (deprecated) + * "marvell,armada370-nand" (deprecated) + * "marvell,pxa3xx-nand" (deprecated) +- reg: shall contain registers location and length for data and reg. +- #address-cells: shall be set to 1. Encode the nand CS. +- #size-cells: shall be set to 0. +- interrupts: shall define the nand controller interrupt. +- clocks: shall reference nand controller clocks. +- marvell,system-controller: Set to retrieve the syscon node that handles + NAND controller related registers (only required with the + "marvell,armada-8k-nand[-controller]" compatibles). + +Optional properties: +- dmas: shall reference DMA channel associated to the NAND controller. +- dma-names: shall be "rxtx". + +Optional children nodes: +Children nodes represent the available NAND chips. + +Required properties: +- reg: shall contain the native Chip Select ids (0-3) +- marvell,rb: shall contain the native Ready/Busy ids (0-1) + +Optional properties: +- marvell,nand-keep-config: orders the driver not to take the timings + from the core and leaving them completely untouched. Bootloader + timings will then be used. +- marvell,nand-enable-arbiter: only useful for PXA platforms, will + enable bus arbiter between NFC and DFI bus (must be enabled for + NFC operation) +- nand-on-flash-bbt: speed up the boot process by not discovering all + the bad blocks at each boot and reading directly an on flash table. +- nand-ecc-mode: one of the supported ECC modes ("none", "soft", + "hw"). If not specified, hardware ECC will be used. +- nand-ecc-algo: algorithm to use if previous choice was "soft" + ("hamming" or "bch). This property may be added for hardware ECC for + clarification but will be ignored by the driver because ECC mode is + chosen depending on the page size and the strength required by the + NAND chip. This value may be overwritten with the nand-ecc-strength + property. +- nand-ecc-strength: desired ECC strength. +- nand-ecc-step-size: indication on the ECC step size. This has no + effect and will be ignored by the driver when using hardware + ECC. Because Marvell's NAND flash controller does use fixed strength + (1-bit for Hamming, 16-bit for BCH), the step size will shrink or + grown in order to fit the required strength and the value + updated. Step sizes are not completely random for all and follow + certain patterns described in AN-379, "Marvell SoC NFC ECC". + +See Documentation/devicetree/bindings/mtd/nand.txt for more details on +generic bindings. + + +Example: +nand_controller: nand-controller at d0000 { + compatible = "marvell,armada370-nand-controller"; + reg = <0xd0000 0x54>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&coredivclk 0>; + status = "okay"; + + nand at 0 { + reg = <0>; + marvell,rb = <0>; + nand-ecc-mode = "hw"; + marvell,nand-keep-config; + marvell,nand-enable-arbiter; + nand-on-flash-bbt; + nand-ecc-strength = <4>; + nand-ecc-step-size = <512>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition at 0 { + label = "Rootfs"; + reg = <0x00000000 0x40000000>; + }; + }; + }; +};
Rob Herring
maintainerRFC 5/12 dt-bindings: mtd: add Marvell NAND controller documentation
In reply to Miquel RaynalQuoted 15 lines · Miquel Raynal
Document the bindings for the legacy and the new bindings relative to Marvell NAND controller driver rework.
Signed-off-by Miquel Raynal <[email protected]>--- .../devicetree/bindings/mtd/marvell-nand.txt | 95 ++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 Documentation/devicetree/bindings/mtd/marvell-nand.txtDocumentation/devicetree/bindings/mtd/marvell-nand.txt new file +0
-0
Documentation/devicetree/bindings/mtd/marvell-nand.txt line 5
Quoted 5 lines
Documentation/devicetree/bindings/mtd/marvell-nand.txt new file +5
-0@@ -0,0 +1,95 @@ +Marvell NAND Flash Controller (NFC) + +Required properties: +C'est faux, t'en a rajout? un y a pas longtps :). +Je conseille de mettre ?a sous forme de liste, genre
Humm.
Documentation/devicetree/bindings/mtd/marvell-nand.txt line 14
Quoted 9 lines
Documentation/devicetree/bindings/mtd/marvell-nand.txt new file +9
-0+ +- compatible: can be one of the following: + * "marvell,armada-8k-nand-controller" + * "marvell,armada370-nand-controller" + * "marvell,pxa3xx-nand-controller" + * "marvell,armada-8k-nand" (deprecated) + * "marvell,armada370-nand" (deprecated) + * "marvell,pxa3xx-nand" (deprecated) +- reg: shall contain registers location and length for data and reg.
2 regions?
Documentation/devicetree/bindings/mtd/marvell-nand.txt line 18
Quoted 4 lines
Documentation/devicetree/bindings/mtd/marvell-nand.txt new file +4
-0+- #address-cells: shall be set to 1. Encode the nand CS. +- #size-cells: shall be set to 0. +- interrupts: shall define the nand controller interrupt. +- clocks: shall reference nand controller clocks.
How many clocks?
Quoted 12 lines
Documentation/devicetree/bindings/mtd/marvell-nand.txt new file +12
-0+- marvell,system-controller: Set to retrieve the syscon node that handles + NAND controller related registers (only required with the + "marvell,armada-8k-nand[-controller]" compatibles). + +Optional properties: +- dmas: shall reference DMA channel associated to the NAND controller. +- dma-names: shall be "rxtx". + +Optional children nodes: +Children nodes represent the available NAND chips. + +Required properties:
Documentation/devicetree/bindings/mtd/marvell-nand.txt line 40
Quoted 10 lines
Documentation/devicetree/bindings/mtd/marvell-nand.txt new file +10
-0+- reg: shall contain the native Chip Select ids (0-3) +- marvell,rb: shall contain the native Ready/Busy ids (0-1) + +Optional properties: +- marvell,nand-keep-config: orders the driver not to take the timings + from the core and leaving them completely untouched. Bootloader + timings will then be used. +- marvell,nand-enable-arbiter: only useful for PXA platforms, will + enable bus arbiter between NFC and DFI bus (must be enabled for + NFC operation)
Why do you need this if it must be enabled?
Quoted 8 lines
Documentation/devicetree/bindings/mtd/marvell-nand.txt new file +8
-0+- nand-on-flash-bbt: speed up the boot process by not discovering all + the bad blocks at each boot and reading directly an on flash table. +- nand-ecc-mode: one of the supported ECC modes ("none", "soft", + "hw"). If not specified, hardware ECC will be used. +- nand-ecc-algo: algorithm to use if previous choice was "soft" + ("hamming" or "bch). This property may be added for hardware ECC for + clarification but will be ignored by the driver because ECC mode is + chosen depending on the page size and the strength required by the
Documentation/devicetree/bindings/mtd/marvell-nand.txt line 58
Quoted 10 lines
Documentation/devicetree/bindings/mtd/marvell-nand.txt new file +10
-0+ NAND chip. This value may be overwritten with the nand-ecc-strength + property. +- nand-ecc-strength: desired ECC strength. +- nand-ecc-step-size: indication on the ECC step size. This has no + effect and will be ignored by the driver when using hardware + ECC. Because Marvell's NAND flash controller does use fixed strength + (1-bit for Hamming, 16-bit for BCH), the step size will shrink or + grown in order to fit the required strength and the value + updated. Step sizes are not completely random for all and follow + certain patterns described in AN-379, "Marvell SoC NFC ECC".
For standard properties, just reference nand.txt and add any constraints. Don't define what the property is again.
Quoted 4 lines
Documentation/devicetree/bindings/mtd/marvell-nand.txt new file +4
-0+ +See Documentation/devicetree/bindings/mtd/nand.txt for more details on +generic bindings. +
Documentation/devicetree/bindings/mtd/marvell-nand.txt line 72
Quoted 10 lines
Documentation/devicetree/bindings/mtd/marvell-nand.txt new file +10
-0+ +Example: +nand_controller: nand-controller at d0000 { + compatible = "marvell,armada370-nand-controller"; + reg = <0xd0000 0x54>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&coredivclk 0>; + status = "okay";
Don't show status in examples.
Quoted 26 lines
Documentation/devicetree/bindings/mtd/marvell-nand.txt new file +23
-0+ + nand at 0 { + reg = <0>; + marvell,rb = <0>; + nand-ecc-mode = "hw"; + marvell,nand-keep-config; + marvell,nand-enable-arbiter; + nand-on-flash-bbt; + nand-ecc-strength = <4>; + nand-ecc-step-size = <512>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition at 0 { + label = "Rootfs"; + reg = <0x00000000 0x40000000>; + }; + }; + }; +};Signature · 1 line
2.11.0
RFC 5/12 dt-bindings: mtd: add Marvell NAND controller documentation
Hi Rob,
Quoted 5 lines
Documentation/devicetree/bindings/mtd/marvell-nand.txt line 5
Quoted 3 lines
Documentation/devicetree/bindings/mtd/marvell-nand.txt new file +3
-0+Required properties: +C'est faux, t'en a rajout? un y a pas longtps :). +Je conseille de mettre ?a sous forme de liste, genreHumm.
Oops!
Quoted 12 lines
Quoted 11 lines
Documentation/devicetree/bindings/mtd/marvell-nand.txt new file +9
-0+ +- compatible: can be one of the following: + * "marvell,armada-8k-nand-controller" + * "marvell,armada370-nand-controller" + * "marvell,pxa3xx-nand-controller" + * "marvell,armada-8k-nand" (deprecated) + * "marvell,armada370-nand" (deprecated) + * "marvell,pxa3xx-nand" (deprecated) +- reg: shall contain registers location and length for data andreg.
Quoted 1 line
2 regions?
Just one, rephrased.
Quoted 7 lines
Quoted 5 lines
+- #address-cells: shall be set to 1. Encode the nand CS. +- #size-cells: shall be set to 0. +- interrupts: shall define the nand controller interrupt. +- clocks: shall reference nand controller clocks.How many clocks?
Only one too: "reference the NAND controller clock".
Quoted 19 lines
Quoted 19 lines
+- marvell,system-controller: Set to retrieve the syscon node that handles
Documentation/devicetree/bindings/mtd/marvell-nand.txt new file +5
-0+ NAND controller related registers (only required with the + "marvell,armada-8k-nand[-controller]" compatibles). + +Optional properties: +- dmas: shall reference DMA channel associated to the NANDcontroller. +- dma-names: shall be "rxtx".
Documentation/devicetree/bindings/mtd/marvell-nand.txt new file +10
-0+ +Optional children nodes: +Children nodes represent the available NAND chips. + +Required properties: +- reg: shall contain the native Chip Select ids (0-3) +- marvell,rb: shall contain the native Ready/Busy ids (0-1) + +Optional properties: +- marvell,nand-keep-config: orders the driver not to take the
Quoted 8 lines
Documentation/devicetree/bindings/mtd/marvell-nand.txt line 40
Quoted 6 lines
timings
Documentation/devicetree/bindings/mtd/marvell-nand.txt new file +5
-0+ from the core and leaving them completely untouched. Bootloader + timings will then be used. +- marvell,nand-enable-arbiter: only useful for PXA platforms, will + enable bus arbiter between NFC and DFI bus (must be enabled for + NFC operation)Why do you need this if it must be enabled?
That is right, there is no more need for it, also removed it from the driver, just knowing the board with the compatible string is enough.
Quoted 27 lines
Quoted 22 lines
+- nand-on-flash-bbt: speed up the boot process by not discovering all
+ the bad blocks at each boot and reading directly an on flash table. +- nand-ecc-mode: one of the supported ECC modes ("none", "soft",Documentation/devicetree/bindings/mtd/marvell-nand.txt new file +3
-0+ "hw"). If not specified, hardware ECC will be used. +- nand-ecc-algo: algorithm to use if previous choice was "soft" + ("hamming" or "bch). This property may be added for hardware ECCfor
+ clarification but will be ignored by the driver because ECC mode is
+ chosen depending on the page size and the strength required by the
+ NAND chip. This value may be overwritten with the nand-ecc-strengthDocumentation/devicetree/bindings/mtd/marvell-nand.txt new file +5
-0+ property. +- nand-ecc-strength: desired ECC strength. +- nand-ecc-step-size: indication on the ECC step size. This has no + effect and will be ignored by the driver when using hardware + ECC. Because Marvell's NAND flash controller does use fixedstrength
Documentation/devicetree/bindings/mtd/marvell-nand.txt line 58
Quoted 4 lines
Documentation/devicetree/bindings/mtd/marvell-nand.txt new file +4
-0+ (1-bit for Hamming, 16-bit for BCH), the step size will shrink or + grown in order to fit the required strength and the value + updated. Step sizes are not completely random for all and follow + certain patterns described in AN-379, "Marvell SoC NFC ECC".
Quoted 2 lines
For standard properties, just reference nand.txt and add any constraints. Don't define what the property is again.
Ok.
Quoted 17 lines
Quoted 5 lines
(quoted diff) +2
-0+ +See Documentation/devicetree/bindings/mtd/nand.txt for moredetails on +generic bindings.
Documentation/devicetree/bindings/mtd/marvell-nand.txt new file +1
-0+Documentation/devicetree/bindings/mtd/marvell-nand.txt line 72
Quoted 10 lines
Documentation/devicetree/bindings/mtd/marvell-nand.txt new file +10
-0+ +Example: +nand_controller: nand-controller at d0000 { + compatible = "marvell,armada370-nand-controller"; + reg = <0xd0000 0x54>; + #address-cells = <1>; + #size-cells = <0>; + interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&coredivclk 0>; + status = "okay";Don't show status in examples.
Ok.
Quoted 19 lines
Quoted 19 lines
Documentation/devicetree/bindings/mtd/marvell-nand.txt new file +18
-0+ + nand at 0 { + reg = <0>; + marvell,rb = <0>; + nand-ecc-mode = "hw"; + marvell,nand-keep-config; + marvell,nand-enable-arbiter; + nand-on-flash-bbt; + nand-ecc-strength = <4>; + nand-ecc-step-size = <512>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition at 0 { + label = "Rootfs";
Quoted 8 lines
Quoted 8 lines
Documentation/devicetree/bindings/mtd/marvell-nand.txt new file +5
-0+ reg = <0x00000000 0x40000000>; + }; + }; + }; +};Signature · 1 line
2.11.0
Thanks for the review,
Miqu?l
RFC 6/12 mtd: nand: add reworked Marvell NAND controller driver
In reply to Miquel RaynalAdd marvell_nand driver which aims at replacing the existing pxa3xx_nand driver.
The new driver intends to be easier to understand and follows the brand new NAND framework rules by implementing hooks for every pattern the controller might support and referencing them inside a parser object that will be given to the core at each ->exec_op() call.
Raw accessors are implemented, useful to test/debug memory/filesystem corruptions. Userspace binaries contained in the mtd-utils package may now be used and their output trusted.
Timings may not be kept from the bootloader anymore, the timings used for instance in U-Boot were not optimal and it supposed to have NAND support (and initialized) in the bootloader.
Thanks to the improved timings, implementation of ONFI mode 5 support (with EDO managed by adding a delay on data sampling), merging the commands together and optimizing writes in the command registers, the new driver may achieve faster throughputs in both directions. Measurements show an improvement of about +23% read throughput and +24% write throughput. These measurements have been done with an Armada-385-DB-AP (4kiB NAND pages forced in 4-bit strength BCH ECC correction) using the userspace tool 'flash_speed' from the MTD test suite.
Besides these important topics, the new driver addresses several unsolved known issues in the old driver which:
- did not work with ECC soft neither with ECC none ;
- relied on naked read/write (which is unchanged) while the NFCv1 embedded in the pxa3xx platforms do not implement it, so several NAND commands did not actually ever work without any notice (like reading the ONFI PARAM_PAGE or SET/GET_FEATURES) ;
- wrote the OOB data correctly, but was not able to read it correctly past the first OOB data chunk ;
- did not displayed ECC bytes ;
- used device tree bindings that did not allow more than one NAND chip, and did not allow to choose the correct chip select if not incrementing from 0. Plus, the Ready/Busy line used had to be 0.
Old device tree bindings are still supported but deprecated. A more hierarchical view has to be used to keep the controller and the NAND chip structures clearly separated both inside the device tree and also in the driver code.
--- drivers/mtd/nand/Kconfig | 11 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/marvell_nand.c | 2384 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 2396 insertions(+) create mode 100644 drivers/mtd/nand/marvell_nand.c
drivers/mtd/nand/Kconfig +11 -0
@@ -323,6 +323,17 @@ config MTD_NAND_PXA3xx platforms (XP, 370, 375, 38x, 39x) and 64-bit Armada platforms (7K, 8K) (NFCv2). +config MTD_NAND_MARVELL + tristate "NAND controller support on Marvell boards" + depends on PXA3xx || ARCH_MMP || PLAT_ORION || ARCH_MVEBU || \ + (COMPILE_TEST && HAS_IOMEM) + help + This enables the NAND flash controller driver for Marvell boards, + including: + - PXA3xx processors (NFCv1) + - 32-bit Armada platforms (XP, 37x, 38x, 39x) (NFCv2) + - 64-bit Aramda platforms (7k, 8k) (NFCv2) + config MTD_NAND_SLC_LPC32XX tristate "NXP LPC32xx SLC Controller" depends on ARCH_LPC32XX
drivers/mtd/nand/Makefile +1 -0
@@ -31,6 +31,7 @@ obj-$(CONFIG_MTD_NAND_OMAP2) += omap2_nand.o obj-$(CONFIG_MTD_NAND_OMAP_BCH_BUILD) += omap_elm.o obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o obj-$(CONFIG_MTD_NAND_PXA3xx) += pxa3xx_nand.o +obj-$(CONFIG_MTD_NAND_MARVELL) += marvell_nand.o obj-$(CONFIG_MTD_NAND_TMIO) += tmio_nand.o obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o obj-$(CONFIG_MTD_NAND_PASEMI) += pasemi_nand.o
drivers/mtd/nand/marvell_nand.c new file +2384 -0
2,384 lines not on this page · raw message
Signature · 1 line
2.11.0
RFC 6/12 mtd: nand: add reworked Marvell NAND controller driver
Quoted 8 lines · On Wed, 18 Oct 2017 16:36:23 +0200 Miquel Raynal…
drivers/mtd/nand/marvell_nand.c new file +8
-0+ +enum marvell_nfc_variant { +MARVELL_NFC_VARIANT_PXA3XX , +MARVELL_NFC_VARIANT_ARMADA370 , +MARVELL_NFC_VARIANT_ARMADA_8K , +}; + +/*
drivers/mtd/nand/marvell_nand.c line 281
Quoted 10 lines
drivers/mtd/nand/marvell_nand.c new file +10
-0+* NAND controller capabilities for distinction between compatible strings +* +* @variant: Board type +* @max_cs_nb: Number of Chip Select lines available +* @max_rb_nb: Number of Ready/Busy lines available +* @legacy_of_bindings Indicates if DT parsing must be done using the old +* fashion way +*/ +struct marvell_nfc_caps { +enum marvell_nfc_variant variant;
Do you really need this variant field. It seems you're only using it to detect if you should MUX the NAND pins using the Special Function registers on armada8k. Maybe you should just have:
bool need_system_controller;
and set it to true for the "armada8k" compatible.
Quoted 5 lines
drivers/mtd/nand/marvell_nand.c new file +5
-0+unsigned int max_cs_nb; +unsigned int max_rb_nb; +bool legacy_of_bindings; +}; +
Re: [RFC 06/12] mtd: nand: add reworked Marvell NAND controller driver
Hi Boris,
Quoted 24 lines
Quoted 17 lines
drivers/mtd/nand/marvell_nand.c new file +8
-0+enum marvell_nfc_variant { +MARVELL_NFC_VARIANT_PXA3XX , +MARVELL_NFC_VARIANT_ARMADA370 , +MARVELL_NFC_VARIANT_ARMADA_8K , +}; + +/* +* NAND controller capabilities for distinction between compatible strings
+ *
+ * @variant: Board type
+ * @max_cs_nb: Number of Chip Select lines available
+ * @max_rb_nb: Number of Ready/Busy lines available
+ * @legacy_of_bindings Indicates if DT parsing must be done
using the old
+ * fashion way
+ */drivers/mtd/nand/marvell_nand.c line 281
Quoted 2 lines
drivers/mtd/nand/marvell_nand.c new file +2
-0+struct marvell_nfc_caps { +enum marvell_nfc_variant variant;Do you really need this variant field. It seems you're only using it to detect if you should MUX the NAND pins using the Special Function registers on armada8k. Maybe you should just have:
Quoted 3 lines
bool need_system_controller;and set it to true for the "armada8k" compatible.
Ok. After Rob's review I also added a "bool need_arbiter" to avoid using any "variant == PXA_VARIANT" in the code.
Quoted 6 lines
drivers/mtd/nand/marvell_nand.c line 286
Quoted 6 lines
drivers/mtd/nand/marvell_nand.c new file +5
-0+unsigned int max_cs_nb; +unsigned int max_rb_nb; +bool legacy_of_bindings; +}; +
Thanks,
Miquèl
RFC 7/12 ARM: dts: armada-370-xp: use reworked NAND controller driver
In reply to Miquel RaynalChange the bindings to use the reworked Marvell NAND controller driver. Also adapt the nand controller node organization to distinguish which property is relevant for the controller, and which one is NAND chip specific. Expose the partitions as a subnode of the NAND chip.
Remove the 'marvell,nand-enable-arbiter' property, inefective with Armada boards.
--- arch/arm/boot/dts/armada-370-db.dts | 56 ++++---- arch/arm/boot/dts/armada-370-dlink-dns327l.dts | 119 ++++++++-------- arch/arm/boot/dts/armada-370-mirabox.dts | 50 ++++--- arch/arm/boot/dts/armada-370-netgear-rn102.dts | 89 ++++++------ arch/arm/boot/dts/armada-370-netgear-rn104.dts | 89 ++++++------ arch/arm/boot/dts/armada-370-rd.dts | 51 ++++--- arch/arm/boot/dts/armada-370-seagate-nas-xbay.dtsi | 63 +++++---- arch/arm/boot/dts/armada-370-xp.dtsi | 6 +- arch/arm/boot/dts/armada-xp-db-dxbc2.dts | 1 - arch/arm/boot/dts/armada-xp-db-xc3-24g4xg.dts | 1 - arch/arm/boot/dts/armada-xp-db.dts | 1 - arch/arm/boot/dts/armada-xp-gp.dts | 1 - arch/arm/boot/dts/armada-xp-lenovo-ix4-300d.dts | 1 - arch/arm/boot/dts/armada-xp-linksys-mamba.dts | 155 +++++++++++---------- arch/arm/boot/dts/armada-xp-netgear-rn2120.dts | 89 ++++++------ 15 files changed, 420 insertions(+), 352 deletions(-)
arch/arm/boot/dts/armada-370-db.dts +29 -27
@@ -142,33 +142,6 @@ usb at 51000 { status = "okay"; }; - - nand at d0000 { - status = "okay"; - num-cs = <1>; - marvell,nand-keep-config; - marvell,nand-enable-arbiter; - nand-on-flash-bbt; - - partitions { - compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; - - partition at 0 { - label = "U-Boot"; - reg = <0 0x800000>; - }; - partition at 800000 { - label = "Linux"; - reg = <0x800000 0x800000>; - }; - partition at 1000000 { - label = "Filesystem"; - reg = <0x1000000 0x3f000000>; - }; - }; - }; }; };
@@ -276,3 +249,32 @@ }; }; +&nand_controller { + status = "okay"; + + nand at 0 { + reg = <0>; + marvell,rb = <0>; + marvell,nand-keep-config; + nand-on-flash-bbt; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition at 0 { + label = "U-Boot"; + reg = <0 0x800000>; + }; + partition at 800000 { + label = "Linux"; + reg = <0x800000 0x800000>; + }; + partition at 1000000 { + label = "Filesystem"; + reg = <0x1000000 0x3f000000>; + }; + }; + }; +};
arch/arm/boot/dts/armada-370-dlink-dns327l.dts +64 -55
128 lines not on this page · raw message
arch/arm/boot/dts/armada-370-mirabox.dts +29 -21
59 lines not on this page · raw message
arch/arm/boot/dts/armada-370-netgear-rn102.dts +49 -40
98 lines not on this page · raw message
arch/arm/boot/dts/armada-370-netgear-rn104.dts +49 -40
98 lines not on this page · raw message
arch/arm/boot/dts/armada-370-rd.dts +30 -21
60 lines not on this page · raw message
arch/arm/boot/dts/armada-370-seagate-nas-xbay.dtsi +36 -27
72 lines not on this page · raw message
arch/arm/boot/dts/armada-370-xp.dtsi +3 -3
@@ -281,11 +281,11 @@ status = "disabled"; }; - nand: nand at d0000 { - compatible = "marvell,armada370-nand"; + nand_controller: nand-controller at d0000 { + compatible = "marvell,armada370-nand-controller"; reg = <0xd0000 0x54>; #address-cells = <1>; - #size-cells = <1>; + #size-cells = <0>; interrupts = <113>; clocks = <&coredivclk 0>; status = "disabled";
arch/arm/boot/dts/armada-xp-db-dxbc2.dts +0 -1
@@ -109,7 +109,6 @@ status = "okay"; num-cs = <1>; marvell,nand-keep-config; - marvell,nand-enable-arbiter; nand-on-flash-bbt; nand-ecc-strength = <4>; nand-ecc-step-size = <512>;
arch/arm/boot/dts/armada-xp-db-xc3-24g4xg.dts +0 -1
@@ -108,7 +108,6 @@ status = "okay"; num-cs = <1>; marvell,nand-keep-config; - marvell,nand-enable-arbiter; nand-on-flash-bbt; nand-ecc-strength = <4>; nand-ecc-step-size = <512>;
arch/arm/boot/dts/armada-xp-db.dts +0 -1
@@ -184,7 +184,6 @@ status = "okay"; num-cs = <1>; marvell,nand-keep-config; - marvell,nand-enable-arbiter; nand-on-flash-bbt; partitions {
arch/arm/boot/dts/armada-xp-gp.dts +0 -1
@@ -201,7 +201,6 @@ status = "okay"; num-cs = <1>; marvell,nand-keep-config; - marvell,nand-enable-arbiter; nand-on-flash-bbt; }; };
arch/arm/boot/dts/armada-xp-lenovo-ix4-300d.dts +0 -1
@@ -122,7 +122,6 @@ status = "okay"; num-cs = <1>; marvell,nand-keep-config; - marvell,nand-enable-arbiter; nand-on-flash-bbt; partitions {
arch/arm/boot/dts/armada-xp-linksys-mamba.dts +82 -73
164 lines not on this page · raw message
arch/arm/boot/dts/armada-xp-netgear-rn2120.dts +49 -40
98 lines not on this page · raw message
Signature · 1 line
2.11.0
RFC 8/12 ARM: dts: armada-375: use reworked NAND controller driver
In reply to Miquel RaynalChange the bindings to use the reworked Marvell NAND controller driver. Also adapt the nand controller node organization to distinguish which property is relevant for the controller, and which one is NAND chip specific. Expose the partitions as a subnode of the NAND chip.
Remove the 'marvell,nand-enable-arbiter' property, inefective with Armada boards.
--- arch/arm/boot/dts/armada-375-db.dts | 49 ++++++++++++++++++++++--------------- arch/arm/boot/dts/armada-375.dtsi | 6 ++--- 2 files changed, 32 insertions(+), 23 deletions(-)
arch/arm/boot/dts/armada-375-db.dts +29 -20
@@ -140,28 +140,37 @@ nr-ports = <2>; }; -&nand { +&nand_controller { + status = "okay"; pinctrl-0 = <&nand_pins>; pinctrl-names = "default"; - status = "okay"; - num-cs = <1>; - marvell,nand-keep-config; - marvell,nand-enable-arbiter; - nand-on-flash-bbt; - nand-ecc-strength = <4>; - nand-ecc-step-size = <512>; - - partition at 0 { - label = "U-Boot"; - reg = <0 0x800000>; - }; - partition at 800000 { - label = "Linux"; - reg = <0x800000 0x800000>; - }; - partition at 1000000 { - label = "Filesystem"; - reg = <0x1000000 0x3f000000>; + + nand at 0 { + reg = <0>; + marvell,rb = <0>; + marvell,nand-keep-config; + nand-on-flash-bbt; + nand-ecc-strength = <4>; + nand-ecc-step-size = <512>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition at 0 { + label = "U-Boot"; + reg = <0 0x800000>; + }; + partition at 800000 { + label = "Linux"; + reg = <0x800000 0x800000>; + }; + partition at 1000000 { + label = "Filesystem"; + reg = <0x1000000 0x3f000000>; + }; + }; }; };
arch/arm/boot/dts/armada-375.dtsi +3 -3
@@ -539,11 +539,11 @@ status = "disabled"; }; - nand: nand at d0000 { - compatible = "marvell,armada370-nand"; + nand_controller: nand-controller at d0000 { + compatible = "marvell,armada370-nand-controller"; reg = <0xd0000 0x54>; #address-cells = <1>; - #size-cells = <1>; + #size-cells = <0>; interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>; clocks = <&gateclk 11>; status = "disabled";
Signature · 1 line
2.11.0
RFC 9/12 ARM: dts: armada-38x: use reworked NAND controller driver
In reply to Miquel RaynalChange the bindings to use the reworked Marvell NAND controller driver. Also adapt the nand controller node organization to distinguish which property is relevant for the controller, and which one is NAND chip specific. Expose the partitions as a subnode of the NAND chip.
Remove the 'marvell,nand-enable-arbiter' property, inefective with Armada boards.
--- arch/arm/boot/dts/armada-385-db-ap.dts | 68 ++++++------ arch/arm/boot/dts/armada-385-linksys-caiman.dts | 129 +++++++++++----------- arch/arm/boot/dts/armada-385-linksys-cobra.dts | 129 +++++++++++----------- arch/arm/boot/dts/armada-385-linksys-rango.dts | 141 ++++++++++++------------ arch/arm/boot/dts/armada-385-linksys-shelby.dts | 129 +++++++++++----------- arch/arm/boot/dts/armada-385-linksys.dtsi | 15 ++- arch/arm/boot/dts/armada-388-db.dts | 54 +++++---- arch/arm/boot/dts/armada-38x.dtsi | 6 +- 8 files changed, 353 insertions(+), 318 deletions(-)
arch/arm/boot/dts/armada-385-db-ap.dts +35 -33
@@ -166,39 +166,6 @@ status = "okay"; }; - nfc: flash at d0000 { - status = "okay"; - num-cs = <1>; - nand-ecc-strength = <4>; - nand-ecc-step-size = <512>; - marvell,nand-keep-config; - marvell,nand-enable-arbiter; - nand-on-flash-bbt; - - partitions { - compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; - - partition at 0 { - label = "U-Boot"; - reg = <0x00000000 0x00800000>; - read-only; - }; - - partition at 800000 { - label = "uImage"; - reg = <0x00800000 0x00400000>; - read-only; - }; - - partition at c00000 { - label = "Root"; - reg = <0x00c00000 0x3f400000>; - }; - }; - }; - usb3 at f0000 { status = "okay"; usb-phy = <&usb3_phy>;
@@ -263,3 +230,38 @@ spi-max-frequency = <54000000>; }; }; + +&nand_controller { + status = "okay"; + + nand at 0 { + reg = <0>; + marvell,rb = <0>; + nand-on-flash-bbt; + nand-ecc-strength = <4>; + nand-ecc-step-size = <512>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition at 0 { + label = "U-Boot"; + reg = <0x00000000 0x00800000>; + read-only; + }; + + partition at 800000 { + label = "uImage"; + reg = <0x00800000 0x00400000>; + read-only; + }; + + partition at c00000 { + label = "Root"; + reg = <0x00c00000 0x3f400000>; + }; + }; + }; +};
arch/arm/boot/dts/armada-385-linksys-caiman.dts +67 -62
134 lines not on this page · raw message
arch/arm/boot/dts/armada-385-linksys-cobra.dts +67 -62
134 lines not on this page · raw message
arch/arm/boot/dts/armada-385-linksys-rango.dts +73 -68
147 lines not on this page · raw message
arch/arm/boot/dts/armada-385-linksys-shelby.dts +67 -62
134 lines not on this page · raw message
arch/arm/boot/dts/armada-385-linksys.dtsi +10 -5
@@ -169,13 +169,18 @@ }; }; -&nand { +&nand_controller { /* 128MiB or 256MiB */ status = "okay"; - num-cs = <1>; - marvell,nand-keep-config; - marvell,nand-enable-arbiter; - nand-on-flash-bbt; + #address-cells = <1>; + #size-cells = <0>; + + nand: nand at 0 { + reg = <0>; + marvell,rb = <0>; + marvell,nand-keep-config; + nand-on-flash-bbt; + }; }; &mdio {
arch/arm/boot/dts/armada-388-db.dts +31 -23
63 lines not on this page · raw message
arch/arm/boot/dts/armada-38x.dtsi +3 -3
@@ -583,11 +583,11 @@ status = "okay"; }; - nand: flash at d0000 { - compatible = "marvell,armada370-nand"; + nand_controller: nand-controller at d0000 { + compatible = "marvell,armada370-nand-controller"; reg = <0xd0000 0x54>; #address-cells = <1>; - #size-cells = <1>; + #size-cells = <0>; interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>; clocks = <&coredivclk 0>; status = "disabled";
Signature · 1 line
2.11.0
RFC 10/12 ARM: dts: armada-39x: use reworked NAND controller driver
In reply to Miquel RaynalChange the bindings to use the reworked Marvell NAND controller driver. Also adapt the nand controller node organization to distinguish which property is relevant for the controller, and which one is NAND chip specific. Expose the partitions as a subnode of the NAND chip.
Remove the 'marvell,nand-enable-arbiter' property, inefective with Armada boards.
--- arch/arm/boot/dts/armada-390-db.dts | 65 +++++++++++++++++---------------- arch/arm/boot/dts/armada-395-gp.dts | 73 +++++++++++++++++++------------------ arch/arm/boot/dts/armada-398-db.dts | 59 +++++++++++++++++------------- arch/arm/boot/dts/armada-39x.dtsi | 6 +-- 4 files changed, 109 insertions(+), 94 deletions(-)
arch/arm/boot/dts/armada-390-db.dts +34 -31
@@ -86,37 +86,6 @@ status = "okay"; }; - flash at d0000 { - status = "okay"; - pinctrl-0 = <&nand_pins>; - pinctrl-names = "default"; - num-cs = <1>; - marvell,nand-keep-config; - marvell,nand-enable-arbiter; - nand-on-flash-bbt; - nand-ecc-strength = <8>; - nand-ecc-step-size = <512>; - - partitions { - compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; - - partition at 0 { - label = "U-Boot"; - reg = <0 0x800000>; - }; - partition at 800000 { - label = "Linux"; - reg = <0x800000 0x800000>; - }; - partition at 1000000 { - label = "Filesystem"; - reg = <0x1000000 0x3f000000>; - }; - }; - }; - /* CON98 */ usb3 at f8000 { status = "okay";
@@ -173,3 +142,37 @@ }; }; }; + +&nand_controller { + status = "okay"; + pinctrl-0 = <&nand_pins>; + pinctrl-names = "default"; + + nand at 0 { + reg = <0>; + marvell,rb = <0>; + marvell,nand-keep-config; + nand-on-flash-bbt; + nand-ecc-strength = <8>; + nand-ecc-step-size = <512>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition at 0 { + label = "U-Boot"; + reg = <0 0x800000>; + }; + partition at 800000 { + label = "Linux"; + reg = <0x800000 0x800000>; + }; + partition at 1000000 { + label = "Filesystem"; + reg = <0x1000000 0x3f000000>; + }; + }; + }; +};
arch/arm/boot/dts/armada-395-gp.dts +38 -35
82 lines not on this page · raw message
arch/arm/boot/dts/armada-398-db.dts +34 -25
68 lines not on this page · raw message
arch/arm/boot/dts/armada-39x.dtsi +3 -3
@@ -404,11 +404,11 @@ interrupts = <GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH>; }; - flash at d0000 { - compatible = "marvell,armada370-nand"; + nand_controller: nand-controller at d0000 { + compatible = "marvell,armada370-nand-controller"; reg = <0xd0000 0x54>; #address-cells = <1>; - #size-cells = <1>; + #size-cells = <0>; interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>; clocks = <&coredivclk 0>; status = "disabled";
Signature · 1 line
2.11.0
RFC 11/12 ARM: dts: pxa: use reworked NAND controller driver
In reply to Miquel RaynalChange the bindings to use the reworked Marvell NAND controller driver. Also adapt the nand controller node organization to distinguish which property is relevant for the controller, and which one is NAND chip specific. Expose the partitions as a subnode of the NAND chip.
--- arch/arm/boot/dts/pxa3xx.dtsi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
arch/arm/boot/dts/pxa3xx.dtsi +3 -3
@@ -116,15 +116,15 @@ status = "disabled"; }; - nand0: nand at 43100000 { - compatible = "marvell,pxa3xx-nand"; + nand_controller: nand-controller at 43100000 { + compatible = "marvell,pxa3xx-nand-controller"; reg = <0x43100000 90>; interrupts = <45>; clocks = <&clks CLK_NAND>; dmas = <&pdma 97 3>; dma-names = "data"; #address-cells = <1>; - #size-cells = <1>; + #size-cells = <0>; status = "disabled"; };
Signature · 1 line
2.11.0
RFC 12/12 ARM64: dts: marvell: use reworked NAND controller driver on Armada 7K/8K
In reply to Miquel RaynalChange the bindings to use the reworked Marvell NAND controller driver. Also adapt the nand controller node organization to distinguish which property is relevant for the controller, and which one is NAND chip specific. Expose the partitions as a subnode of the NAND chip.
Remove the 'marvell,nand-enable-arbiter' property, inefective with Armada boards.
--- arch/arm64/boot/dts/marvell/armada-7040-db.dts | 47 +++++++++++++--------- .../boot/dts/marvell/armada-cp110-master.dtsi | 6 +-- .../arm64/boot/dts/marvell/armada-cp110-slave.dtsi | 6 +-- 3 files changed, 34 insertions(+), 25 deletions(-)
arch/arm64/boot/dts/marvell/armada-7040-db.dts +28 -19
@@ -144,32 +144,41 @@ }; }; -&cpm_nand { +&cpm_nand_controller { /* * SPI on CPM and NAND have common pins on this board. We can - * use only one at a time. To enable the NAND (whihch will + * use only one at a time. To enable the NAND (which will * disable the SPI), the "status = "okay";" line have to be * added here. */ - num-cs = <1>; pinctrl-0 = <&nand_pins>, <&nand_rb>; pinctrl-names = "default"; - nand-ecc-strength = <4>; - nand-ecc-step-size = <512>; - marvell,nand-enable-arbiter; - nand-on-flash-bbt; - - partition at 0 { - label = "U-Boot"; - reg = <0 0x200000>; - }; - partition at 200000 { - label = "Linux"; - reg = <0x200000 0xe00000>; - }; - partition at 1000000 { - label = "Filesystem"; - reg = <0x1000000 0x3f000000>; + + nand at 0 { + reg = <0>; + marvell,rb = <0>; + nand-on-flash-bbt; + nand-ecc-strength = <4>; + nand-ecc-step-size = <512>; + + partitions { + + partition at 0 { + label = "U-Boot"; + reg = <0 0x200000>; + }; + + partition at 200000 { + label = "Linux"; + reg = <0x200000 0xe00000>; + }; + + partition at 1000000 { + label = "Filesystem"; + reg = <0x1000000 0x3f000000>; + }; + + }; }; };
arch/arm64/boot/dts/marvell/armada-cp110-master.dtsi +3 -3
@@ -268,14 +268,14 @@ status = "disabled"; }; - cpm_nand: nand at 720000 { + cpm_nand_controller: nand at 720000 { /* * Due to the limiation of the pin available * this controller is only usable on the CPM * for A7K and on the CPS for A8K. */ - compatible = "marvell,armada-8k-nand", - "marvell,armada370-nand"; + compatible = "marvell,armada-8k-nand-controller", + "marvell,armada370-nand-controller"; reg = <0x720000 0x54>; #address-cells = <1>; #size-cells = <1>;
arch/arm64/boot/dts/marvell/armada-cp110-slave.dtsi +3 -3
@@ -269,14 +269,14 @@ status = "disabled"; }; - cps_nand: nand at 720000 { + cps_nand_controller: nand at 720000 { /* * Due to the limiation of the pin available * this controller is only usable on the CPM * for A7K and on the CPS for A8K. */ - compatible = "marvell,armada370-nand", - "marvell,armada370-nand"; + compatible = "marvell,armada-8k-nand-controller", + "marvell,armada370-nand-controller"; reg = <0x720000 0x54>; #address-cells = <1>; #size-cells = <1>;
Signature · 1 line
2.11.0
A message that is not in the archive (sent off-list or not yet received): 20171018143629.29302-1-miquel.raynal-wi1+55ScJUtKEb57/[email protected]
RFC 12/12 ARM64: dts: marvell: use reworked NAND controller driver on Armada 7K/8K
Change the bindings to use the reworked Marvell NAND controller driver. Also adapt the nand controller node organization to distinguish which property is relevant for the controller, and which one is NAND chip specific. Expose the partitions as a subnode of the NAND chip.
Remove the 'marvell,nand-enable-arbiter' property, inefective with Armada boards.
--- arch/arm64/boot/dts/marvell/armada-7040-db.dts | 47 +++++++++++++--------- .../boot/dts/marvell/armada-cp110-master.dtsi | 6 +-- .../arm64/boot/dts/marvell/armada-cp110-slave.dtsi | 6 +-- 3 files changed, 34 insertions(+), 25 deletions(-)
arch/arm64/boot/dts/marvell/armada-7040-db.dts +28 -19
@@ -144,32 +144,41 @@ }; }; -&cpm_nand { +&cpm_nand_controller { /* * SPI on CPM and NAND have common pins on this board. We can - * use only one at a time. To enable the NAND (whihch will + * use only one at a time. To enable the NAND (which will * disable the SPI), the "status = "okay";" line have to be * added here. */ - num-cs = <1>; pinctrl-0 = <&nand_pins>, <&nand_rb>; pinctrl-names = "default"; - nand-ecc-strength = <4>; - nand-ecc-step-size = <512>; - marvell,nand-enable-arbiter; - nand-on-flash-bbt; - - partition@0 { - label = "U-Boot"; - reg = <0 0x200000>; - }; - partition@200000 { - label = "Linux"; - reg = <0x200000 0xe00000>; - }; - partition@1000000 { - label = "Filesystem"; - reg = <0x1000000 0x3f000000>; + + nand@0 { + reg = <0>; + marvell,rb = <0>; + nand-on-flash-bbt; + nand-ecc-strength = <4>; + nand-ecc-step-size = <512>; + + partitions { + + partition@0 { + label = "U-Boot"; + reg = <0 0x200000>; + }; + + partition@200000 { + label = "Linux"; + reg = <0x200000 0xe00000>; + }; + + partition@1000000 { + label = "Filesystem"; + reg = <0x1000000 0x3f000000>; + }; + + }; }; };
arch/arm64/boot/dts/marvell/armada-cp110-master.dtsi +3 -3
@@ -268,14 +268,14 @@ status = "disabled"; }; - cpm_nand: nand@720000 { + cpm_nand_controller: nand@720000 { /* * Due to the limiation of the pin available * this controller is only usable on the CPM * for A7K and on the CPS for A8K. */ - compatible = "marvell,armada-8k-nand", - "marvell,armada370-nand"; + compatible = "marvell,armada-8k-nand-controller", + "marvell,armada370-nand-controller"; reg = <0x720000 0x54>; #address-cells = <1>; #size-cells = <1>;
arch/arm64/boot/dts/marvell/armada-cp110-slave.dtsi +3 -3
@@ -269,14 +269,14 @@ status = "disabled"; }; - cps_nand: nand@720000 { + cps_nand_controller: nand@720000 { /* * Due to the limiation of the pin available * this controller is only usable on the CPM * for A7K and on the CPS for A8K. */ - compatible = "marvell,armada370-nand", - "marvell,armada370-nand"; + compatible = "marvell,armada-8k-nand-controller", + "marvell,armada370-nand-controller"; reg = <0x720000 0x54>; #address-cells = <1>; #size-cells = <1>;
Signature · 1 line
2.11.0
RFC 12/12 ARM64: dts: marvell: use reworked NAND controller driver on Armada 7K/8K
In reply to Miquel RaynalChange the bindings to use the reworked Marvell NAND controller driver. Also adapt the nand controller node organization to distinguish which property is relevant for the controller, and which one is NAND chip specific. Expose the partitions as a subnode of the NAND chip.
Remove the 'marvell,nand-enable-arbiter' property, inefective with Armada boards.
--- arch/arm64/boot/dts/marvell/armada-7040-db.dts | 47 +++++++++++++--------- .../boot/dts/marvell/armada-cp110-master.dtsi | 6 +-- .../arm64/boot/dts/marvell/armada-cp110-slave.dtsi | 6 +-- 3 files changed, 34 insertions(+), 25 deletions(-)
arch/arm64/boot/dts/marvell/armada-7040-db.dts +28 -19
@@ -144,32 +144,41 @@ }; }; -&cpm_nand { +&cpm_nand_controller { /* * SPI on CPM and NAND have common pins on this board. We can - * use only one at a time. To enable the NAND (whihch will + * use only one at a time. To enable the NAND (which will * disable the SPI), the "status = "okay";" line have to be * added here. */ - num-cs = <1>; pinctrl-0 = <&nand_pins>, <&nand_rb>; pinctrl-names = "default"; - nand-ecc-strength = <4>; - nand-ecc-step-size = <512>; - marvell,nand-enable-arbiter; - nand-on-flash-bbt; - - partition@0 { - label = "U-Boot"; - reg = <0 0x200000>; - }; - partition@200000 { - label = "Linux"; - reg = <0x200000 0xe00000>; - }; - partition@1000000 { - label = "Filesystem"; - reg = <0x1000000 0x3f000000>; + + nand@0 { + reg = <0>; + marvell,rb = <0>; + nand-on-flash-bbt; + nand-ecc-strength = <4>; + nand-ecc-step-size = <512>; + + partitions { + + partition@0 { + label = "U-Boot"; + reg = <0 0x200000>; + }; + + partition@200000 { + label = "Linux"; + reg = <0x200000 0xe00000>; + }; + + partition@1000000 { + label = "Filesystem"; + reg = <0x1000000 0x3f000000>; + }; + + }; }; };
arch/arm64/boot/dts/marvell/armada-cp110-master.dtsi +3 -3
@@ -268,14 +268,14 @@ status = "disabled"; }; - cpm_nand: nand@720000 { + cpm_nand_controller: nand@720000 { /* * Due to the limiation of the pin available * this controller is only usable on the CPM * for A7K and on the CPS for A8K. */ - compatible = "marvell,armada-8k-nand", - "marvell,armada370-nand"; + compatible = "marvell,armada-8k-nand-controller", + "marvell,armada370-nand-controller"; reg = <0x720000 0x54>; #address-cells = <1>; #size-cells = <1>;
arch/arm64/boot/dts/marvell/armada-cp110-slave.dtsi +3 -3
@@ -269,14 +269,14 @@ status = "disabled"; }; - cps_nand: nand@720000 { + cps_nand_controller: nand@720000 { /* * Due to the limiation of the pin available * this controller is only usable on the CPM * for A7K and on the CPS for A8K. */ - compatible = "marvell,armada370-nand", - "marvell,armada370-nand"; + compatible = "marvell,armada-8k-nand-controller", + "marvell,armada370-nand-controller"; reg = <0x720000 0x54>; #address-cells = <1>; #size-cells = <1>;
Signature · 1 line
2.11.0
RFC 12/12 ARM64: dts: marvell: use reworked NAND controller driver on Armada 7K/8K
In reply to Miquel RaynalChange the bindings to use the reworked Marvell NAND controller driver. Also adapt the nand controller node organization to distinguish which property is relevant for the controller, and which one is NAND chip specific. Expose the partitions as a subnode of the NAND chip.
Remove the 'marvell,nand-enable-arbiter' property, inefective with Armada boards.
--- arch/arm64/boot/dts/marvell/armada-7040-db.dts | 47 +++++++++++++--------- .../boot/dts/marvell/armada-cp110-master.dtsi | 6 +-- .../arm64/boot/dts/marvell/armada-cp110-slave.dtsi | 6 +-- 3 files changed, 34 insertions(+), 25 deletions(-)
arch/arm64/boot/dts/marvell/armada-7040-db.dts +28 -19
@@ -144,32 +144,41 @@ }; }; -&cpm_nand { +&cpm_nand_controller { /* * SPI on CPM and NAND have common pins on this board. We can - * use only one at a time. To enable the NAND (whihch will + * use only one at a time. To enable the NAND (which will * disable the SPI), the "status = "okay";" line have to be * added here. */ - num-cs = <1>; pinctrl-0 = <&nand_pins>, <&nand_rb>; pinctrl-names = "default"; - nand-ecc-strength = <4>; - nand-ecc-step-size = <512>; - marvell,nand-enable-arbiter; - nand-on-flash-bbt; - - partition@0 { - label = "U-Boot"; - reg = <0 0x200000>; - }; - partition@200000 { - label = "Linux"; - reg = <0x200000 0xe00000>; - }; - partition@1000000 { - label = "Filesystem"; - reg = <0x1000000 0x3f000000>; + + nand@0 { + reg = <0>; + marvell,rb = <0>; + nand-on-flash-bbt; + nand-ecc-strength = <4>; + nand-ecc-step-size = <512>; + + partitions { + + partition@0 { + label = "U-Boot"; + reg = <0 0x200000>; + }; + + partition@200000 { + label = "Linux"; + reg = <0x200000 0xe00000>; + }; + + partition@1000000 { + label = "Filesystem"; + reg = <0x1000000 0x3f000000>; + }; + + }; }; };
arch/arm64/boot/dts/marvell/armada-cp110-master.dtsi +3 -3
@@ -268,14 +268,14 @@ status = "disabled"; }; - cpm_nand: nand@720000 { + cpm_nand_controller: nand@720000 { /* * Due to the limiation of the pin available * this controller is only usable on the CPM * for A7K and on the CPS for A8K. */ - compatible = "marvell,armada-8k-nand", - "marvell,armada370-nand"; + compatible = "marvell,armada-8k-nand-controller", + "marvell,armada370-nand-controller"; reg = <0x720000 0x54>; #address-cells = <1>; #size-cells = <1>;
arch/arm64/boot/dts/marvell/armada-cp110-slave.dtsi +3 -3
@@ -269,14 +269,14 @@ status = "disabled"; }; - cps_nand: nand@720000 { + cps_nand_controller: nand@720000 { /* * Due to the limiation of the pin available * this controller is only usable on the CPM * for A7K and on the CPS for A8K. */ - compatible = "marvell,armada370-nand", - "marvell,armada370-nand"; + compatible = "marvell,armada-8k-nand-controller", + "marvell,armada370-nand-controller"; reg = <0x720000 0x54>; #address-cells = <1>; #size-cells = <1>;
Signature · 6 lines
2.11.0 ______________________________________________________ Linux MTD discussion mailing list http://lists.infradead.org/mailman/listinfo/linux-mtd/
cover Marvell NAND controller rework with ->exec_op()
In reply to Miquel RaynalHi Miquel,
Quoted 8 lines · On Wed, 18 Oct 2017 16:36:17 +0200 Miquel Raynal…
Hi,
This series adds the implementation of the NAND framework ->exec_op() interface with all the related hooks and helpers. The reasons for adding it are explained in details in the commit log "mtd: nand: add
->exec_op() implementation", but mostly it will ease later expansion of
the framework, as well as the implementation of new vendor specific commands, and should also ease driver development.
Still need to review patch 6, but I'd like to say I'm really happy to see that happen, finally. Hopefully that will help us make the NAND framework a bit easier to maintain and allow us to support advanced NAND features without having to patch all NAND controller drivers every time we add a new vendor specific operation. I also have in mind a few NAND controller drivers that have been rejected because they were not fitting in the ->cmd_ctrl() approach and I didn't want to add more drivers with their own partially implemented/broken ->cmdfunc() method.
Kudos for the work you've done, and I hope we'll get reviews from other active NAND driver maintainers/contributors. To all NAND driver
maintainers: note that the long term goal is to get rid of ->cmd_ctrl()/cmdfunc()/read/write_buf/byte/word() entirely and replace them with a single ->exec_op() hook. Please let us know if you need extra things that are not yet exposed in nand_op_instr/nand_op_parser or if you would like things to be done differently.
Thanks,
Boris
Quoted 115 lines
The series contains as well the reworked NAND controller driver from Marvell.
A lot of comments are written to explain how to use the new API and the Marvell driver shows how to implement it. A proper external documentation is being written and will later be submitted.
It also changes the device tree NAND node definition for all platforms referring to the Marvell driver to use the new bindings. They are more hierarchical and fit the real organization of the hardware, by having NAND partitions that are part of NAND chip nodes, themselves part of the NAND controller node.
See the commit log of "mtd: nand: add reworked Marvell NAND controller driver" for details about why a completely new driver is needed.
As this series changes several core functions and also directly touches multiple controller drivers, it would be great to have reviews and tests from the concerned maintainers. Only PXA3xx SoCs are not supported yet (explaining the RFC) as having a local board to test is really needed.
Thank you,
Miqu?lBoris Brezillon (2):
mtd: nand: provide several helpers to do common NAND operations
mtd: nand: force drivers to explicitly send READ/PROG commandsMiquel Raynal (10):
mtd: nand: use a staticdata_interfacein thenand_chipstructure
mtd: nand: add ->exec_op() implementation
dt-bindings: mtd: add Marvell NAND controller documentation
mtd: nand: add reworked Marvell NAND controller driver
ARM: dts: armada-370-xp: use reworked NAND controller driver
ARM: dts: armada-375: use reworked NAND controller driver
ARM: dts: armada-38x: use reworked NAND controller driver
ARM: dts: armada-39x: use reworked NAND controller driver
ARM: dts: pxa: use reworked NAND controller driver
ARM64: dts: marvell: use reworked NAND controller driver on Armada7K/8K.../devicetree/bindings/mtd/marvell-nand.txt | 95 + arch/arm/boot/dts/armada-370-db.dts | 56 +- arch/arm/boot/dts/armada-370-dlink-dns327l.dts | 119 +- arch/arm/boot/dts/armada-370-mirabox.dts | 50 +- arch/arm/boot/dts/armada-370-netgear-rn102.dts | 89 +- arch/arm/boot/dts/armada-370-netgear-rn104.dts | 89 +- arch/arm/boot/dts/armada-370-rd.dts | 51 +- arch/arm/boot/dts/armada-370-seagate-nas-xbay.dtsi | 63 +- arch/arm/boot/dts/armada-370-xp.dtsi | 6 +- arch/arm/boot/dts/armada-375-db.dts | 49 +- arch/arm/boot/dts/armada-375.dtsi | 6 +- arch/arm/boot/dts/armada-385-db-ap.dts | 68 +- arch/arm/boot/dts/armada-385-linksys-caiman.dts | 129 +- arch/arm/boot/dts/armada-385-linksys-cobra.dts | 129 +- arch/arm/boot/dts/armada-385-linksys-rango.dts | 141 +- arch/arm/boot/dts/armada-385-linksys-shelby.dts | 129 +- arch/arm/boot/dts/armada-385-linksys.dtsi | 15 +- arch/arm/boot/dts/armada-388-db.dts | 54 +- arch/arm/boot/dts/armada-38x.dtsi | 6 +- arch/arm/boot/dts/armada-390-db.dts | 65 +- arch/arm/boot/dts/armada-395-gp.dts | 73 +- arch/arm/boot/dts/armada-398-db.dts | 59 +- arch/arm/boot/dts/armada-39x.dtsi | 6 +- arch/arm/boot/dts/armada-xp-db-dxbc2.dts | 1 - arch/arm/boot/dts/armada-xp-db-xc3-24g4xg.dts | 1 - arch/arm/boot/dts/armada-xp-db.dts | 1 - arch/arm/boot/dts/armada-xp-gp.dts | 1 - arch/arm/boot/dts/armada-xp-lenovo-ix4-300d.dts | 1 - arch/arm/boot/dts/armada-xp-linksys-mamba.dts | 155 +- arch/arm/boot/dts/armada-xp-netgear-rn2120.dts | 89 +- arch/arm/boot/dts/pxa3xx.dtsi | 6 +- arch/arm64/boot/dts/marvell/armada-7040-db.dts | 47 +- .../boot/dts/marvell/armada-cp110-master.dtsi | 6 +- .../arm64/boot/dts/marvell/armada-cp110-slave.dtsi | 6 +- drivers/mtd/nand/Kconfig | 11 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/atmel/nand-controller.c | 9 +- drivers/mtd/nand/bf5xx_nand.c | 6 +- drivers/mtd/nand/brcmnand/brcmnand.c | 20 +- drivers/mtd/nand/cafe_nand.c | 20 +- drivers/mtd/nand/denali.c | 109 +- drivers/mtd/nand/diskonchip.c | 4 +- drivers/mtd/nand/docg4.c | 19 +- drivers/mtd/nand/fsl_elbc_nand.c | 6 +- drivers/mtd/nand/fsl_ifc_nand.c | 6 +- drivers/mtd/nand/fsmc_nand.c | 5 +- drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 76 +- drivers/mtd/nand/hisi504_nand.c | 9 +- drivers/mtd/nand/jz4740_nand.c | 16 +- drivers/mtd/nand/lpc32xx_mlc.c | 7 +- drivers/mtd/nand/lpc32xx_slc.c | 33 +- drivers/mtd/nand/marvell_nand.c | 2384 ++++++++++++++++++++ drivers/mtd/nand/mtk_nand.c | 21 +- drivers/mtd/nand/nand_base.c | 2020 ++++++++++++++--- drivers/mtd/nand/nand_hynix.c | 106 +- drivers/mtd/nand/nand_micron.c | 50 +- drivers/mtd/nand/nand_timings.c | 23 +- drivers/mtd/nand/omap2.c | 8 +- drivers/mtd/nand/pxa3xx_nand.c | 8 +- drivers/mtd/nand/qcom_nandc.c | 16 +- drivers/mtd/nand/r852.c | 11 +- drivers/mtd/nand/sh_flctl.c | 6 +- drivers/mtd/nand/sunxi_nand.c | 97 +- drivers/mtd/nand/tango_nand.c | 27 +- drivers/mtd/nand/tmio_nand.c | 5 +- drivers/mtd/nand/vf610_nfc.c | 6 +- drivers/staging/mt29f_spinand/mt29f_spinand.c | 7 +- include/linux/mtd/rawnand.h | 391 +++- 68 files changed, 5980 insertions(+), 1424 deletions(-) create mode 100644 Documentation/devicetree/bindings/mtd/marvell-nand.txt create mode 100644 drivers/mtd/nand/marvell_nand.c
[ Dropping all mailing lists except linux-mtd ]
Quoted 9 lines · Boris Brezillon
Still need to review patch 6, but I'd like to say I'm really happy to see that happen, finally. Hopefully that will help us make the NAND framework a bit easier to maintain and allow us to support advanced NAND features without having to patch all NAND controller drivers every time we add a new vendor specific operation. I also have in mind a few NAND controller drivers that have been rejected because they were not fitting in the ->cmd_ctrl() approach and I didn't want to add more drivers with their own partially implemented/broken ->cmdfunc() method.
Quoted 7 lines
Kudos for the work you've done, and I hope we'll get reviews from other active NAND driver maintainers/contributors. To all NAND driver
maintainers: note that the long term goal is to get rid of ->cmd_ctrl()/cmdfunc()/read/write_buf/byte/word() entirely and replace them with a single ->exec_op() hook. Please let us know if you need extra things that are not yet exposed in nand_op_instr/nand_op_parser or if you would like things to be done differently.
A long time ago, we discussed a limitation of the API for reads and writes handling only a single page at a time. My controller is able to "burst" 1-15 contiguous pages.
Does this rewrite help with such a feature, or is it orthogonal?
Regards.
Hi Marc,
Quoted 21 lines · On Thu, 19 Oct 2017 10:47:07 +0200 Marc Gonzalez…
[ Dropping all mailing lists except linux-mtd ]
Quoted 9 lines · Boris Brezillon
Still need to review patch 6, but I'd like to say I'm really happy to see that happen, finally. Hopefully that will help us make the NAND framework a bit easier to maintain and allow us to support advanced NAND features without having to patch all NAND controller drivers every time we add a new vendor specific operation. I also have in mind a few NAND controller drivers that have been rejected because they were not fitting in the ->cmd_ctrl() approach and I didn't want to add more drivers with their own partially implemented/broken ->cmdfunc() method.
Quoted 7 lines
Kudos for the work you've done, and I hope we'll get reviews from other active NAND driver maintainers/contributors. To all NAND driver
maintainers: note that the long term goal is to get rid of ->cmd_ctrl()/cmdfunc()/read/write_buf/byte/word() entirely and replace them with a single ->exec_op() hook. Please let us know if you need extra things that are not yet exposed in nand_op_instr/nand_op_parser or if you would like things to be done differently.
Quoted 5 lines
A long time ago, we discussed a limitation of the API for reads and writes handling only a single page at a time. My controller is able to "burst" 1-15 contiguous pages.
Does this rewrite help with such a feature, or is it orthogonal?
It's kind of orthogonal. To do what you want to do we'd need to extend the current nand_chip/nand_ecc_ctrl interfaces to pass the MTD request directly to the NAND controller driver and let it detect when a burst access makes sense. Right now this is not my priority, but yes, I've already thought a bit about how this interface would look like (see this presentation [1] page 31).
Regards,
Boris
[1]https://events.linuxfoundation.org/sites/events/files/slides/brezillon-nand-framework_0.pdf