[PATCH 0/5] mtd: rawnand: fsl_elbc: Device tree support

This patch series adds device tree support for P2020 NAND controller in SW ECC mode.
Pali Rohár (5): mtd: rawnand: fsl_elbc: Implement RNDOUT command mtd: rawnand: fsl_elbc: Add device tree support mtd: rawnand: fsl_elbc: Use ECC configuration from device tree mtd: nand: raw: Add support for DT property nand-ecc-algo=bch powerpc: dts: p2020: Add localbus node
arch/powerpc/dts/p2020-post.dtsi | 7 ++ arch/powerpc/dts/p2020rdb-pc.dts | 4 ++ arch/powerpc/dts/p2020rdb-pc_36b.dts | 4 ++ drivers/mtd/nand/raw/Kconfig | 4 ++ drivers/mtd/nand/raw/fsl_elbc_nand.c | 99 +++++++++++++++++++++------- drivers/mtd/nand/raw/nand_base.c | 6 ++ 6 files changed, 99 insertions(+), 25 deletions(-)

This is needed for SW ECC.
Signed-off-by: Pali Rohár pali@kernel.org --- drivers/mtd/nand/raw/fsl_elbc_nand.c | 8 ++++++++ 1 file changed, 8 insertions(+)
diff --git a/drivers/mtd/nand/raw/fsl_elbc_nand.c b/drivers/mtd/nand/raw/fsl_elbc_nand.c index ddfd75d32d06..f8698ec0158a 100644 --- a/drivers/mtd/nand/raw/fsl_elbc_nand.c +++ b/drivers/mtd/nand/raw/fsl_elbc_nand.c @@ -312,6 +312,14 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command, fsl_elbc_run_command(mtd); return;
+ /* RNDOUT moves the pointer inside the page */ + case NAND_CMD_RNDOUT: + vdbg("fsl_elbc_cmdfunc: NAND_CMD_RNDOUT, column: 0x%x.\n", + column); + + ctrl->index = column; + return; + /* READOOB reads only the OOB because no ECC is performed. */ case NAND_CMD_READOOB: vdbg("fsl_elbc_cmdfunc: NAND_CMD_READOOB, page_addr:"

This allows boards to specify NAND settings via standard DT properties.
Signed-off-by: Pali Rohár pali@kernel.org --- drivers/mtd/nand/raw/Kconfig | 4 +++ drivers/mtd/nand/raw/fsl_elbc_nand.c | 42 ++++++++++++++++++++++++++-- 2 files changed, 44 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index 0e826c192986..fb71a759591b 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -152,6 +152,10 @@ config NAND_FSL_ELBC help Enable the Freescale Enhanced Local Bus Controller FCM NAND driver.
+config NAND_FSL_ELBC_DT + bool "Support Freescale Enhanced Local Bus Controller FCM NAND driver (DT mode)" + depends on NAND_FSL_ELBC + config NAND_FSL_IFC bool "Support Freescale Integrated Flash Controller NAND driver" select TPL_SYS_NAND_SELF_INIT if TPL_NAND_SUPPORT diff --git a/drivers/mtd/nand/raw/fsl_elbc_nand.c b/drivers/mtd/nand/raw/fsl_elbc_nand.c index f8698ec0158a..f8d2bdfb1309 100644 --- a/drivers/mtd/nand/raw/fsl_elbc_nand.c +++ b/drivers/mtd/nand/raw/fsl_elbc_nand.c @@ -20,6 +20,10 @@ #include <asm/io.h> #include <linux/errno.h>
+#ifdef CONFIG_NAND_FSL_ELBC_DT +#include <dm/read.h> +#endif + #ifdef VERBOSE_DEBUG #define DEBUG_ELBC #define vdbg(format, arg...) printf("DEBUG: " format, ##arg) @@ -664,7 +668,7 @@ static void fsl_elbc_ctrl_init(void) elbc_ctrl->addr = NULL; }
-static int fsl_elbc_chip_init(int devnum, u8 *addr) +static int fsl_elbc_chip_init(int devnum, u8 *addr, ofnode flash_node) { struct mtd_info *mtd; struct nand_chip *nand; @@ -712,6 +716,8 @@ static int fsl_elbc_chip_init(int devnum, u8 *addr) elbc_ctrl->chips[priv->bank] = priv;
/* fill in nand_chip structure */ + nand->flash_node = flash_node; + /* set up function call table */ nand->read_byte = fsl_elbc_read_byte; nand->write_buf = fsl_elbc_write_buf; @@ -804,6 +810,8 @@ static int fsl_elbc_chip_init(int devnum, u8 *addr) return 0; }
+#ifndef CONFIG_NAND_FSL_ELBC_DT + #ifndef CONFIG_SYS_NAND_BASE_LIST #define CONFIG_SYS_NAND_BASE_LIST { CONFIG_SYS_NAND_BASE } #endif @@ -816,5 +824,35 @@ void board_nand_init(void) int i;
for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) - fsl_elbc_chip_init(i, (u8 *)base_address[i]); + fsl_elbc_chip_init(i, (u8 *)base_address[i], ofnode_null()); +} + +#else + +static int fsl_elbc_nand_probe(struct udevice *dev) +{ + return fsl_elbc_chip_init(0, (void *)dev_read_addr(dev), dev_ofnode(dev)); +} + +static const struct udevice_id fsl_elbc_nand_dt_ids[] = { + { .compatible = "fsl,elbc-fcm-nand", }, + {} +}; + +U_BOOT_DRIVER(fsl_elbc_nand) = { + .name = "fsl_elbc_nand", + .id = UCLASS_MTD, + .of_match = fsl_elbc_nand_dt_ids, + .probe = fsl_elbc_nand_probe, +}; + +void board_nand_init(void) +{ + struct udevice *dev; + int ret; + + ret = uclass_get_device_by_driver(UCLASS_MTD, DM_DRIVER_GET(fsl_elbc_nand), &dev); + if (ret && ret != -ENODEV) + printf("Failed to initialize fsl_elbc_nand NAND controller. (error %d)\n", ret); } +#endif

Initialize ECC configuration after nand_scan_ident() call and only in case nand_scan_ident() have not done it. nand_scan_ident() fills ECC configuration from device tree.
Fixes usage of NAND_ECC_SOFT_BCH when it is specified in device tree.
Signed-off-by: Pali Rohár pali@kernel.org --- drivers/mtd/nand/raw/fsl_elbc_nand.c | 49 +++++++++++++++------------- 1 file changed, 26 insertions(+), 23 deletions(-)
diff --git a/drivers/mtd/nand/raw/fsl_elbc_nand.c b/drivers/mtd/nand/raw/fsl_elbc_nand.c index f8d2bdfb1309..e734139b5ea5 100644 --- a/drivers/mtd/nand/raw/fsl_elbc_nand.c +++ b/drivers/mtd/nand/raw/fsl_elbc_nand.c @@ -737,36 +737,39 @@ static int fsl_elbc_chip_init(int devnum, u8 *addr, ofnode flash_node) nand->controller = &elbc_ctrl->controller; nand_set_controller_data(nand, priv);
- nand->ecc.read_page = fsl_elbc_read_page; - nand->ecc.write_page = fsl_elbc_write_page; - nand->ecc.write_subpage = fsl_elbc_write_subpage; - priv->fmr = (15 << FMR_CWTO_SHIFT) | (2 << FMR_AL_SHIFT);
- /* If CS Base Register selects full hardware ECC then use it */ - if ((br & BR_DECC) == BR_DECC_CHK_GEN) { - nand->ecc.mode = NAND_ECC_HW; - - nand->ecc.layout = (priv->fmr & FMR_ECCM) ? - &fsl_elbc_oob_sp_eccm1 : - &fsl_elbc_oob_sp_eccm0; + ret = nand_scan_ident(mtd, 1, NULL); + if (ret) + return ret;
- nand->ecc.size = 512; - nand->ecc.bytes = 3; - nand->ecc.steps = 1; - nand->ecc.strength = 1; - } else { - /* otherwise fall back to software ECC */ + /* If nand_scan_ident() has not selected ecc.mode, do it now */ + if (nand->ecc.mode == NAND_ECC_NONE) { + /* If CS Base Register selects full hardware ECC then use it */ + if ((br & BR_DECC) == BR_DECC_CHK_GEN) { + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.layout = (priv->fmr & FMR_ECCM) ? + &fsl_elbc_oob_sp_eccm1 : + &fsl_elbc_oob_sp_eccm0; + nand->ecc.size = 512; + nand->ecc.bytes = 3; + nand->ecc.steps = 1; + nand->ecc.strength = 1; + } else { + /* otherwise fall back to software ECC */ #if defined(CONFIG_NAND_ECC_BCH) - nand->ecc.mode = NAND_ECC_SOFT_BCH; + nand->ecc.mode = NAND_ECC_SOFT_BCH; #else - nand->ecc.mode = NAND_ECC_SOFT; + nand->ecc.mode = NAND_ECC_SOFT; #endif + } }
- ret = nand_scan_ident(mtd, 1, NULL); - if (ret) - return ret; + if (nand->ecc.mode == NAND_ECC_HW) { + nand->ecc.read_page = fsl_elbc_read_page; + nand->ecc.write_page = fsl_elbc_write_page; + nand->ecc.write_subpage = fsl_elbc_write_subpage; + }
/* Large-page-specific setup */ if (mtd->writesize == 2048) { @@ -785,7 +788,7 @@ static int fsl_elbc_chip_init(int devnum, u8 *addr, ofnode flash_node) priv->fmr |= FMR_ECCM;
/* adjust ecc setup if needed */ - if ((br & BR_DECC) == BR_DECC_CHK_GEN) { + if (nand->ecc.mode == NAND_ECC_HW) { nand->ecc.steps = 4; nand->ecc.layout = (priv->fmr & FMR_ECCM) ? &fsl_elbc_oob_lp_eccm1 :

According to Linux kernel DT schema nand-controller.yaml, using DT property nand-ecc-algo=bch is the correct way for specifying BCH as ECC algorithm.
Signed-off-by: Pali Rohár pali@kernel.org --- drivers/mtd/nand/raw/nand_base.c | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index f7616985d95e..e0c087f0ebaf 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -4598,6 +4598,12 @@ static int nand_dt_init(struct mtd_info *mtd, struct nand_chip *chip, ofnode nod ecc_mode = NAND_ECC_SOFT_BCH; }
+ if (ecc_mode == NAND_ECC_SOFT) { + str = ofnode_read_string(node, "nand-ecc-algo"); + if (str && !strcmp(str, "bch")) + ecc_mode = NAND_ECC_SOFT_BCH; + } + ecc_strength = ofnode_read_s32_default(node, "nand-ecc-strength", -1); ecc_step = ofnode_read_s32_default(node,

This node is required for NAND and NOR support. Node is taken from the upstream Linux kernel DTS file.
Signed-off-by: Pali Rohár pali@kernel.org --- arch/powerpc/dts/p2020-post.dtsi | 7 +++++++ arch/powerpc/dts/p2020rdb-pc.dts | 4 ++++ arch/powerpc/dts/p2020rdb-pc_36b.dts | 4 ++++ 3 files changed, 15 insertions(+)
diff --git a/arch/powerpc/dts/p2020-post.dtsi b/arch/powerpc/dts/p2020-post.dtsi index 8d30fa212c43..0a9e81a4248c 100644 --- a/arch/powerpc/dts/p2020-post.dtsi +++ b/arch/powerpc/dts/p2020-post.dtsi @@ -85,3 +85,10 @@ device_type = "pci"; bus-range = <0x0 0xff>; }; + +&lbc { + #address-cells = <2>; + #size-cells = <1>; + compatible = "fsl,p2020-elbc", "fsl,elbc", "simple-bus"; + interrupts = <19 2 0 0>; +}; diff --git a/arch/powerpc/dts/p2020rdb-pc.dts b/arch/powerpc/dts/p2020rdb-pc.dts index b37931ac4491..67fa340d09d0 100644 --- a/arch/powerpc/dts/p2020rdb-pc.dts +++ b/arch/powerpc/dts/p2020rdb-pc.dts @@ -15,6 +15,10 @@ #size-cells = <2>; interrupt-parent = <&mpic>;
+ lbc: localbus@ffe05000 { + reg = <0 0xffe05000 0 0x1000>; + }; + soc: soc@ffe00000 { ranges = <0x0 0x0 0xffe00000 0x100000>; }; diff --git a/arch/powerpc/dts/p2020rdb-pc_36b.dts b/arch/powerpc/dts/p2020rdb-pc_36b.dts index ecdc022d9974..2e8d352ecfb8 100644 --- a/arch/powerpc/dts/p2020rdb-pc_36b.dts +++ b/arch/powerpc/dts/p2020rdb-pc_36b.dts @@ -15,6 +15,10 @@ #size-cells = <2>; interrupt-parent = <&mpic>;
+ lbc: localbus@fffe05000 { + reg = <0 0xfffe05000 0 0x1000>; + }; + soc: soc@fffe00000 { ranges = <0x0 0xf 0xffe00000 0x100000>; };

This node is required for NAND and NOR support. Node is taken from the upstream Linux kernel DTS file.
Signed-off-by: Pali Rohár pali@kernel.org
--- Changes in v2: * Fix copy/paste definition error in p2020rdb-pc_36b.dts --- arch/powerpc/dts/p2020-post.dtsi | 7 +++++++ arch/powerpc/dts/p2020rdb-pc.dts | 4 ++++ arch/powerpc/dts/p2020rdb-pc_36b.dts | 4 ++++ 3 files changed, 15 insertions(+)
diff --git a/arch/powerpc/dts/p2020-post.dtsi b/arch/powerpc/dts/p2020-post.dtsi index 8d30fa212c43..0a9e81a4248c 100644 --- a/arch/powerpc/dts/p2020-post.dtsi +++ b/arch/powerpc/dts/p2020-post.dtsi @@ -85,3 +85,10 @@ device_type = "pci"; bus-range = <0x0 0xff>; }; + +&lbc { + #address-cells = <2>; + #size-cells = <1>; + compatible = "fsl,p2020-elbc", "fsl,elbc", "simple-bus"; + interrupts = <19 2 0 0>; +}; diff --git a/arch/powerpc/dts/p2020rdb-pc.dts b/arch/powerpc/dts/p2020rdb-pc.dts index b37931ac4491..67fa340d09d0 100644 --- a/arch/powerpc/dts/p2020rdb-pc.dts +++ b/arch/powerpc/dts/p2020rdb-pc.dts @@ -15,6 +15,10 @@ #size-cells = <2>; interrupt-parent = <&mpic>;
+ lbc: localbus@ffe05000 { + reg = <0 0xffe05000 0 0x1000>; + }; + soc: soc@ffe00000 { ranges = <0x0 0x0 0xffe00000 0x100000>; }; diff --git a/arch/powerpc/dts/p2020rdb-pc_36b.dts b/arch/powerpc/dts/p2020rdb-pc_36b.dts index ecdc022d9974..6f2519600313 100644 --- a/arch/powerpc/dts/p2020rdb-pc_36b.dts +++ b/arch/powerpc/dts/p2020rdb-pc_36b.dts @@ -15,6 +15,10 @@ #size-cells = <2>; interrupt-parent = <&mpic>;
+ lbc: localbus@fffe05000 { + reg = <0xf 0xffe05000 0 0x1000>; + }; + soc: soc@fffe00000 { ranges = <0x0 0xf 0xffe00000 0x100000>; };
participants (1)
-
Pali Rohár