[U-Boot] [RFC] Sunxi NAND support for U-Boot

Following up on earlier SPL patches, here a series based on Yassin Jaffer's work to bring NAND support to U-boot. RFC because I know that the sunxi nand configuration options are dependent on a work-in-progress by Daniel - trying to deliver a single SPL for both MMC and NAND boot. Given I have spent the past few weeks learning how Boris' original driver works, I have probably grown a bit blind for potentially existing issues. Please provide plenty of feedback so I can bring this in good shape.
Patches tested on an Olimex Lime with 4GB Hynix nand. Has a partition as defined in sunxi-common.h with two UBIFS partitions (boot, rootfs) set up in Linux. To me it feels self-explanatory how to set this up, but I've spent the last few weeks full-time on this, implying my view of the world and how it revolves around MTD is rather distorted. Feel free to ask directed questions if you desire assistance in setting up and/or testing. Thanks!
Roy

From: yassin yassinjaffer@gmail.com
Signed-off-by: Roy Spliet r.spliet@ultimaker.com --- include/linux/mtd/nand.h | 49 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+)
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index bc927ec..b026110 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -1008,6 +1008,55 @@ static inline int jedec_feature(struct nand_chip *chip) : 0; }
+/** + * struct nand_sdr_timings - SDR NAND chip timings + * + * This struct defines the timing requirements of a SDR NAND chip. + * These informations can be found in every NAND datasheets and the timings + * meaning are described in the ONFI specifications: + * www.onfi.org/~/media/ONFI/specs/onfi_3_1_spec.pdf (chapter 4.15 Timing + * Parameters) + * + * All these timings are expressed in picoseconds. + */ + +struct nand_sdr_timings { + u32 tALH_min; + u32 tADL_min; + u32 tALS_min; + u32 tAR_min; + u32 tCEA_max; + u32 tCEH_min; + u32 tCH_min; + u32 tCHZ_max; + u32 tCLH_min; + u32 tCLR_min; + u32 tCLS_min; + u32 tCOH_min; + u32 tCS_min; + u32 tDH_min; + u32 tDS_min; + u32 tFEAT_max; + u32 tIR_min; + u32 tITC_max; + u32 tRC_min; + u32 tREA_max; + u32 tREH_min; + u32 tRHOH_min; + u32 tRHW_min; + u32 tRHZ_max; + u32 tRLOH_min; + u32 tRP_min; + u32 tRR_min; + u64 tRST_max; + u32 tWB_max; + u32 tWC_min; + u32 tWH_min; + u32 tWHR_min; + u32 tWP_min; + u32 tWW_min; +}; + #ifdef __UBOOT__ /* Standard NAND functions from nand_base.c */ void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len);

From: yassin yassinjaffer@gmail.com
Signed-off-by: Roy Spliet r.spliet@ultimaker.com --- drivers/mtd/nand/Makefile | 2 +- drivers/mtd/nand/nand_timings.c | 252 ++++++++++++++++++++++++++++++++++++++++ include/linux/mtd/nand.h | 3 + 3 files changed, 256 insertions(+), 1 deletion(-) create mode 100644 drivers/mtd/nand/nand_timings.c
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index a0cf4d5..f194493 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -32,7 +32,7 @@ obj-y += nand_bbt.o obj-y += nand_ids.o obj-y += nand_util.o obj-y += nand_ecc.o -obj-y += nand_base.o +obj-y += nand_base.o nand_timings.o
endif # not spl
diff --git a/drivers/mtd/nand/nand_timings.c b/drivers/mtd/nand/nand_timings.c new file mode 100644 index 0000000..9e8b0a5 --- /dev/null +++ b/drivers/mtd/nand/nand_timings.c @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2014 Free Electrons + * + * Author: Boris BREZILLON boris.brezillon@free-electrons.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#include <common.h> +#include <linux/err.h> +#include <linux/mtd/nand.h> + +static const struct nand_sdr_timings onfi_sdr_timings[] = { + /* Mode 0 */ + { + .tADL_min = 200000, + .tALH_min = 20000, + .tALS_min = 50000, + .tAR_min = 25000, + .tCEA_max = 100000, + .tCEH_min = 20000, + .tCH_min = 20000, + .tCHZ_max = 100000, + .tCLH_min = 20000, + .tCLR_min = 20000, + .tCLS_min = 50000, + .tCOH_min = 0, + .tCS_min = 70000, + .tDH_min = 20000, + .tDS_min = 40000, + .tFEAT_max = 1000000, + .tIR_min = 10000, + .tITC_max = 1000000, + .tRC_min = 100000, + .tREA_max = 40000, + .tREH_min = 30000, + .tRHOH_min = 0, + .tRHW_min = 200000, + .tRHZ_max = 200000, + .tRLOH_min = 0, + .tRP_min = 50000, + .tRST_max = 250000000000ULL, + .tWB_max = 200000, + .tRR_min = 40000, + .tWC_min = 100000, + .tWH_min = 30000, + .tWHR_min = 120000, + .tWP_min = 50000, + .tWW_min = 100000, + }, + /* Mode 1 */ + { + .tADL_min = 100000, + .tALH_min = 10000, + .tALS_min = 25000, + .tAR_min = 10000, + .tCEA_max = 45000, + .tCEH_min = 20000, + .tCH_min = 10000, + .tCHZ_max = 50000, + .tCLH_min = 10000, + .tCLR_min = 10000, + .tCLS_min = 25000, + .tCOH_min = 15000, + .tCS_min = 35000, + .tDH_min = 10000, + .tDS_min = 20000, + .tFEAT_max = 1000000, + .tIR_min = 0, + .tITC_max = 1000000, + .tRC_min = 50000, + .tREA_max = 30000, + .tREH_min = 15000, + .tRHOH_min = 15000, + .tRHW_min = 100000, + .tRHZ_max = 100000, + .tRLOH_min = 0, + .tRP_min = 25000, + .tRR_min = 20000, + .tRST_max = 500000000, + .tWB_max = 100000, + .tWC_min = 45000, + .tWH_min = 15000, + .tWHR_min = 80000, + .tWP_min = 25000, + .tWW_min = 100000, + }, + /* Mode 2 */ + { + .tADL_min = 100000, + .tALH_min = 10000, + .tALS_min = 15000, + .tAR_min = 10000, + .tCEA_max = 30000, + .tCEH_min = 20000, + .tCH_min = 10000, + .tCHZ_max = 50000, + .tCLH_min = 10000, + .tCLR_min = 10000, + .tCLS_min = 15000, + .tCOH_min = 15000, + .tCS_min = 25000, + .tDH_min = 5000, + .tDS_min = 15000, + .tFEAT_max = 1000000, + .tIR_min = 0, + .tITC_max = 1000000, + .tRC_min = 35000, + .tREA_max = 25000, + .tREH_min = 15000, + .tRHOH_min = 15000, + .tRHW_min = 100000, + .tRHZ_max = 100000, + .tRLOH_min = 0, + .tRR_min = 20000, + .tRST_max = 500000000, + .tWB_max = 100000, + .tRP_min = 17000, + .tWC_min = 35000, + .tWH_min = 15000, + .tWHR_min = 80000, + .tWP_min = 17000, + .tWW_min = 100000, + }, + /* Mode 3 */ + { + .tADL_min = 100000, + .tALH_min = 5000, + .tALS_min = 10000, + .tAR_min = 10000, + .tCEA_max = 25000, + .tCEH_min = 20000, + .tCH_min = 5000, + .tCHZ_max = 50000, + .tCLH_min = 5000, + .tCLR_min = 10000, + .tCLS_min = 10000, + .tCOH_min = 15000, + .tCS_min = 25000, + .tDH_min = 5000, + .tDS_min = 10000, + .tFEAT_max = 1000000, + .tIR_min = 0, + .tITC_max = 1000000, + .tRC_min = 30000, + .tREA_max = 20000, + .tREH_min = 10000, + .tRHOH_min = 15000, + .tRHW_min = 100000, + .tRHZ_max = 100000, + .tRLOH_min = 0, + .tRP_min = 15000, + .tRR_min = 20000, + .tRST_max = 500000000, + .tWB_max = 100000, + .tWC_min = 30000, + .tWH_min = 10000, + .tWHR_min = 80000, + .tWP_min = 15000, + .tWW_min = 100000, + }, + /* Mode 4 */ + { + .tADL_min = 70000, + .tALH_min = 5000, + .tALS_min = 10000, + .tAR_min = 10000, + .tCEA_max = 25000, + .tCEH_min = 20000, + .tCH_min = 5000, + .tCHZ_max = 30000, + .tCLH_min = 5000, + .tCLR_min = 10000, + .tCLS_min = 10000, + .tCOH_min = 15000, + .tCS_min = 20000, + .tDH_min = 5000, + .tDS_min = 10000, + .tFEAT_max = 1000000, + .tIR_min = 0, + .tITC_max = 1000000, + .tRC_min = 25000, + .tREA_max = 20000, + .tREH_min = 10000, + .tRHOH_min = 15000, + .tRHW_min = 100000, + .tRHZ_max = 100000, + .tRLOH_min = 5000, + .tRP_min = 12000, + .tRR_min = 20000, + .tRST_max = 500000000, + .tWB_max = 100000, + .tWC_min = 25000, + .tWH_min = 10000, + .tWHR_min = 80000, + .tWP_min = 12000, + .tWW_min = 100000, + }, + /* Mode 5 */ + { + .tADL_min = 70000, + .tALH_min = 5000, + .tALS_min = 10000, + .tAR_min = 10000, + .tCEA_max = 25000, + .tCEH_min = 20000, + .tCH_min = 5000, + .tCHZ_max = 30000, + .tCLH_min = 5000, + .tCLR_min = 10000, + .tCLS_min = 10000, + .tCOH_min = 15000, + .tCS_min = 15000, + .tDH_min = 5000, + .tDS_min = 7000, + .tFEAT_max = 1000000, + .tIR_min = 0, + .tITC_max = 1000000, + .tRC_min = 20000, + .tREA_max = 16000, + .tREH_min = 7000, + .tRHOH_min = 15000, + .tRHW_min = 100000, + .tRHZ_max = 100000, + .tRLOH_min = 5000, + .tRP_min = 10000, + .tRR_min = 20000, + .tRST_max = 500000000, + .tWB_max = 100000, + .tWC_min = 20000, + .tWH_min = 7000, + .tWHR_min = 80000, + .tWP_min = 10000, + .tWW_min = 100000, + }, +}; + +/** + * onfi_async_timing_mode_to_sdr_timings - [NAND Interface] Retrieve NAND + * timings according to the given ONFI timing mode + * @mode: ONFI timing mode + */ +const struct nand_sdr_timings *onfi_async_timing_mode_to_sdr_timings(int mode) +{ + if (mode < 0 || mode >= ARRAY_SIZE(onfi_sdr_timings)) + return ERR_PTR(-EINVAL); + + return &onfi_sdr_timings[mode]; +} +EXPORT_SYMBOL(onfi_async_timing_mode_to_sdr_timings); diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index b026110..abda5c3 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -1057,6 +1057,9 @@ struct nand_sdr_timings { u32 tWW_min; };
+/* get timing characteristics from ONFI timing mode. */ +const struct nand_sdr_timings *onfi_async_timing_mode_to_sdr_timings(int mode); + #ifdef __UBOOT__ /* Standard NAND functions from nand_base.c */ void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len);

On Fri, 2015-06-05 at 13:52 +0200, Roy Spliet wrote:
From: yassin yassinjaffer@gmail.com
Signed-off-by: Roy Spliet r.spliet@ultimaker.com
drivers/mtd/nand/Makefile | 2 +- drivers/mtd/nand/nand_timings.c | 252 ++++++++++++++++++++++++++++++++++++++++ include/linux/mtd/nand.h | 3 + 3 files changed, 256 insertions(+), 1 deletion(-) create mode 100644 drivers/mtd/nand/nand_timings.c
This code comes from Linux and yet I see no acknowledgement of that, much less a statement of which version of Linux this was pulled from.
It would probably be better to handle this as part of a general sync with the Linux mtd code.
-Scott

Hello Scott et al.,
Op 06-06-15 om 00:02 schreef Scott Wood:
On Fri, 2015-06-05 at 13:52 +0200, Roy Spliet wrote:
From: yassin yassinjaffer@gmail.com
Signed-off-by: Roy Spliet r.spliet@ultimaker.com
drivers/mtd/nand/Makefile | 2 +- drivers/mtd/nand/nand_timings.c | 252 ++++++++++++++++++++++++++++++++++++++++ include/linux/mtd/nand.h | 3 + 3 files changed, 256 insertions(+), 1 deletion(-) create mode 100644 drivers/mtd/nand/nand_timings.c
This code comes from Linux and yet I see no acknowledgement of that, much less a statement of which version of Linux this was pulled from.
Correct, my apologies as I should have clarified that "work by Boris Brezillon" does not mean "upsteam work". The code comes from Boris' github tree[1] and contains work that was not yet brought upstream, yet is required for NAND on sunxi to work.
It would probably be better to handle this as part of a general sync with the Linux mtd code.
Ideally yes, but. In upstream Linux MTD we have a few issues to address as shown by Boris' patch-set, most importantly: - NAND chip timings (patch 1 to 3) - Randomisation support (patch 6) - Per-partition settings for ECC and randomisation (left out of my RFC, highly desirable but not strictly required for U-boot booting) - OF definition of all the above
U-Boot has the additional challenge - Partitioning is not done the upstream way, but rather in/around cmd_mtdparts
We started discussion on these topics in the #mtd IRC channel (OFTC), and several ideas have come up. The way I see it these issues can currently either be addressed by bolting/duct-taping/tie-wrapping more layers of indirection on top of upstream MTD framework, but really I feel that it might require a bit more of a structural approach that may or may not break existing drivers. The code I posted as RFC is functional. If you wish to steer towards an MTD sync-up, I highly suggest first getting these issues tackled, and then making sure that we extend the U-boot side of MTD with OF support for everything. This trajectory should include deprecating the current implementation of cmd_mtdparts and hook that command up to the "new" upstream way. In other words: now is probably not the right moment.
I'm afraid that as much as I'd like to, I will not have time to address every single one of these issues (not to mention I am hardly the expert in MTD/NAND). I'm happy to think along with designing a sustainable solution, but I will need someone to chime in when it comes to carrying solutions upstream.
Roy
-Scott
[1] https://github.com/bbrezillon/linux-sunxi/commits/sunxi-nand

On 8 June 2015 at 10:11, Roy Spliet r.spliet@ultimaker.com wrote:
Hello Scott et al.,
Op 06-06-15 om 00:02 schreef Scott Wood:
On Fri, 2015-06-05 at 13:52 +0200, Roy Spliet wrote:
From: yassin yassinjaffer@gmail.com
Signed-off-by: Roy Spliet r.spliet@ultimaker.com
drivers/mtd/nand/Makefile | 2 +- drivers/mtd/nand/nand_timings.c | 252 ++++++++++++++++++++++++++++++++++++++++ include/linux/mtd/nand.h | 3 + 3 files changed, 256 insertions(+), 1 deletion(-) create mode 100644 drivers/mtd/nand/nand_timings.c
This code comes from Linux and yet I see no acknowledgement of that, much less a statement of which version of Linux this was pulled from.
Correct, my apologies as I should have clarified that "work by Boris Brezillon" does not mean "upsteam work". The code comes from Boris' github tree[1] and contains work that was not yet brought upstream, yet is required for NAND on sunxi to work.
It would probably be better to handle this as part of a general sync with the Linux mtd code.
Ideally yes, but. In upstream Linux MTD we have a few issues to address as shown by Boris' patch-set, most importantly:
- NAND chip timings (patch 1 to 3)
- Randomisation support (patch 6)
- Per-partition settings for ECC and randomisation (left out of my RFC,
highly desirable but not strictly required for U-boot booting)
Hello,
as I understand it the ECC and randomisation settings for the bootloader part of the nand are suboptimal or unusable for ubifs so if u-boot SPL is to read the u-boot binary and later u-boot the kernel from an ubifs volume it has to support non-uniform settings. Alternatively the bootloader part can be extended to contain partitions for u-boot binary and kernel image written to raw partition without filesystem much like what Andriod usually does.
Thanks
Michal

Hello Michal,
Op 08-06-15 om 10:34 schreef Michal Suchanek:
On 8 June 2015 at 10:11, Roy Spliet r.spliet@ultimaker.com wrote:
Hello Scott et al.,
Op 06-06-15 om 00:02 schreef Scott Wood:
On Fri, 2015-06-05 at 13:52 +0200, Roy Spliet wrote:
From: yassin yassinjaffer@gmail.com
Signed-off-by: Roy Spliet r.spliet@ultimaker.com
drivers/mtd/nand/Makefile | 2 +- drivers/mtd/nand/nand_timings.c | 252 ++++++++++++++++++++++++++++++++++++++++ include/linux/mtd/nand.h | 3 + 3 files changed, 256 insertions(+), 1 deletion(-) create mode 100644 drivers/mtd/nand/nand_timings.c
This code comes from Linux and yet I see no acknowledgement of that, much less a statement of which version of Linux this was pulled from.
Correct, my apologies as I should have clarified that "work by Boris Brezillon" does not mean "upsteam work". The code comes from Boris' github tree[1] and contains work that was not yet brought upstream, yet is required for NAND on sunxi to work.
It would probably be better to handle this as part of a general sync with the Linux mtd code.
Ideally yes, but. In upstream Linux MTD we have a few issues to address as shown by Boris' patch-set, most importantly:
- NAND chip timings (patch 1 to 3)
- Randomisation support (patch 6)
- Per-partition settings for ECC and randomisation (left out of my RFC,
highly desirable but not strictly required for U-boot booting)
Hello,
as I understand it the ECC and randomisation settings for the bootloader part of the nand are suboptimal or unusable for ubifs so if u-boot SPL is to read the u-boot binary and later u-boot the kernel from an ubifs volume it has to support non-uniform settings. Alternatively the bootloader part can be extended to contain partitions for u-boot binary and kernel image written to raw partition without filesystem much like what Andriod usually does.
SPL does not read U-boot from UBI. The SPL driver is separate, much smaller and only reads in the same way BROM does. U-boot itself is not bound by the limitations imposed by BROM, which means proper randomisation and ECC settings can be used for "the UBI partition". Yours,
Roy
Thanks
Michal

On Mon, 08 Jun 2015 10:41:28 +0200 Roy Spliet r.spliet@ultimaker.com wrote:
Hello Michal,
Op 08-06-15 om 10:34 schreef Michal Suchanek:
On 8 June 2015 at 10:11, Roy Spliet r.spliet@ultimaker.com wrote:
Hello Scott et al.,
Op 06-06-15 om 00:02 schreef Scott Wood:
On Fri, 2015-06-05 at 13:52 +0200, Roy Spliet wrote:
From: yassin yassinjaffer@gmail.com
Signed-off-by: Roy Spliet r.spliet@ultimaker.com
drivers/mtd/nand/Makefile | 2 +- drivers/mtd/nand/nand_timings.c | 252 ++++++++++++++++++++++++++++++++++++++++ include/linux/mtd/nand.h | 3 + 3 files changed, 256 insertions(+), 1 deletion(-) create mode 100644 drivers/mtd/nand/nand_timings.c
This code comes from Linux and yet I see no acknowledgement of that, much less a statement of which version of Linux this was pulled from.
Correct, my apologies as I should have clarified that "work by Boris Brezillon" does not mean "upsteam work". The code comes from Boris' github tree[1] and contains work that was not yet brought upstream, yet is required for NAND on sunxi to work.
It would probably be better to handle this as part of a general sync with the Linux mtd code.
Ideally yes, but. In upstream Linux MTD we have a few issues to address as shown by Boris' patch-set, most importantly:
- NAND chip timings (patch 1 to 3)
- Randomisation support (patch 6)
- Per-partition settings for ECC and randomisation (left out of my RFC,
highly desirable but not strictly required for U-boot booting)
Hello,
as I understand it the ECC and randomisation settings for the bootloader part of the nand are suboptimal or unusable for ubifs so if u-boot SPL is to read the u-boot binary and later u-boot the kernel from an ubifs volume it has to support non-uniform settings. Alternatively the bootloader part can be extended to contain partitions for u-boot binary and kernel image written to raw partition without filesystem much like what Andriod usually does.
SPL does not read U-boot from UBI. The SPL driver is separate, much smaller and only reads in the same way BROM does. U-boot itself is not bound by the limitations imposed by BROM, which means proper randomisation and ECC settings can be used for "the UBI partition".
Yep, I agree with Roy here: we shouldn't use the BROM config except for the SPL partition. Defining the same randomizer seed for all the pages in a given block is pretty much useless since it won't prevent the reproducible pattern issue, which is one of the cause of excessive bitblits in MLC NANDs. The same goes for the ECC config used by the BROM, because in some cases it might prevent using the whole page (ECC config is stronger than required, and thus ECC bytes take more than what's available in the OOB area).

On Mon, 2015-06-08 at 10:11 +0200, Roy Spliet wrote:
Hello Scott et al.,
Op 06-06-15 om 00:02 schreef Scott Wood:
On Fri, 2015-06-05 at 13:52 +0200, Roy Spliet wrote:
From: yassin yassinjaffer@gmail.com
Signed-off-by: Roy Spliet r.spliet@ultimaker.com
drivers/mtd/nand/Makefile | 2 +- drivers/mtd/nand/nand_timings.c | 252 ++++++++++++++++++++++++++++++++++++++++ include/linux/mtd/nand.h | 3 + 3 files changed, 256 insertions(+), 1 deletion(-) create mode 100644 drivers/mtd/nand/nand_timings.c
This code comes from Linux and yet I see no acknowledgement of that, much less a statement of which version of Linux this was pulled from.
Correct, my apologies as I should have clarified that "work by Boris Brezillon" does not mean "upsteam work". The code comes from Boris' github tree[1] and contains work that was not yet brought upstream, yet is required for NAND on sunxi to work.
It would probably be better to handle this as part of a general sync with the Linux mtd code.
Ideally yes, but. In upstream Linux MTD we have a few issues to address as shown by Boris' patch-set, most importantly:
- NAND chip timings (patch 1 to 3)
Patches 1-3 are already in Linux (if there are subtle differences needed those should be followup patches, with an explanation of why it needs to differ from Linux), except that patch 3 for some reason has "onfi_timing_mode_ds" where Linux has "onfi_timing_mode_default".
I will try to do a sync soon.
U-Boot has the additional challenge
- Partitioning is not done the upstream way, but rather in/around
cmd_mtdparts
We started discussion on these topics in the #mtd IRC channel (OFTC), and several ideas have come up. The way I see it these issues can currently either be addressed by bolting/duct-taping/tie-wrapping more layers of indirection on top of upstream MTD framework, but really I feel that it might require a bit more of a structural approach that may or may not break existing drivers. The code I posted as RFC is functional. If you wish to steer towards an MTD sync-up, I highly suggest first getting these issues tackled, and then making sure that we extend the U-boot side of MTD with OF support for everything. This trajectory should include deprecating the current implementation of cmd_mtdparts and hook that command up to the "new" upstream way. In other words: now is probably not the right moment.
I don't see why those things are a prerequisite for syncing MTD from Linux to U-Boot. If there are further changes in Linux after a sync that we want to bring in, we can sync again and it will be easier because the sync will contain fewer changes.
-Scott

Hi Scott,
On June 8, 2015, 8:24 p.m., Scott Wood wrote:
<snip>
Correct, my apologies as I should have clarified that "work by Boris Brezillon" does not mean "upsteam work". The code comes from Boris' github tree[1] and contains work that was not yet brought upstream, yet is required for NAND on sunxi to work.
It would probably be better to handle this as part of a general sync with the Linux mtd code.
Ideally yes, but. In upstream Linux MTD we have a few issues to address as shown by Boris' patch-set, most importantly:
- NAND chip timings (patch 1 to 3)
Patches 1-3 are already in Linux (if there are subtle differences needed those should be followup patches, with an explanation of why it needs to differ from Linux), except that patch 3 for some reason has "onfi_timing_mode_ds" where Linux has "onfi_timing_mode_default".
I will try to do a sync soon.
If you could do that that would be great, that should at least shrink the size of this patch-set by 3 patches.
I assume that you will not be sending a pull-req for this sync for v2015.07, if you can make a git branch with the sync available somewhere for us to build on top of, then that would be great.
Thanks & Regards,
Hans

On Wed, 2015-06-10 at 10:33 +0200, Hans de Goede wrote:
Hi Scott,
On June 8, 2015, 8:24 p.m., Scott Wood wrote:
<snip>
Correct, my apologies as I should have clarified that "work by
Boris
Brezillon" does not mean "upsteam work". The code comes from Boris' github tree[1]
and
contains work that was not yet brought upstream, yet is required for NAND on
sunxi
to work.
It would probably be better to handle this as part of a general sync with the Linux mtd code.
Ideally yes, but. In upstream Linux MTD we have a few issues to address as shown by Boris' patch-set, most importantly:
- NAND chip timings (patch 1 to 3)
Patches 1-3 are already in Linux (if there are subtle differences needed those should be followup patches, with an explanation of
why it
needs to differ from Linux), except that patch 3 for some reason
has
"onfi_timing_mode_ds" where Linux has "onfi_timing_mode_default".
I will try to do a sync soon.
If you could do that that would be great, that should at least shrink the size of this patch-set by 3 patches.
I assume that you will not be sending a pull-req for this sync for v2015.07,
No, it's too late for that even if I had it ready now.
if you can make a git branch with the sync available somewhere for us to build on top of, then that would be great.
OK.
-Scott

From: yassin yassinjaffer@gmail.com
Signed-off-by: Roy Spliet r.spliet@ultimaker.com --- drivers/mtd/nand/nand_base.c | 1 + include/linux/mtd/nand.h | 7 +++++++ 2 files changed, 8 insertions(+)
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index c0e381a..dbeb092 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -3709,6 +3709,7 @@ static bool find_full_id_nand(struct mtd_info *mtd, struct nand_chip *chip, chip->options |= type->options; chip->ecc_strength_ds = NAND_ECC_STRENGTH(type); chip->ecc_step_ds = NAND_ECC_STEP(type); + chip->onfi_timing_mode_ds = type->onfi_timing_mode_ds;
*busw = type->options & NAND_BUSWIDTH_16;
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index abda5c3..0cdb3b9 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -634,6 +634,7 @@ struct nand_buffers { * @ecc_step_ds: [INTERN] ECC step required by the @ecc_strength_ds, * also from the datasheet. It is the recommended ECC step * size, if known; if unknown, set to zero. + * @onfi_timing_mode_ds:[INTERN] ONFI timing mode deduced from datasheet. * @numchips: [INTERN] number of physical chips * @chipsize: [INTERN] the size of one chip for multichip arrays * @pagemask: [INTERN] page number mask = number of (pages / chip) - 1 @@ -718,6 +719,7 @@ struct nand_chip { uint8_t bits_per_cell; uint16_t ecc_strength_ds; uint16_t ecc_step_ds; + int onfi_timing_mode_ds; int badblockpos; int badblockbits;
@@ -822,6 +824,10 @@ struct nand_chip { * @ecc_step_ds in nand_chip{}, also from the datasheet. * For example, the "4bit ECC for each 512Byte" can be set with * NAND_ECC_INFO(4, 512). + * @onfi_timing_mode_ds: the ONFI timing mode supported by this NAND chip. This + * should be deduced from timings described in the + * datasheet. + * */ struct nand_flash_dev { char *name; @@ -842,6 +848,7 @@ struct nand_flash_dev { uint16_t strength_ds; uint16_t step_ds; } ecc; + int onfi_timing_mode_ds; };
/**

On Fri, 5 Jun 2015 13:52:36 +0200 Roy Spliet r.spliet@ultimaker.com wrote:
That one has been accepted, so I guess it will be taken by Scott during his sync with the last mainline kernel.
From: yassin yassinjaffer@gmail.com
Signed-off-by: Roy Spliet r.spliet@ultimaker.com
drivers/mtd/nand/nand_base.c | 1 + include/linux/mtd/nand.h | 7 +++++++ 2 files changed, 8 insertions(+)
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index c0e381a..dbeb092 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -3709,6 +3709,7 @@ static bool find_full_id_nand(struct mtd_info *mtd, struct nand_chip *chip, chip->options |= type->options; chip->ecc_strength_ds = NAND_ECC_STRENGTH(type); chip->ecc_step_ds = NAND_ECC_STEP(type);
chip->onfi_timing_mode_ds = type->onfi_timing_mode_ds;
*busw = type->options & NAND_BUSWIDTH_16;
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index abda5c3..0cdb3b9 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -634,6 +634,7 @@ struct nand_buffers {
- @ecc_step_ds: [INTERN] ECC step required by the @ecc_strength_ds,
also from the datasheet. It is the recommended ECC step
size, if known; if unknown, set to zero.
- @onfi_timing_mode_ds:[INTERN] ONFI timing mode deduced from datasheet.
- @numchips: [INTERN] number of physical chips
- @chipsize: [INTERN] the size of one chip for multichip arrays
- @pagemask: [INTERN] page number mask = number of (pages / chip) - 1
@@ -718,6 +719,7 @@ struct nand_chip { uint8_t bits_per_cell; uint16_t ecc_strength_ds; uint16_t ecc_step_ds;
- int onfi_timing_mode_ds; int badblockpos; int badblockbits;
@@ -822,6 +824,10 @@ struct nand_chip {
@ecc_step_ds in nand_chip{}, also from the datasheet.
For example, the "4bit ECC for each 512Byte" can be set with
NAND_ECC_INFO(4, 512).
- @onfi_timing_mode_ds: the ONFI timing mode supported by this NAND chip. This
should be deduced from timings described in the
datasheet.
*/
struct nand_flash_dev { char *name; @@ -842,6 +848,7 @@ struct nand_flash_dev { uint16_t strength_ds; uint16_t step_ds; } ecc;
- int onfi_timing_mode_ds;
};
/**

From: yassin yassinjaffer@gmail.com
Signed-off-by: Roy Spliet r.spliet@ultimaker.com --- drivers/mtd/nand/nand_base.c | 154 +++++++++++++++++++++++++++++++++++++++++++ include/linux/mtd/nand.h | 21 ++++++ 2 files changed, 175 insertions(+)
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index dbeb092..1c514a0 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1185,6 +1185,138 @@ EXPORT_SYMBOL(nand_lock); #endif
/** + * nand_page_is_empty - check wether a NAND page contains only FFs + * @mtd: mtd info + * @data: data buffer + * @oob: oob buffer + * + * Reads the data stored in the databuf buffer and check if it contains only + * FFs. + * + * Return true if it does else return false. + */ +bool nand_page_is_empty(struct mtd_info *mtd, void *data, void *oob) +{ + u8 *buf; + int length; + u32 pattern = 0xffffffff; + int bitflips = 0; + int cnt; + + buf = data; + length = mtd->writesize; + while (length) { + cnt = length < sizeof(pattern) ? length : sizeof(pattern); + if (memcmp(&pattern, buf, cnt)) { + int i; + for (i = 0; i < cnt * 8; i++) { + if (!(buf[i / 8] & + (1 << (i % 8)))) { + bitflips++; + if (bitflips > mtd->ecc_strength) + return false; + } + } + } + + buf += sizeof(pattern); + length -= sizeof(pattern); + } + + buf = oob; + length = mtd->oobsize; + while (length) { + cnt = length < sizeof(pattern) ? length : sizeof(pattern); + if (memcmp(&pattern, buf, cnt)) { + int i; + for (i = 0; i < cnt * 8; i++) { + if (!(buf[i / 8] & + (1 << (i % 8)))) { + bitflips++; + if (bitflips > mtd->ecc_strength) + return false; + } + } + } + + buf += sizeof(pattern); + length -= sizeof(pattern); + } + + return true; +} +EXPORT_SYMBOL(nand_page_is_empty); + +/** + * nand_page_get_status - retrieve page status from the page status table (pst) + * @mtd: mtd info + * @page: page you want to get status on + * + * Return the page status. + */ +int nand_page_get_status(struct mtd_info *mtd, int page) +{ + struct nand_chip *chip = mtd->priv; + u8 shift = (page % 4) * 2; + uint64_t offset = page / 4; + int ret = NAND_PAGE_STATUS_UNKNOWN; + + if (chip->pst) + ret = (chip->pst[offset] >> shift) & 0x3; + + return ret; +} +EXPORT_SYMBOL(nand_page_get_status); + +/** + * nand_page_set_status - assign page status from in the page status table + * @mtd: mtd info + * @page: page you want to get status on + * @status: new status to assign + */ +void nand_page_set_status(struct mtd_info *mtd, int page, + enum nand_page_status status) +{ + struct nand_chip *chip = mtd->priv; + u8 shift; + uint64_t offset; + + if (!chip->pst) + return; + + shift = (page % 4) * 2; + offset = page / 4; + chip->pst[offset] &= ~(0x3 << shift); + chip->pst[offset] |= (status & 0x3) << shift; +} +EXPORT_SYMBOL(nand_page_set_status); + +/** + * nand_pst_create - create a page status table + * @mtd: mtd info + * + * Allocate a page status table and assign it to the mtd device. + * + * Returns 0 in case of success or -ERRNO in case of error. + */ +int nand_pst_create(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + + if (chip->pst) + return 0; + + chip->pst = kzalloc(mtd->size >> + (chip->page_shift + mtd->subpage_sft + 2), + GFP_KERNEL); + if (!chip->pst) + return -ENOMEM; + + return 0; +} +EXPORT_SYMBOL(nand_pst_create); + +/** * nand_read_page_raw - [INTERN] read raw page data without ecc * @mtd: mtd info structure * @chip: nand chip info structure @@ -2521,6 +2653,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, int bytes = mtd->writesize; int cached = writelen > bytes && page != blockmask; uint8_t *wbuf = buf; + int subpage;
WATCHDOG_RESET(); /* Partial page write? */ @@ -2547,6 +2680,14 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, if (ret) break;
+ for (subpage = column / chip->subpagesize; + subpage < (column + writelen) / chip->subpagesize; + subpage++) + nand_page_set_status(mtd, + (page << mtd->subpage_sft) + + subpage, + NAND_PAGE_FILLED); + writelen -= bytes; if (!writelen) break; @@ -2804,6 +2945,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, int page, status, pages_per_block, ret, chipnr; struct nand_chip *chip = mtd->priv; loff_t len; + int i;
pr_debug("%s: start = 0x%012llx, len = %llu\n", __func__, (unsigned long long)instr->addr, @@ -2880,6 +3022,18 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, goto erase_exit; }
+ for (i = 0; i < pages_per_block; i++) { + int subpage; + for (subpage = 0; + subpage < 1 << mtd->subpage_sft; + subpage++) { + nand_page_set_status(mtd, + ((page + i) << mtd->subpage_sft) + + subpage, + NAND_PAGE_EMPTY); + } + } + /* Increment page address and decrement length */ len -= (1ULL << chip->phys_erase_shift); page += pages_per_block; diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 0cdb3b9..ef6a783 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -547,6 +547,24 @@ struct nand_ecc_ctrl { int page); };
+/* + * Constants for page status + */ +enum nand_page_status { + NAND_PAGE_STATUS_UNKNOWN, + NAND_PAGE_EMPTY, + NAND_PAGE_FILLED, +}; + +bool nand_page_is_empty(struct mtd_info *mtd, void *data, void *oob); + +int nand_page_get_status(struct mtd_info *mtd, int page); + +void nand_page_set_status(struct mtd_info *mtd, int page, + enum nand_page_status status); + +int nand_pst_create(struct mtd_info *mtd); + /** * struct nand_buffers - buffer structure for read/write * @ecccalc: buffer pointer for calculated ECC, size is oobsize. @@ -660,6 +678,7 @@ struct nand_buffers { * @bbt_md: [REPLACEABLE] bad block table mirror descriptor * @badblock_pattern: [REPLACEABLE] bad block scan pattern used for initial * bad block scan. + * @pst: [INTERN] page status table * @controller: [REPLACEABLE] a pointer to a hardware controller * structure which is shared among multiple independent * devices. @@ -750,6 +769,8 @@ struct nand_chip {
struct nand_bbt_descr *badblock_pattern;
+ uint8_t *pst; + void *priv; };

On Fri, 5 Jun 2015 13:52:37 +0200 Roy Spliet r.spliet@ultimaker.com wrote:
Ditto (this work hasn't been accepted in Linux).
From: yassin yassinjaffer@gmail.com
Signed-off-by: Roy Spliet r.spliet@ultimaker.com
drivers/mtd/nand/nand_base.c | 154 +++++++++++++++++++++++++++++++++++++++++++ include/linux/mtd/nand.h | 21 ++++++ 2 files changed, 175 insertions(+)
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index dbeb092..1c514a0 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1185,6 +1185,138 @@ EXPORT_SYMBOL(nand_lock); #endif
/**
- nand_page_is_empty - check wether a NAND page contains only FFs
- @mtd: mtd info
- @data: data buffer
- @oob: oob buffer
- Reads the data stored in the databuf buffer and check if it contains only
- FFs.
- Return true if it does else return false.
- */
+bool nand_page_is_empty(struct mtd_info *mtd, void *data, void *oob) +{
- u8 *buf;
- int length;
- u32 pattern = 0xffffffff;
- int bitflips = 0;
- int cnt;
- buf = data;
- length = mtd->writesize;
- while (length) {
cnt = length < sizeof(pattern) ? length : sizeof(pattern);
if (memcmp(&pattern, buf, cnt)) {
int i;
for (i = 0; i < cnt * 8; i++) {
if (!(buf[i / 8] &
(1 << (i % 8)))) {
bitflips++;
if (bitflips > mtd->ecc_strength)
return false;
}
}
}
buf += sizeof(pattern);
length -= sizeof(pattern);
- }
- buf = oob;
- length = mtd->oobsize;
- while (length) {
cnt = length < sizeof(pattern) ? length : sizeof(pattern);
if (memcmp(&pattern, buf, cnt)) {
int i;
for (i = 0; i < cnt * 8; i++) {
if (!(buf[i / 8] &
(1 << (i % 8)))) {
bitflips++;
if (bitflips > mtd->ecc_strength)
return false;
}
}
}
buf += sizeof(pattern);
length -= sizeof(pattern);
- }
- return true;
+} +EXPORT_SYMBOL(nand_page_is_empty);
+/**
- nand_page_get_status - retrieve page status from the page status table (pst)
- @mtd: mtd info
- @page: page you want to get status on
- Return the page status.
- */
+int nand_page_get_status(struct mtd_info *mtd, int page) +{
- struct nand_chip *chip = mtd->priv;
- u8 shift = (page % 4) * 2;
- uint64_t offset = page / 4;
- int ret = NAND_PAGE_STATUS_UNKNOWN;
- if (chip->pst)
ret = (chip->pst[offset] >> shift) & 0x3;
- return ret;
+} +EXPORT_SYMBOL(nand_page_get_status);
+/**
- nand_page_set_status - assign page status from in the page status table
- @mtd: mtd info
- @page: page you want to get status on
- @status: new status to assign
- */
+void nand_page_set_status(struct mtd_info *mtd, int page,
enum nand_page_status status)
+{
- struct nand_chip *chip = mtd->priv;
- u8 shift;
- uint64_t offset;
- if (!chip->pst)
return;
- shift = (page % 4) * 2;
- offset = page / 4;
- chip->pst[offset] &= ~(0x3 << shift);
- chip->pst[offset] |= (status & 0x3) << shift;
+} +EXPORT_SYMBOL(nand_page_set_status);
+/**
- nand_pst_create - create a page status table
- @mtd: mtd info
- Allocate a page status table and assign it to the mtd device.
- Returns 0 in case of success or -ERRNO in case of error.
- */
+int nand_pst_create(struct mtd_info *mtd) +{
- struct nand_chip *chip = mtd->priv;
- if (chip->pst)
return 0;
- chip->pst = kzalloc(mtd->size >>
(chip->page_shift + mtd->subpage_sft + 2),
GFP_KERNEL);
- if (!chip->pst)
return -ENOMEM;
- return 0;
+} +EXPORT_SYMBOL(nand_pst_create);
+/**
- nand_read_page_raw - [INTERN] read raw page data without ecc
- @mtd: mtd info structure
- @chip: nand chip info structure
@@ -2521,6 +2653,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, int bytes = mtd->writesize; int cached = writelen > bytes && page != blockmask; uint8_t *wbuf = buf;
int subpage;
WATCHDOG_RESET(); /* Partial page write? */
@@ -2547,6 +2680,14 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, if (ret) break;
for (subpage = column / chip->subpagesize;
subpage < (column + writelen) / chip->subpagesize;
subpage++)
nand_page_set_status(mtd,
(page << mtd->subpage_sft) +
subpage,
NAND_PAGE_FILLED);
- writelen -= bytes; if (!writelen) break;
@@ -2804,6 +2945,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, int page, status, pages_per_block, ret, chipnr; struct nand_chip *chip = mtd->priv; loff_t len;
int i;
pr_debug("%s: start = 0x%012llx, len = %llu\n", __func__, (unsigned long long)instr->addr,
@@ -2880,6 +3022,18 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, goto erase_exit; }
for (i = 0; i < pages_per_block; i++) {
int subpage;
for (subpage = 0;
subpage < 1 << mtd->subpage_sft;
subpage++) {
nand_page_set_status(mtd,
((page + i) << mtd->subpage_sft) +
subpage,
NAND_PAGE_EMPTY);
}
}
- /* Increment page address and decrement length */ len -= (1ULL << chip->phys_erase_shift); page += pages_per_block;
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 0cdb3b9..ef6a783 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -547,6 +547,24 @@ struct nand_ecc_ctrl { int page); };
+/*
- Constants for page status
- */
+enum nand_page_status {
- NAND_PAGE_STATUS_UNKNOWN,
- NAND_PAGE_EMPTY,
- NAND_PAGE_FILLED,
+};
+bool nand_page_is_empty(struct mtd_info *mtd, void *data, void *oob);
+int nand_page_get_status(struct mtd_info *mtd, int page);
+void nand_page_set_status(struct mtd_info *mtd, int page,
enum nand_page_status status);
+int nand_pst_create(struct mtd_info *mtd);
/**
- struct nand_buffers - buffer structure for read/write
- @ecccalc: buffer pointer for calculated ECC, size is oobsize.
@@ -660,6 +678,7 @@ struct nand_buffers {
- @bbt_md: [REPLACEABLE] bad block table mirror descriptor
- @badblock_pattern: [REPLACEABLE] bad block scan pattern used for initial
bad block scan.
- @pst: [INTERN] page status table
- @controller: [REPLACEABLE] a pointer to a hardware controller
structure which is shared among multiple independent
devices.
@@ -750,6 +769,8 @@ struct nand_chip {
struct nand_bbt_descr *badblock_pattern;
- uint8_t *pst;
- void *priv;
};

From: yassin yassinjaffer@gmail.com
Signed-off-by: Roy Spliet r.spliet@ultimaker.com --- drivers/mtd/nand/nand_base.c | 101 ++++++++++++++++++++++++++----------------- 1 file changed, 61 insertions(+), 40 deletions(-)
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 1c514a0..83586cc 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -4114,47 +4114,15 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips, } EXPORT_SYMBOL(nand_scan_ident);
- -/** - * nand_scan_tail - [NAND Interface] Scan for the NAND device - * @mtd: MTD device structure - * - * This is the second phase of the normal nand_scan() function. It fills out - * all the uninitialized function pointers with the defaults and scans for a - * bad block table if appropriate. +/* + * Initialize ECC struct: + * - fill ECC struct with default function/values when these ones are undefined + * - fill ECC infos based on MTD device */ -int nand_scan_tail(struct mtd_info *mtd) +static int nand_ecc_ctrl_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc) { int i; - struct nand_chip *chip = mtd->priv; - struct nand_ecc_ctrl *ecc = &chip->ecc; - struct nand_buffers *nbuf;
- /* New bad blocks should be marked in OOB, flash-based BBT, or both */ - BUG_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) && - !(chip->bbt_options & NAND_BBT_USE_FLASH)); - - if (!(chip->options & NAND_OWN_BUFFERS)) { -#ifndef __UBOOT__ - nbuf = kzalloc(sizeof(*nbuf) + mtd->writesize - + mtd->oobsize * 3, GFP_KERNEL); - if (!nbuf) - return -ENOMEM; - nbuf->ecccalc = (uint8_t *)(nbuf + 1); - nbuf->ecccode = nbuf->ecccalc + mtd->oobsize; - nbuf->databuf = nbuf->ecccode + mtd->oobsize; -#else - nbuf = kzalloc(sizeof(struct nand_buffers), GFP_KERNEL); -#endif - - chip->buffers = nbuf; - } else { - if (!chip->buffers) - return -ENOMEM; - } - - /* Set the internal oob buffer location, just after the page data */ - chip->oob_poi = chip->buffers->databuf + mtd->writesize;
/* * If no default placement scheme is given, select an appropriate one. @@ -4180,9 +4148,6 @@ int nand_scan_tail(struct mtd_info *mtd) } }
- if (!chip->write_page) - chip->write_page = nand_write_page; - /* * Check ECC mode, default to software if 3byte/512byte hardware ECC is * selected and we have 256 byte pagesize fallback to software ECC @@ -4349,6 +4314,62 @@ int nand_scan_tail(struct mtd_info *mtd) } ecc->total = ecc->steps * ecc->bytes;
+ return 0; +} + +/** + * nand_scan_tail - [NAND Interface] Scan for the NAND device + * @mtd: MTD device structure + * + * This is the second phase of the normal nand_scan() function. It fills out + * all the uninitialized function pointers with the defaults and scans for a + * bad block table if appropriate. + */ +int nand_scan_tail(struct mtd_info *mtd) +{ + int ret; + struct nand_chip *chip = mtd->priv; + struct nand_ecc_ctrl *ecc = &chip->ecc; + struct nand_buffers *nbuf; + + /* New bad blocks should be marked in OOB, flash-based BBT, or both */ + BUG_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) && + !(chip->bbt_options & NAND_BBT_USE_FLASH)); + + if (!(chip->options & NAND_OWN_BUFFERS)) { +#ifndef __UBOOT__ + nbuf = kzalloc(sizeof(*nbuf) + mtd->writesize + + mtd->oobsize * 3, GFP_KERNEL); + if (!nbuf) + return -ENOMEM; + nbuf->ecccalc = (uint8_t *)(nbuf + 1); + nbuf->ecccode = nbuf->ecccalc + mtd->oobsize; + nbuf->databuf = nbuf->ecccode + mtd->oobsize; +#else + nbuf = kzalloc(sizeof(struct nand_buffers), GFP_KERNEL); +#endif + + chip->buffers = nbuf; + } else { + if (!chip->buffers) + return -ENOMEM; + } + + /* Set the internal oob buffer location, just after the page data */ + chip->oob_poi = chip->buffers->databuf + mtd->writesize; + + if (!chip->write_page) + chip->write_page = nand_write_page; + + /* Initialize ECC struct */ + ret = nand_ecc_ctrl_init(mtd, ecc); + if (ret) { + if (!(chip->options & NAND_OWN_BUFFERS)) + kfree(chip->buffers); + + return ret; + } + /* Allow subpage writes up to ecc.steps. Not possible for MLC flash */ if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && nand_is_slc(chip)) { switch (ecc->steps) {

On Fri, 5 Jun 2015 13:52:38 +0200 Roy Spliet r.spliet@ultimaker.com wrote:
Ditto (work not mainlined yet, so we'd better either get rid of it). BTW, a commit message would help understanding what you're doing in this patch (even if I'm probably the one who omit the commit message in the first place, that doesn't mean you should do the same ;-))
From: yassin yassinjaffer@gmail.com
Signed-off-by: Roy Spliet r.spliet@ultimaker.com
drivers/mtd/nand/nand_base.c | 101 ++++++++++++++++++++++++++----------------- 1 file changed, 61 insertions(+), 40 deletions(-)
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 1c514a0..83586cc 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -4114,47 +4114,15 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips, } EXPORT_SYMBOL(nand_scan_ident);
-/**
- nand_scan_tail - [NAND Interface] Scan for the NAND device
- @mtd: MTD device structure
- This is the second phase of the normal nand_scan() function. It fills out
- all the uninitialized function pointers with the defaults and scans for a
- bad block table if appropriate.
+/*
- Initialize ECC struct:
- fill ECC struct with default function/values when these ones are undefined
*/
- fill ECC infos based on MTD device
-int nand_scan_tail(struct mtd_info *mtd) +static int nand_ecc_ctrl_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc) { int i;
struct nand_chip *chip = mtd->priv;
struct nand_ecc_ctrl *ecc = &chip->ecc;
struct nand_buffers *nbuf;
/* New bad blocks should be marked in OOB, flash-based BBT, or both */
BUG_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) &&
!(chip->bbt_options & NAND_BBT_USE_FLASH));
if (!(chip->options & NAND_OWN_BUFFERS)) {
-#ifndef __UBOOT__
nbuf = kzalloc(sizeof(*nbuf) + mtd->writesize
+ mtd->oobsize * 3, GFP_KERNEL);
if (!nbuf)
return -ENOMEM;
nbuf->ecccalc = (uint8_t *)(nbuf + 1);
nbuf->ecccode = nbuf->ecccalc + mtd->oobsize;
nbuf->databuf = nbuf->ecccode + mtd->oobsize;
-#else
nbuf = kzalloc(sizeof(struct nand_buffers), GFP_KERNEL);
-#endif
chip->buffers = nbuf;
} else {
if (!chip->buffers)
return -ENOMEM;
}
/* Set the internal oob buffer location, just after the page data */
chip->oob_poi = chip->buffers->databuf + mtd->writesize;
/*
- If no default placement scheme is given, select an appropriate one.
@@ -4180,9 +4148,6 @@ int nand_scan_tail(struct mtd_info *mtd) } }
- if (!chip->write_page)
chip->write_page = nand_write_page;
- /*
- Check ECC mode, default to software if 3byte/512byte hardware ECC is
- selected and we have 256 byte pagesize fallback to software ECC
@@ -4349,6 +4314,62 @@ int nand_scan_tail(struct mtd_info *mtd) } ecc->total = ecc->steps * ecc->bytes;
- return 0;
+}
+/**
- nand_scan_tail - [NAND Interface] Scan for the NAND device
- @mtd: MTD device structure
- This is the second phase of the normal nand_scan() function. It fills out
- all the uninitialized function pointers with the defaults and scans for a
- bad block table if appropriate.
- */
+int nand_scan_tail(struct mtd_info *mtd) +{
- int ret;
- struct nand_chip *chip = mtd->priv;
- struct nand_ecc_ctrl *ecc = &chip->ecc;
- struct nand_buffers *nbuf;
- /* New bad blocks should be marked in OOB, flash-based BBT, or both */
- BUG_ON((chip->bbt_options & NAND_BBT_NO_OOB_BBM) &&
!(chip->bbt_options & NAND_BBT_USE_FLASH));
- if (!(chip->options & NAND_OWN_BUFFERS)) {
+#ifndef __UBOOT__
nbuf = kzalloc(sizeof(*nbuf) + mtd->writesize
+ mtd->oobsize * 3, GFP_KERNEL);
if (!nbuf)
return -ENOMEM;
nbuf->ecccalc = (uint8_t *)(nbuf + 1);
nbuf->ecccode = nbuf->ecccalc + mtd->oobsize;
nbuf->databuf = nbuf->ecccode + mtd->oobsize;
+#else
nbuf = kzalloc(sizeof(struct nand_buffers), GFP_KERNEL);
+#endif
chip->buffers = nbuf;
- } else {
if (!chip->buffers)
return -ENOMEM;
- }
- /* Set the internal oob buffer location, just after the page data */
- chip->oob_poi = chip->buffers->databuf + mtd->writesize;
- if (!chip->write_page)
chip->write_page = nand_write_page;
- /* Initialize ECC struct */
- ret = nand_ecc_ctrl_init(mtd, ecc);
- if (ret) {
if (!(chip->options & NAND_OWN_BUFFERS))
kfree(chip->buffers);
return ret;
- }
- /* Allow subpage writes up to ecc.steps. Not possible for MLC flash */ if (!(chip->options & NAND_NO_SUBPAGE_WRITE) && nand_is_slc(chip)) { switch (ecc->steps) {

Based on BBrezillons work, minus per-partition support. Changes to support that would be quite invasive while it hasn't been solved yet for Linux.
Signed-off-by: Roy Spliet r.spliet@ultimaker.com --- drivers/mtd/nand/nand_base.c | 255 ++++++++++++++++++++++++++++++++++--------- include/linux/mtd/nand.h | 96 ++++++++++++++++ 2 files changed, 299 insertions(+), 52 deletions(-)
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 83586cc..5196c0c 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1185,6 +1185,62 @@ EXPORT_SYMBOL(nand_lock); #endif
/** + * nand_rnd_is_activ - check wether a region of a NAND page requires NAND + * randomizer to be disabled + * @mtd: mtd info + * @page: NAND page + * @column: offset within the page + * @len: len of the region + * + * Returns 1 if the randomizer should be enabled, 0 if not, or -ERR in case of + * error. + * + * In case of success len will contain the size of the region: + * - if the requested region fits in a NAND random region len will not change + * - else len will be replaced by the available length within the NAND random + * region + */ +int nand_rnd_is_activ(struct mtd_info *mtd, int page, int column, int *len) +{ + struct nand_chip *chip = mtd->priv; + struct nand_rnd_layout *layout = chip->rnd.layout; + struct nand_rndfree *range; + int ret = 1; + int tmp; + int i; + + if (!len || *len < 0 || column < 0 || + column + *len > mtd->writesize + mtd->oobsize) + return -EINVAL; + + if (layout) { + for (i = 0; i < layout->nranges; i++) { + range = &layout->ranges[i]; + if (column + *len <= range->offset) { + break; + } else if (column >= range->offset + range->length) { + continue; + } else if (column < range->offset) { + tmp = range->offset - column; + if (*len > tmp) + *len = tmp; + break; + } else { + tmp = range->offset + range->length - column; + if (*len > tmp) + *len = tmp; + ret = 0; + break; + } + + } + } + + return ret; +} +EXPORT_SYMBOL(nand_rnd_is_activ); + +/** * nand_page_is_empty - check wether a NAND page contains only FFs * @mtd: mtd info * @data: data buffer @@ -1329,9 +1385,13 @@ EXPORT_SYMBOL(nand_pst_create); static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { - chip->read_buf(mtd, buf, mtd->writesize); - if (oob_required) - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + nand_rnd_config(mtd, page, 0, NAND_RND_READ); + nand_rnd_read_buf(mtd, buf, mtd->writesize); + if (oob_required){ + nand_rnd_config(mtd, page, mtd->writesize, NAND_RND_READ); + nand_rnd_read_buf(mtd, chip->oob_poi, mtd->oobsize); + } + nand_rnd_config(mtd, -1, -1, NAND_RND_READ); return 0; }
@@ -1352,29 +1412,40 @@ static int nand_read_page_raw_syndrome(struct mtd_info *mtd, int eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; uint8_t *oob = chip->oob_poi; - int steps, size; + int steps, size, column = 0;
for (steps = chip->ecc.steps; steps > 0; steps--) { - chip->read_buf(mtd, buf, eccsize); + nand_rnd_config(mtd, page, column, NAND_RND_READ); + nand_rnd_read_buf(mtd, buf, eccsize); buf += eccsize; + column += eccsize;
if (chip->ecc.prepad) { - chip->read_buf(mtd, oob, chip->ecc.prepad); + nand_rnd_config(mtd, page, column, NAND_RND_READ); + nand_rnd_read_buf(mtd, oob, chip->ecc.prepad); oob += chip->ecc.prepad; + column += chip->ecc.prepad; }
- chip->read_buf(mtd, oob, eccbytes); + nand_rnd_config(mtd, page, column, NAND_RND_READ); + nand_rnd_read_buf(mtd, oob, eccbytes); oob += eccbytes; + column += eccbytes;
if (chip->ecc.postpad) { - chip->read_buf(mtd, oob, chip->ecc.postpad); + nand_rnd_config(mtd, page, column, NAND_RND_READ); + nand_rnd_read_buf(mtd, oob, chip->ecc.postpad); oob += chip->ecc.postpad; + column += chip->ecc.postpad; } }
size = mtd->oobsize - (oob - chip->oob_poi); - if (size) - chip->read_buf(mtd, oob, size); + if (size) { + nand_rnd_config(mtd, page, column, NAND_RND_READ); + nand_rnd_read_buf(mtd, oob, size); + } + nand_rnd_config(mtd, -1, -1, NAND_RND_READ);
return 0; } @@ -1462,7 +1533,8 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_col_addr, -1);
p = bufpoi + data_col_addr; - chip->read_buf(mtd, p, datafrag_len); + nand_rnd_config(mtd, -1, data_col_addr, NAND_RND_READ); + nand_rnd_read_buf(mtd, p, datafrag_len);
/* Calculate ECC */ for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) @@ -1481,7 +1553,8 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, } if (gaps) { chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1); - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + nand_rnd_config(mtd, -1, mtd->writesize, NAND_RND_READ); + nand_rnd_read_buf(mtd, chip->oob_poi, mtd->oobsize); } else { /* * Send the command to read the particular ECC bytes take care @@ -1496,7 +1569,8 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize + aligned_pos, -1); - chip->read_buf(mtd, &chip->oob_poi[aligned_pos], aligned_len); + nand_rnd_config(mtd, -1, mtd->writesize + aligned_pos, NAND_RND_READ); + nand_rnd_read_buf(mtd, &chip->oob_poi[aligned_pos], aligned_len); }
for (i = 0; i < eccfrag_len; i++) @@ -1515,6 +1589,8 @@ static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, max_bitflips = max_t(unsigned int, max_bitflips, stat); } } + nand_rnd_config(mtd, -1, -1, NAND_RND_READ); + return max_bitflips; }
@@ -1539,13 +1615,17 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *ecc_code = chip->buffers->ecccode; uint32_t *eccpos = chip->ecc.layout->eccpos; unsigned int max_bitflips = 0; + int column = 0;
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { chip->ecc.hwctl(mtd, NAND_ECC_READ); - chip->read_buf(mtd, p, eccsize); + nand_rnd_config(mtd, page, column, NAND_RND_READ); + nand_rnd_read_buf(mtd, p, eccsize); chip->ecc.calculate(mtd, p, &ecc_calc[i]); + column += eccsize; } - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + nand_rnd_config(mtd, page, column, NAND_RND_READ); + nand_rnd_read_buf(mtd, chip->oob_poi, mtd->oobsize);
for (i = 0; i < chip->ecc.total; i++) ecc_code[i] = chip->oob_poi[eccpos[i]]; @@ -1564,6 +1644,8 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, max_bitflips = max_t(unsigned int, max_bitflips, stat); } } + nand_rnd_config(mtd, -1, -1, NAND_RND_READ); + return max_bitflips; }
@@ -1592,11 +1674,14 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, uint32_t *eccpos = chip->ecc.layout->eccpos; uint8_t *ecc_calc = chip->buffers->ecccalc; unsigned int max_bitflips = 0; + int column = 0;
/* Read the OOB area first */ chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); - chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + nand_rnd_config(mtd, page, mtd->writesize, NAND_RND_READ); + nand_rnd_read_buf(mtd, chip->oob_poi, mtd->oobsize); chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); + column = 0;
for (i = 0; i < chip->ecc.total; i++) ecc_code[i] = chip->oob_poi[eccpos[i]]; @@ -1605,7 +1690,8 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, int stat;
chip->ecc.hwctl(mtd, NAND_ECC_READ); - chip->read_buf(mtd, p, eccsize); + nand_rnd_config(mtd, page, column, NAND_RND_READ); + nand_rnd_read_buf(mtd, p, eccsize); chip->ecc.calculate(mtd, p, &ecc_calc[i]);
stat = chip->ecc.correct(mtd, p, &ecc_code[i], NULL); @@ -1616,6 +1702,8 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, max_bitflips = max_t(unsigned int, max_bitflips, stat); } } + nand_rnd_config(mtd, -1, -1, NAND_RND_READ); + return max_bitflips; }
@@ -1639,20 +1727,26 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *p = buf; uint8_t *oob = chip->oob_poi; unsigned int max_bitflips = 0; + int column = 0;
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { int stat;
chip->ecc.hwctl(mtd, NAND_ECC_READ); - chip->read_buf(mtd, p, eccsize); + nand_rnd_config(mtd, page, column, NAND_RND_READ); + nand_rnd_read_buf(mtd, p, eccsize); + column += eccsize;
if (chip->ecc.prepad) { - chip->read_buf(mtd, oob, chip->ecc.prepad); + nand_rnd_config(mtd, page, column, NAND_RND_READ); + nand_rnd_read_buf(mtd, oob, chip->ecc.prepad); oob += chip->ecc.prepad; }
chip->ecc.hwctl(mtd, NAND_ECC_READSYN); - chip->read_buf(mtd, oob, eccbytes); + nand_rnd_config(mtd, page, column, NAND_RND_READ); + nand_rnd_read_buf(mtd, oob, eccbytes); + column += eccbytes; stat = chip->ecc.correct(mtd, p, oob, NULL);
if (stat < 0) { @@ -1665,15 +1759,20 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, oob += eccbytes;
if (chip->ecc.postpad) { - chip->read_buf(mtd, oob, chip->ecc.postpad); + nand_rnd_config(mtd, page, column, NAND_RND_READ); + nand_rnd_read_buf(mtd, oob, chip->ecc.postpad); + column += chip->ecc.postpad; oob += chip->ecc.postpad; } }
/* Calculate remaining oob bytes */ i = mtd->oobsize - (oob - chip->oob_poi); - if (i) - chip->read_buf(mtd, oob, i); + if (i) { + nand_rnd_config(mtd, page, column, NAND_RND_READ); + nand_rnd_read_buf(mtd, oob, i); + } + nand_rnd_config(mtd, -1, -1, NAND_RND_READ);
return max_bitflips; } @@ -1685,9 +1784,11 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip, * @ops: oob ops structure * @len: size of oob to transfer */ -static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob, +static uint8_t *nand_transfer_oob(struct mtd_info *mtd, uint8_t *oob, struct mtd_oob_ops *ops, size_t len) { + struct nand_chip *chip = mtd->priv; + switch (ops->mode) {
case MTD_OPS_PLACE_OOB: @@ -1804,6 +1905,7 @@ read_retry: * Now read the page into the buffer. Absent an error, * the read methods return max bitflips per ecc step. */ + nand_rnd_config(mtd, page, -1, NAND_RND_READ); if (unlikely(ops->mode == MTD_OPS_RAW)) ret = chip->ecc.read_page_raw(mtd, chip, bufpoi, oob_required, @@ -1816,6 +1918,8 @@ read_retry: else ret = chip->ecc.read_page(mtd, chip, bufpoi, oob_required, page); + nand_rnd_config(mtd, -1, -1, NAND_RND_READ); + if (ret < 0) { if (!aligned) /* Invalidate page cache */ @@ -1843,7 +1947,7 @@ read_retry: int toread = min(oobreadlen, max_oobsize);
if (toread) { - oob = nand_transfer_oob(chip, + oob = nand_transfer_oob(mtd, oob, ops, toread); oobreadlen -= toread; } @@ -1960,7 +2064,9 @@ static int nand_read_oob_std(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); + nand_rnd_config(mtd, page, mtd->writesize, NAND_RND_READ); + nand_rnd_read_buf(mtd, chip->oob_poi, mtd->oobsize); + nand_rnd_config(mtd, -1, -1, NAND_RND_READ); return 0; }
@@ -1979,7 +2085,7 @@ static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad; int eccsize = chip->ecc.size; uint8_t *bufpoi = buf; - int i, toread, sndrnd = 0, pos; + int i, toread, sndrnd = 0, pos = eccsize;
chip->cmdfunc(mtd, NAND_CMD_READ0, chip->ecc.size, page); for (i = 0; i < chip->ecc.steps; i++) { @@ -1992,12 +2098,17 @@ static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip, } else sndrnd = 1; toread = min_t(int, length, chunk); - chip->read_buf(mtd, bufpoi, toread); + nand_rnd_config(mtd, page, pos, NAND_RND_READ); + nand_rnd_read_buf(mtd, bufpoi, toread); bufpoi += toread; length -= toread; } - if (length > 0) - chip->read_buf(mtd, bufpoi, length); + if (length > 0){ + pos = mtd->writesize + mtd->oobsize - length; + nand_rnd_config(mtd, page, pos, NAND_RND_READ); + nand_rnd_read_buf(mtd, bufpoi, length); + } + nand_rnd_config(mtd, -1, -1, NAND_RND_READ);
return 0; } @@ -2016,7 +2127,9 @@ static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip, int length = mtd->oobsize;
chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page); - chip->write_buf(mtd, buf, length); + nand_rnd_config(mtd, page, mtd->writesize, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, buf, length); + nand_rnd_config(mtd, -1, -1, NAND_RND_WRITE); /* Send command to program the OOB data */ chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
@@ -2071,12 +2184,18 @@ static int nand_write_oob_syndrome(struct mtd_info *mtd, } else sndcmd = 1; len = min_t(int, length, chunk); - chip->write_buf(mtd, bufpoi, len); + nand_rnd_config(mtd, page, pos, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, bufpoi, len); bufpoi += len; length -= len; } - if (length > 0) - chip->write_buf(mtd, bufpoi, length); + + if (length > 0){ + pos = mtd->writesize + mtd->oobsize - length; + nand_rnd_config(mtd, page, pos, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, bufpoi, length); + } + nand_rnd_config(mtd, -1, -1, NAND_RND_WRITE);
chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1); status = chip->waitfunc(mtd, chip); @@ -2147,7 +2266,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, break;
len = min(len, readlen); - buf = nand_transfer_oob(chip, buf, ops, len); + buf = nand_transfer_oob(mtd, buf, ops, len);
if (chip->options & NAND_NEED_READRDY) { /* Apply delay or wait for ready/busy pin */ @@ -2265,29 +2384,39 @@ static int nand_write_page_raw_syndrome(struct mtd_info *mtd, int eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; uint8_t *oob = chip->oob_poi; - int steps, size; + int steps, size, column = 0;
for (steps = chip->ecc.steps; steps > 0; steps--) { - chip->write_buf(mtd, buf, eccsize); + nand_rnd_config(mtd, -1, column, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, buf, eccsize); buf += eccsize; + column += eccsize;
if (chip->ecc.prepad) { - chip->write_buf(mtd, oob, chip->ecc.prepad); + nand_rnd_config(mtd, -1, column, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, oob, chip->ecc.prepad); oob += chip->ecc.prepad; + column += chip->ecc.prepad; }
- chip->write_buf(mtd, oob, eccbytes); + nand_rnd_config(mtd, -1, column, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, oob, eccbytes); oob += eccbytes; + column += eccbytes;
if (chip->ecc.postpad) { - chip->write_buf(mtd, oob, chip->ecc.postpad); + nand_rnd_config(mtd, -1, column, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, oob, chip->ecc.postpad); oob += chip->ecc.postpad; + column += chip->ecc.postpad; } }
size = mtd->oobsize - (oob - chip->oob_poi); - if (size) - chip->write_buf(mtd, oob, size); + if (size) { + nand_rnd_config(mtd, -1, column, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, oob, size); + }
return 0; } @@ -2334,17 +2463,21 @@ static int nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *ecc_calc = chip->buffers->ecccalc; const uint8_t *p = buf; uint32_t *eccpos = chip->ecc.layout->eccpos; + int column = 0;
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { chip->ecc.hwctl(mtd, NAND_ECC_WRITE); - chip->write_buf(mtd, p, eccsize); + nand_rnd_config(mtd, -1, column, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, p, eccsize); chip->ecc.calculate(mtd, p, &ecc_calc[i]); + column += eccsize; }
for (i = 0; i < chip->ecc.total; i++) chip->oob_poi[eccpos[i]] = ecc_calc[i];
- chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + nand_rnd_config(mtd, -1, column, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, chip->oob_poi, mtd->oobsize);
return 0; } @@ -2380,7 +2513,10 @@ static int nand_write_subpage_hwecc(struct mtd_info *mtd, chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
/* write data (untouched subpages already masked by 0xFF) */ - chip->write_buf(mtd, buf, ecc_size); + nand_rnd_config(mtd, -1, offset, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, buf, ecc_size); + offset += ecc_size; +
/* mask ECC of un-touched subpages by padding 0xFF */ if ((step < start_step) || (step > end_step)) @@ -2405,7 +2541,8 @@ static int nand_write_subpage_hwecc(struct mtd_info *mtd, chip->oob_poi[eccpos[i]] = ecc_calc[i];
/* write OOB buffer to NAND device */ - chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); + nand_rnd_config(mtd, -1, offset, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, chip->oob_poi, mtd->oobsize);
return 0; } @@ -2430,31 +2567,42 @@ static int nand_write_page_syndrome(struct mtd_info *mtd, int eccsteps = chip->ecc.steps; const uint8_t *p = buf; uint8_t *oob = chip->oob_poi; + int column = 0;
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
chip->ecc.hwctl(mtd, NAND_ECC_WRITE); - chip->write_buf(mtd, p, eccsize); + nand_rnd_config(mtd, -1, column, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, p, eccsize); + column += eccsize;
if (chip->ecc.prepad) { - chip->write_buf(mtd, oob, chip->ecc.prepad); + nand_rnd_config(mtd, -1, column, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, oob, chip->ecc.prepad); oob += chip->ecc.prepad; + column += chip->ecc.prepad; }
chip->ecc.calculate(mtd, p, oob); - chip->write_buf(mtd, oob, eccbytes); + nand_rnd_config(mtd, -1, column, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, oob, eccbytes); oob += eccbytes; + column += eccbytes;
if (chip->ecc.postpad) { - chip->write_buf(mtd, oob, chip->ecc.postpad); + nand_rnd_config(mtd, -1, column, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, oob, chip->ecc.postpad); oob += chip->ecc.postpad; + column += chip->ecc.postpad; } }
/* Calculate remaining oob bytes */ i = mtd->oobsize - (oob - chip->oob_poi); - if (i) - chip->write_buf(mtd, oob, i); + if (i) { + nand_rnd_config(mtd, -1, column, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, oob, i); + }
return 0; } @@ -2485,6 +2633,7 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
+ nand_rnd_config(mtd, page, 0, NAND_RND_WRITE); if (unlikely(raw)) status = chip->ecc.write_page_raw(mtd, chip, buf, oob_required); @@ -2494,6 +2643,8 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, else status = chip->ecc.write_page(mtd, chip, buf, oob_required);
+ nand_rnd_config(mtd, -1, -1, NAND_RND_WRITE); + if (status < 0) return status;
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index ef6a783..6465a52 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -565,6 +565,64 @@ void nand_page_set_status(struct mtd_info *mtd, int page,
int nand_pst_create(struct mtd_info *mtd);
+/* + * Constants for randomizer modes + */ +typedef enum { + NAND_RND_NONE, + NAND_RND_SOFT, + NAND_RND_HW, +} nand_rnd_modes_t; + +/* + * Constants for randomizer actions + */ +enum nand_rnd_action { + NAND_RND_NO_ACTION, + NAND_RND_READ, + NAND_RND_WRITE, +}; + +/** + * struct nand_rndfree - Structure defining a NAND page region where the + * randomizer should be disabled + * @offset: range offset + * @length: range length + */ +struct nand_rndfree { + u32 offset; + u32 length; +}; + +/** + * struct nand_rnd_layout - Structure defining rndfree regions + * @nranges: number of ranges + * @ranges: array defining the rndfree regions + */ +struct nand_rnd_layout { + int nranges; + struct nand_rndfree ranges[0]; +}; + +/** + * struct nand_rnd_ctrl - Randomizer Control structure + * @mode: Randomizer mode + * @config: function to prepare the randomizer (i.e.: set the appropriate + * seed/init value). + * @read_buf: function that read from the NAND and descramble the retrieved + * data. + * @write_buf: function that scramble data before writing it to the NAND. + */ +struct nand_rnd_ctrl { + nand_rnd_modes_t mode; + struct nand_rnd_layout *layout; + void *priv; + int (*config)(struct mtd_info *mtd, int page, int column, + enum nand_rnd_action action); + void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len); + void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len); +}; + /** * struct nand_buffers - buffer structure for read/write * @ecccalc: buffer pointer for calculated ECC, size is oobsize. @@ -763,6 +821,8 @@ struct nand_chip { struct nand_buffers *buffers; struct nand_hw_control hwcontrol;
+ struct nand_rnd_ctrl rnd; + uint8_t *bbt; struct nand_bbt_descr *bbt_td; struct nand_bbt_descr *bbt_md; @@ -894,6 +954,42 @@ extern int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, extern int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, uint8_t *buf);
+static inline int nand_rnd_config(struct mtd_info *mtd, int page, int column, + enum nand_rnd_action action) +{ + struct nand_chip *chip = mtd->priv; + + if (chip->rnd.config) + return chip->rnd.config(mtd, page, column, action); + + return 0; +} + +static inline void nand_rnd_write_buf(struct mtd_info *mtd, const uint8_t *buf, + int len) +{ + struct nand_chip *chip = mtd->priv; + + if (chip->rnd.write_buf) + chip->rnd.write_buf(mtd, buf, len); + else + chip->write_buf(mtd, buf, len); +} + +static inline void nand_rnd_read_buf(struct mtd_info *mtd, uint8_t *buf, + int len) +{ + struct nand_chip *chip = mtd->priv; + + if (chip->rnd.read_buf) + chip->rnd.read_buf(mtd, buf, len); + else + chip->read_buf(mtd, buf, len); +} + +int nand_rnd_is_activ(struct mtd_info *mtd, int page, int column, int *len); + + #ifdef __UBOOT__ /* * Constants for oob configuration

On Fri, 5 Jun 2015 13:52:39 +0200 Roy Spliet r.spliet@ultimaker.com wrote:
Based on BBrezillons work, minus per-partition support. Changes to support that would be quite invasive while it hasn't been solved yet for Linux.
Same comment as in other patches: this work hasn't been accepted yet in mainline Linux, so maybe we should keep this code in the sunxi-nand driver until it is merged in Linux.

Heavily based on BBrezillon's (downstream) driver. Most noticable differences - No per-partition ECC settings. Partitions in U-boot are quite different from Linux - U-boot register definitions, shared with sunxi_nand_spl - FDT parsing in-line, there's no framework method yet
Signed-off-by: Roy Spliet r.spliet@ultimaker.com --- arch/arm/include/asm/arch-sunxi/nand.h | 46 +- board/sunxi/board.c | 5 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/sunxi_nand.c | 1887 ++++++++++++++++++++++++++++++++ include/fdtdec.h | 13 + lib/fdtdec.c | 17 + 6 files changed, 1966 insertions(+), 3 deletions(-) create mode 100644 drivers/mtd/nand/sunxi_nand.c
diff --git a/arch/arm/include/asm/arch-sunxi/nand.h b/arch/arm/include/asm/arch-sunxi/nand.h index 22844d8..d0fae80 100644 --- a/arch/arm/include/asm/arch-sunxi/nand.h +++ b/arch/arm/include/asm/arch-sunxi/nand.h @@ -1,5 +1,5 @@ /* - * (C) Copyright 2015 Roy Spliet rspliet@ultimaker.com + * (C) Copyright 2015 Roy Spliet r.spliet@ultimaker.com * * SPDX-License-Identifier: GPL-2.0+ */ @@ -27,8 +27,7 @@ struct sunxi_nand u32 ecc_ctl; /* 0x034 ECC configure and control */ u32 ecc_st; /* 0x038 ECC status and operation info */ u32 efr; /* 0x03C Enhanced feature */ - u32 err_cnt0; /* 0x040 Corrected error bit counter 0 */ - u32 err_cnt1; /* 0x044 Corrected error bit counter 1 */ + u32 err_cnt[4]; /* 0x040[4] Corrected error bit counter 0 */ u32 user_data[16]; /* 0x050[16] User data field */ u32 efnand_st; /* 0x090 EFNAND status */ u32 res0[3]; @@ -40,28 +39,69 @@ struct sunxi_nand u32 res1[3]; u32 mdma_addr; /* 0x0C0 MBUS DMA Address */ u32 mdma_cnt; /* 0x0C4 MBUS DMA data counter */ + u32 res2[206]; + u32 ram0_base; };
#define SUNXI_NAND_CTL_EN (1 << 0) #define SUNXI_NAND_CTL_RST (1 << 1) +#define SUNXI_NAND_BUS_WIDTH (1 << 2) +#define SUNXI_NAND_CTL_RB_SEL_MASK (0x3 << 3) +#define SUNXI_NAND_CTL_RB_SEL(a) ((a) << 3) +#define SUNXI_NAND_CTL_CE_ACT (1 << 6) +#define SUNXI_NAND_CTL_PAGE_SIZE_MASK (0xf << 8) #define SUNXI_NAND_CTL_PAGE_SIZE(a) ((fls(a) - 11) << 8) #define SUNXI_NAND_CTL_RAM_METHOD_DMA (1 << 14) +#define SUNXI_NAND_CTL_CE_SEL_MASK (0xf << 24) +#define SUNXI_NAND_CTL_CE_SEL(a) (a << 24) +#define SUNXI_NAND_CTL_DEBUG (1 << 31)
+#define SUNXI_NAND_ST_RB_B2R (1 << 0) #define SUNXI_NAND_ST_CMD_INT (1 << 1) #define SUNXI_NAND_ST_DMA_INT (1 << 2) #define SUNXI_NAND_ST_FIFO_FULL (1 << 3) +#define SUNXI_NAND_ST_BUSY (1 << 4) +#define SUNXI_NAND_ST_RB_STATE0 (1 << 8) +#define SUNXI_NAND_ST_RB_STATE1 (1 << 9) +#define SUNXI_NAND_ST_RB_STATE2 (1 << 10) +#define SUNXI_NAND_ST_RB_STATE3 (1 << 11)
+ +#define SUNXI_NAND_INT_B2R_ENABLE (1 << 0) +#define SUNXI_NAND_INT_CMD_ENABLE (1 << 1) +#define SUNXI_NAND_INT_DMA_ENABLE (1 << 2) +#define SUNXI_NAND_INT_MASK (SUNXI_NAND_INT_B2R_ENABLE | \ + SUNXI_NAND_INT_CMD_ENABLE | \ + SUNXI_NAND_INT_DMA_ENABLE) + +#define SUNXI_NAND_CMD_LOW_BYTE(a) (a & 0xff) +#define SUNXI_NAND_CMD_HIGH_BYTE(a) ((a & 0xff) << 8) #define SUNXI_NAND_CMD_ADDR_CYCLES(a) ((a - 1) << 16); +#define SUNXI_NAND_CMD_SEND_ADR (1 << 19) +#define SUNXI_NAND_CMD_ACCESS_RD 0 +#define SUNXI_NAND_CMD_ACCESS_WR (1 << 20) +#define SUNXI_NAND_CMD_DATA_TRANS (1 << 21) #define SUNXI_NAND_CMD_SEND_CMD1 (1 << 22) #define SUNXI_NAND_CMD_WAIT_FLAG (1 << 23) +#define SUNXI_NAND_CMD_SEND_CMD2 (1 << 24) #define SUNXI_NAND_CMD_ORDER_INTERLEAVE 0 #define SUNXI_NAND_CMD_ORDER_SEQ (1 << 25) +#define SUNXI_NAND_CMD_DATA_SWAP_METHOD (1 << 26) +#define SUNXI_NAND_CMD_ROW_AUTO_INC (1 << 27) +#define SUNXI_NAND_CMD_SEND_CMD3 (1 << 28) +#define SUNXI_NAND_CMD_SEND_CMD4 (1 << 29)
#define SUNXI_NAND_ECC_CTL_ECC_EN (1 << 0) #define SUNXI_NAND_ECC_CTL_PIPELINE (1 << 3) +#define SUNXI_NAND_ECC_CTL_EXCEPTION (1 << 4) #define SUNXI_NAND_ECC_CTL_BS_512B (1 << 5) #define SUNXI_NAND_ECC_CTL_RND_EN (1 << 9) +#define SUNXI_NAND_ECC_CTL_RND_DIRECTION (1 << 10) +#define SUNXI_NAND_ECC_CTL_MODE_MASK (0xf << 12) #define SUNXI_NAND_ECC_CTL_MODE(a) ((a) << 12) +#define SUNXI_NAND_ECC_CTL_RND_SEED_MASK (0xffff << 16) #define SUNXI_NAND_ECC_CTL_RND_SEED(a) ((a) << 16)
+extern void sunxi_nand_init(void); + #endif /* _SUNXI_NAND_H */ diff --git a/board/sunxi/board.c b/board/sunxi/board.c index d5bed30..e37209d 100644 --- a/board/sunxi/board.c +++ b/board/sunxi/board.c @@ -31,6 +31,7 @@ #include <asm/arch/dram.h> #include <asm/arch/gpio.h> #include <asm/arch/mmc.h> +#include <asm/arch/nand.h> #include <asm/arch/usb_phy.h> #include <asm/gpio.h> #include <asm/io.h> @@ -329,6 +330,10 @@ void board_nand_init(void)
for (pin = 0; pin < ARRAY_SIZE(ports); pin++) sunxi_gpio_set_cfgpin(SUNXI_GPC(ports[pin]), SUNXI_GPC_NAND); + +#ifndef CONFIG_SPL_BUILD + sunxi_nand_init(); +#endif }
void i2c_init_board(void) diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index f194493..c2368bc 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -66,6 +66,7 @@ obj-$(CONFIG_NAND_OMAP_GPMC) += omap_gpmc.o obj-$(CONFIG_NAND_OMAP_ELM) += omap_elm.o obj-$(CONFIG_NAND_PLAT) += nand_plat.o obj-$(CONFIG_NAND_DOCG4) += docg4.o +obj-$(CONFIG_NAND_SUNXI) += sunxi_nand.o
else # minimal SPL drivers
diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c new file mode 100644 index 0000000..42f45e3 --- /dev/null +++ b/drivers/mtd/nand/sunxi_nand.c @@ -0,0 +1,1887 @@ +/* + * Copyright (C) 2013 Boris BREZILLON b.brezillon.dev@gmail.com + * Copyright (C) 2015 Roy Spliet r.spliet@ultimaker.com + * + * Derived from: + * https://github.com/yuq/sunxi-nfc-mtd + * Copyright (C) 2013 Qiang Yu yuq825@gmail.com + * + * https://github.com/hno/Allwinner-Info + * Copyright (C) 2013 Henrik Nordström <Henrik Nordström> + * + * Copyright (C) 2013 Dmitriy B. rzk333@gmail.com + * Copyright (C) 2013 Sergey Lapin slapin@ossfans.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/gpio.h> +#include <asm/arch/clock.h> +#include <asm/arch/nand.h> +#include <asm/io.h> +#include <asm/errno.h> + +#include <fdtdec.h> +#include <fdt_support.h> +#include <nand.h> +#include <errno.h> +#include <malloc.h> + +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> + +/* Declare global data pointer */ +DECLARE_GLOBAL_DATA_PTR; + +#define NAND_MAX_CLOCK (10 * 1000000) +#define DEFAULT_NAME_FORMAT "nand@%d" +#define MAX_NAME_SIZE (sizeof("nand@") + 2) + +#define SUNXI_NAND_DEFAULT_TIMEOUT_MS 1000 + +/* + * Ready/Busy detection type: describes the Ready/Busy detection modes + * + * @RB_NONE: no external detection available, rely on STATUS command + * and software timeouts + * @RB_NATIVE: use sunxi NAND controller Ready/Busy support. The Ready/Busy + * pin of the NAND flash chip must be connected to one of the + * native NAND R/B pins (those which can be muxed to the NAND + * Controller) + * @RB_GPIO: use a simple GPIO to handle Ready/Busy status. The Ready/Busy + * pin of the NAND flash chip must be connected to a GPIO capable + * pin. + */ +enum sunxi_nand_rb_type { + RB_NONE, + RB_NATIVE, + RB_GPIO, +}; + +/* + * Ready/Busy structure: stores informations related to Ready/Busy detection + * + * @type: the Ready/Busy detection mode + * @info: information related to the R/B detection mode. Either a gpio + * id or a native R/B id (those supported by the NAND controller). + */ +struct sunxi_nand_rb { + enum sunxi_nand_rb_type type; + union { + int gpio; + int nativeid; + } info; +}; + +/* + * Chip Select structure: stores informations related to NAND Chip Select + * + * @cs: the NAND CS id used to communicate with a NAND Chip + * @rb: the Ready/Busy description + */ +struct sunxi_nand_chip_sel { + u8 cs; + struct sunxi_nand_rb rb; +}; + +/* + * sunxi HW ECC infos: stores informations related to HW ECC support + * + * @mode: the sunxi ECC mode field deduced from ECC requirements + * @layout: the OOB layout depending on the ECC requirements and the + * selected ECC mode + */ +struct sunxi_nand_hw_ecc { + int mode; + struct nand_ecclayout layout; +}; + +/* + * sunxi NAND randomizer structure: stores NAND randomizer informations + * + * @page: current page + * @column: current column + * @nseeds: seed table size + * @seeds: seed table + * @subseeds: pre computed sub seeds + * @step: step function + * @left: number of remaining bytes in the page + * @state: current randomizer state + */ +struct sunxi_nand_hw_rnd { + int page; + int column; + int nseeds; + u16 *seeds; + u16 *subseeds; + u16 (*step)(struct mtd_info *mtd, u16 state, int column, int *left); + int left; + u16 state; +}; + +/* + * NAND chip structure: stores NAND chip device related informations + * + * @node: used to store NAND chips into a list + * @nand: base NAND chip structure + * @mtd: base MTD structure + * @default_name: name used if no name was provided by the DT + * @clk_rate: clk_rate required for this NAND chip + * @selected: current active CS + * @nsels: number of CS lines required by the NAND chip + * @sels: array of CS lines descriptions + */ +struct sunxi_nand_chip { + struct list_head node; + struct nand_chip nand; + struct mtd_info mtd; + char default_name[MAX_NAME_SIZE]; + void *buffer; + unsigned long clk_rate; + int selected; + int nsels; + struct sunxi_nand_chip_sel sels[0]; +}; + +static inline struct sunxi_nand_chip *to_sunxi_nand(struct nand_chip *nand) +{ + return container_of(nand, struct sunxi_nand_chip, nand); +} + +/* + * NAND Controller structure: stores sunxi NAND controller informations + * + * @controller: base controller structure + * @regs: NAND controller registers + * @ahb_clk: NAND Controller AHB clock + * @mod_clk: NAND Controller mod clock + * @assigned_cs: bitmask describing already assigned CS lines + * @clk_rate: NAND controller current clock rate + * @chips: a list containing all the NAND chips attached to + * this NAND controller + * @complete: a completion object used to wait for NAND + * controller events + */ +struct sunxi_nfc { + struct nand_hw_control controller; + const struct sunxi_nand * regs; + struct clk *ahb_clk; + struct clk *mod_clk; + unsigned long assigned_cs; + unsigned long clk_rate; + struct list_head chips; +}; + +static inline struct sunxi_nfc *to_sunxi_nfc(struct nand_hw_control *ctrl) +{ + return container_of(ctrl, struct sunxi_nfc, controller); +} + +static void sunxi_set_clk_rate(unsigned long hz) +{ + struct sunxi_ccm_reg *const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + + uint32_t rval = readl(&ccm->pll5_cfg); + int n = (rval >> 8) & 0x1F; + int k = ((rval >> 4) & 0x3) + 1; + int p = (rval >> 16) & 0x3; + + unsigned long clk_rate = 24000000 * n * k >> p; + + unsigned long edo_clk = hz *2; + int div_n = 0, div_m; + + unsigned long nand_clk_divid_ratio = clk_rate / edo_clk; + + if (clk_rate % edo_clk) + nand_clk_divid_ratio++; + + for (div_m = nand_clk_divid_ratio; div_m > 16 && div_n < 3; div_n++) { + if (div_m % 2) + div_m++; + div_m >>= 1; + } + div_m--; + if (div_m > 15) + div_m = 15; /* Overflow */ + + /* config mod clock */ + clrsetbits_le32(&ccm->nand0_clk_cfg, 3 << 24, 2 << 24); /* 0 = OSC24M, 1 = PLL6, 2 = PLL5 */ + clrsetbits_le32(&ccm->nand0_clk_cfg, 3 << 16, div_n << 16); + clrsetbits_le32(&ccm->nand0_clk_cfg, 0xf << 0, div_m << 0); + + /*gate on nand clock*/ + setbits_le32(&ccm->ahb_gate0, (1 << AHB_GATE_OFFSET_NAND0)); +#ifdef CONFIG_MACH_SUN9I + setbits_le32(&ccm->ahb_gate1, (1 << AHB_GATE_OFFSET_DMA)); +#else + setbits_le32(&ccm->ahb_gate0, (1 << AHB_GATE_OFFSET_DMA)); +#endif + setbits_le32(&ccm->nand0_clk_cfg, 0x80000000); +} + +static int sunxi_nfc_wait_int(struct sunxi_nfc *nfc, u32 flags, + unsigned int timeout_ms) +{ + u32 time_start; + + if (!timeout_ms) + timeout_ms = (CONFIG_SYS_HZ * SUNXI_NAND_DEFAULT_TIMEOUT_MS) / 1000; + + time_start = get_timer(0); + + do { + if ((readl(&nfc->regs->st) & flags) == flags) { + setbits_le32(&nfc->regs->st, flags); + return 0; + } + } while (get_timer(time_start) < timeout_ms); + + pr_err("Timeout waiting for interrupt\n"); + return -ETIMEDOUT; +} + +static void sunxi_nfc_wait_cmd_fifo_empty(struct sunxi_nfc *nfc) +{ + u32 timeout = (CONFIG_SYS_HZ * SUNXI_NAND_DEFAULT_TIMEOUT_MS) / 1000; + u32 time_start; + + time_start = get_timer(0); + + do { + if (!(readl(&nfc->regs->st) & SUNXI_NAND_ST_FIFO_FULL)) + return; + } while (get_timer(time_start) < timeout); + + pr_err("Timeout waiting for empty fifo\n"); +} + +static void sunxi_nfc_rst(struct sunxi_nfc *nfc) +{ + u32 timeout = (CONFIG_SYS_HZ * SUNXI_NAND_DEFAULT_TIMEOUT_MS) / 1000; + u32 time_start; + + time_start = get_timer(0); + + writel(0, &nfc->regs->ecc_ctl); + writel(SUNXI_NAND_CTL_RST, &nfc->regs->ctl); + + do { + if (!(readl(&nfc->regs->ctl) & SUNXI_NAND_CTL_RST)) + return; + } while (get_timer(time_start) < timeout); + + pr_err("Timeout waiting for reset\n"); +} + +static int sunxi_nfc_dev_ready(struct mtd_info *mtd) +{ + struct nand_chip *nand = mtd->priv; + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); + struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); + struct sunxi_nand_rb *rb; + unsigned long timeo = (sunxi_nand->nand.state == FL_ERASING ? 400 : 20); + int ret; + + if (sunxi_nand->selected < 0) + return 0; + + rb = &sunxi_nand->sels[sunxi_nand->selected].rb; + + switch (rb->type) { + case RB_NATIVE: + ret = !!(readl(&nfc->regs->st) & + (SUNXI_NAND_ST_RB_STATE0 << rb->info.nativeid)); + if (ret) + break; + + sunxi_nfc_wait_int(nfc, SUNXI_NAND_ST_RB_B2R, timeo); + ret = !!(readl(&nfc->regs->st) & + (SUNXI_NAND_ST_RB_STATE0 << rb->info.nativeid)); + break; + case RB_GPIO: + /*check this*/ + ret = gpio_get_value(rb->info.gpio); + break; + case RB_NONE: + default: + ret = 0; + pr_err("cannot check R/B NAND status!"); + break; + } + + return ret; +} + +static void sunxi_nfc_select_chip(struct mtd_info *mtd, int chip) +{ + struct nand_chip *nand = mtd->priv; + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); + struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); + struct sunxi_nand_chip_sel *sel; + u32 ctl; + + if (chip > 0 && chip >= sunxi_nand->nsels) + return; + + if (chip == sunxi_nand->selected) + return; + + ctl = readl(&nfc->regs->ctl) & + ~(SUNXI_NAND_CTL_CE_SEL_MASK | SUNXI_NAND_CTL_RB_SEL_MASK | + SUNXI_NAND_CTL_EN | SUNXI_NAND_CTL_PAGE_SIZE_MASK); + + if (chip >= 0) { + sel = &sunxi_nand->sels[chip]; + + ctl |= SUNXI_NAND_CTL_CE_SEL(sel->cs) | SUNXI_NAND_CTL_EN | + (((nand->page_shift - 10) & 0xf) << 8); + if (sel->rb.type == RB_NONE) { + nand->dev_ready = NULL; + } else { + nand->dev_ready = sunxi_nfc_dev_ready; + if (sel->rb.type == RB_NATIVE) + ctl |= SUNXI_NAND_CTL_RB_SEL(sel->rb.info.nativeid); + } + + writel(mtd->writesize, &nfc->regs->spare_area); + + if (nfc->clk_rate != sunxi_nand->clk_rate) { + sunxi_set_clk_rate(sunxi_nand->clk_rate); + nfc->clk_rate = sunxi_nand->clk_rate; + } + } + + writel(ctl, &nfc->regs->ctl); + + sunxi_nand->selected = chip; +} + +static void sunxi_nfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct nand_chip *nand = mtd->priv; + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); + struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); + int cnt; + int offs = 0; + u32 tmp; + + while (len > offs) { + cnt = len - offs; + if (cnt > 1024) + cnt = 1024; + + sunxi_nfc_wait_cmd_fifo_empty(nfc); + writel(cnt & 0x3ff, &nfc->regs->data_cnt); + tmp = SUNXI_NAND_CMD_DATA_TRANS | SUNXI_NAND_CMD_DATA_SWAP_METHOD; + writel(tmp, &nfc->regs->cmd); + sunxi_nfc_wait_int(nfc, SUNXI_NAND_ST_CMD_INT, 0); + if (buf) + memcpy_fromio(buf + offs, &nfc->regs->ram0_base, cnt); + offs += cnt; + } +} + +static void sunxi_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf, + int len) +{ + struct nand_chip *nand = mtd->priv; + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); + struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); + int cnt; + int offs = 0; + u32 tmp; + + while (len > offs) { + cnt = len - offs; + if (cnt > 1024) + cnt = 1024; + + sunxi_nfc_wait_cmd_fifo_empty(nfc); + writel(cnt, &nfc->regs->data_cnt); + memcpy_toio(&nfc->regs->ram0_base, buf + offs, cnt); + tmp = SUNXI_NAND_CMD_DATA_TRANS | SUNXI_NAND_CMD_DATA_SWAP_METHOD | + SUNXI_NAND_CMD_ACCESS_WR; + writel(tmp, &nfc->regs->cmd); + sunxi_nfc_wait_int(nfc, SUNXI_NAND_ST_CMD_INT, 0); + offs += cnt; + } +} + +static u16 sunxi_nfc_hwrnd_step(struct sunxi_nand_hw_rnd *rnd, u16 state, int count) +{ + state &= 0x7fff; + count *= 8; + while (count--) + state = ((state >> 1) | + ((((state >> 0) ^ (state >> 1)) & 1) << 14)) & 0x7fff; + + return state; +} + +static u16 sunxi_nfc_hwrnd_single_step(u16 state, int count) +{ + state &= 0x7fff; + while (count--) + state = ((state >> 1) | + ((((state >> 0) ^ (state >> 1)) & 1) << 14)) & 0x7fff; + + return state; +} + +static int sunxi_nfc_hwrnd_config(struct mtd_info *mtd, int page, int column, + enum nand_rnd_action action) +{ + struct nand_chip *nand = mtd->priv; + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); + struct sunxi_nand_hw_rnd *rnd = nand->rnd.priv; + u16 state; + + if (page < 0 && column < 0) { + rnd->page = -1; + rnd->column = -1; + return 0; + } + + if (column < 0) + column = 0; + if (page < 0) + page = rnd->page; + + if (page < 0) + return -EINVAL; + + if (page != rnd->page && action == NAND_RND_READ) { + int status; + + status = nand_page_get_status(mtd, page); + if (status == NAND_PAGE_STATUS_UNKNOWN) { + nand->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1); + sunxi_nfc_read_buf(mtd, sunxi_nand->buffer, + mtd->writesize + mtd->oobsize); + + if (nand_page_is_empty(mtd, sunxi_nand->buffer, + sunxi_nand->buffer + + mtd->writesize)) + status = NAND_PAGE_EMPTY; + else + status = NAND_PAGE_FILLED; + + nand_page_set_status(mtd, page, status); + nand->cmdfunc(mtd, NAND_CMD_RNDOUT, column, -1); + } + } + + state = rnd->seeds[page % rnd->nseeds]; + rnd->page = page; + rnd->column = column; + + if (rnd->step) { + rnd->state = rnd->step(mtd, state, column, &rnd->left); + } else { + rnd->state = sunxi_nfc_hwrnd_step(rnd, state, column % 4096); + rnd->left = mtd->oobsize + mtd->writesize - column; + } + + return 0; +} + +static void sunxi_nfc_hwrnd_write_buf(struct mtd_info *mtd, const uint8_t *buf, + int len) +{ + struct nand_chip *nand = mtd->priv; + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + struct sunxi_nand_hw_rnd *rnd = nand->rnd.priv; + u32 tmp = readl(&nfc->regs->ecc_ctl); + int cnt; + int offs = 0; + int rndactiv; + + tmp &= ~(SUNXI_NAND_ECC_CTL_RND_DIRECTION | + SUNXI_NAND_ECC_CTL_RND_SEED_MASK | + SUNXI_NAND_ECC_CTL_RND_EN); + writel(tmp, &nfc->regs->ecc_ctl); + + if (rnd->page < 0) { + sunxi_nfc_write_buf(mtd, buf, len); + return; + } + + while (len > offs) { + cnt = len - offs; + if (cnt > 1024) + cnt = 1024; + + rndactiv = nand_rnd_is_activ(mtd, rnd->page, rnd->column, + &cnt); + if (rndactiv > 0) { + writel(tmp | SUNXI_NAND_ECC_CTL_RND_EN | + SUNXI_NAND_ECC_CTL_RND_SEED(rnd->state), + &nfc->regs->ecc_ctl); + if (rnd->left < cnt) + cnt = rnd->left; + } + + sunxi_nfc_write_buf(mtd, buf + offs, cnt); + + if (rndactiv > 0) + writel(tmp & ~SUNXI_NAND_ECC_CTL_RND_EN, + &nfc->regs->ecc_ctl); + + offs += cnt; + if (len <= offs) + break; + + sunxi_nfc_hwrnd_config(mtd, -1, rnd->column + cnt, NAND_RND_WRITE); + } +} + +static void sunxi_nfc_hwrnd_read_buf(struct mtd_info *mtd, uint8_t *buf, + int len) +{ + struct nand_chip *nand = mtd->priv; + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + struct sunxi_nand_hw_rnd *rnd = nand->rnd.priv; + u32 tmp = readl(&nfc->regs->ecc_ctl); + int cnt; + int offs = 0; + int rndactiv; + + tmp &= ~(SUNXI_NAND_ECC_CTL_RND_DIRECTION | + SUNXI_NAND_ECC_CTL_RND_SEED_MASK | + SUNXI_NAND_ECC_CTL_RND_EN); + writel(tmp, &nfc->regs->ecc_ctl); + + if (rnd->page < 0) { + sunxi_nfc_read_buf(mtd, buf, len); + return; + } + + while (len > offs) { + cnt = len - offs; + if (cnt > 1024) + cnt = 1024; + + if (nand_page_get_status(mtd, rnd->page) != NAND_PAGE_EMPTY && + nand_rnd_is_activ(mtd, rnd->page, rnd->column, &cnt) > 0) + rndactiv = 1; + else + rndactiv = 0; + + if (rndactiv > 0) { + writel(tmp | SUNXI_NAND_ECC_CTL_RND_EN | + SUNXI_NAND_ECC_CTL_RND_SEED(rnd->state), + &nfc->regs->ecc_ctl); + if (rnd->left < cnt) + cnt = rnd->left; + } + + if (buf) + sunxi_nfc_read_buf(mtd, buf + offs, cnt); + else + sunxi_nfc_read_buf(mtd, NULL, cnt); + + if (rndactiv > 0) + writel(tmp & ~SUNXI_NAND_ECC_CTL_RND_EN, + &nfc->regs->ecc_ctl); + + offs += cnt; + if (len <= offs) + break; + + sunxi_nfc_hwrnd_config(mtd, -1, rnd->column + cnt, NAND_RND_READ); + } +} +static uint8_t sunxi_nfc_read_byte(struct mtd_info *mtd) +{ + uint8_t ret; + + sunxi_nfc_read_buf(mtd, &ret, 1); + + return ret; +} + +static void sunxi_nfc_cmd_ctrl(struct mtd_info *mtd, int dat, + unsigned int ctrl) +{ + struct nand_chip *nand = mtd->priv; + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); + struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); + u32 tmp; + + sunxi_nfc_wait_cmd_fifo_empty(nfc); + + if (ctrl & NAND_CTRL_CHANGE) { + tmp = readl(&nfc->regs->ctl); + if (ctrl & NAND_NCE) + tmp |= SUNXI_NAND_CTL_CE_ACT; + else + tmp &= ~SUNXI_NAND_CTL_CE_ACT; + writel(tmp, &nfc->regs->ctl); + } + + if (dat == NAND_CMD_NONE) + return; + + if (ctrl & NAND_CLE) { + writel(SUNXI_NAND_CMD_SEND_CMD1 | dat, &nfc->regs->cmd); + } else { + writel(dat, &nfc->regs->addr_low); + writel(SUNXI_NAND_CMD_SEND_ADR, &nfc->regs->cmd); + } + + sunxi_nfc_wait_int(nfc, SUNXI_NAND_ST_CMD_INT, 0); +} + +static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, + struct nand_chip *chip, uint8_t *buf, + int oob_required, int page) +{ + struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller); + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(chip); + struct nand_ecc_ctrl *ecc = &chip->ecc; + struct nand_ecclayout *layout = ecc->layout; + struct sunxi_nand_hw_ecc *data = ecc->priv; + unsigned int max_bitflips = 0; + int status; + int offset; + u32 tmp; + int i; + int cnt; + + status = nand_page_get_status(mtd, page); + if (status == NAND_PAGE_STATUS_UNKNOWN) { + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1); + sunxi_nfc_read_buf(mtd, sunxi_nand->buffer, + mtd->writesize + mtd->oobsize); + + if (nand_page_is_empty(mtd, sunxi_nand->buffer, + sunxi_nand->buffer + + mtd->writesize)) { + status = NAND_PAGE_EMPTY; + } else { + status = NAND_PAGE_FILLED; + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1); + } + + nand_page_set_status(mtd, page, status); + } + + if (status == NAND_PAGE_EMPTY) { + memset(buf, 0xff, mtd->writesize); + if (oob_required) + memset(chip->oob_poi, 0xff, mtd->oobsize); + return 0; + } + + tmp = readl(&nfc->regs->ecc_ctl); + tmp &= ~(SUNXI_NAND_ECC_CTL_MODE_MASK | SUNXI_NAND_ECC_CTL_PIPELINE | SUNXI_NAND_ECC_CTL_BS_512B); + tmp |= SUNXI_NAND_ECC_CTL_ECC_EN | SUNXI_NAND_ECC_CTL_MODE(data->mode) | + SUNXI_NAND_ECC_CTL_EXCEPTION; + + writel(tmp, &nfc->regs->ecc_ctl); + + for (i = 0; i < ecc->steps; i++) { + bool rndactiv = false; + + if (i) + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, i * ecc->size, -1); + + offset = mtd->writesize + layout->eccpos[i * ecc->bytes] - 4; + + nand_rnd_config(mtd, page, i * ecc->size, NAND_RND_READ); + nand_rnd_read_buf(mtd, NULL, ecc->size); + + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1); + sunxi_nfc_wait_cmd_fifo_empty(nfc); + + if (i) { + cnt = ecc->bytes + 4; + if (nand_rnd_is_activ(mtd, page, offset, &cnt) > 0 && + cnt == ecc->bytes + 4) + rndactiv = true; + } else { + cnt = ecc->bytes + 2; + if (nand_rnd_is_activ(mtd, page, offset + 2, &cnt) > 0 && + cnt == ecc->bytes + 2) + rndactiv = true; + } + + if (rndactiv) { + tmp = readl(&nfc->regs->ecc_ctl); + tmp &= ~(SUNXI_NAND_ECC_CTL_RND_DIRECTION | SUNXI_NAND_ECC_CTL_EXCEPTION); + tmp |= SUNXI_NAND_ECC_CTL_RND_EN; + writel(tmp, &nfc->regs->ecc_ctl); + } + + tmp = SUNXI_NAND_CMD_DATA_TRANS | SUNXI_NAND_CMD_DATA_SWAP_METHOD | (1 << 30); + writel(tmp, &nfc->regs->cmd); + + sunxi_nfc_wait_int(nfc, SUNXI_NAND_ST_CMD_INT, 0); + memcpy_fromio(buf + (i * ecc->size), + &nfc->regs->ram0_base, ecc->size); + + writel(readl(&nfc->regs->ecc_ctl) & ~SUNXI_NAND_ECC_CTL_RND_EN, + &nfc->regs->ecc_ctl); + + if (readl(&nfc->regs->ecc_st) & 0x1) { + mtd->ecc_stats.failed++; + } else { + tmp = readl(&nfc->regs->err_cnt[0]) & 0xff; + mtd->ecc_stats.corrected += tmp; + max_bitflips = max_t(unsigned int, max_bitflips, tmp); + } + + if (oob_required) { + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1); + sunxi_nfc_wait_cmd_fifo_empty(nfc); + nand_rnd_config(mtd, -1, offset, NAND_RND_READ); + offset -= mtd->writesize; + nand_rnd_read_buf(mtd, chip->oob_poi + offset, + ecc->bytes + 4); + } + } + + if (oob_required) { + cnt = ecc->layout->oobfree[ecc->steps].length; + if (cnt > 0) { + offset = mtd->writesize + + ecc->layout->oobfree[ecc->steps].offset; + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1); + nand_rnd_config(mtd, -1, offset, NAND_RND_READ); + offset -= mtd->writesize; + nand_rnd_read_buf(mtd, chip->oob_poi + offset, cnt); + } + } + + nand_rnd_config(mtd, -1, -1, NAND_RND_READ); + + tmp = readl(&nfc->regs->ecc_ctl); + tmp &= ~SUNXI_NAND_ECC_CTL_ECC_EN; + + writel(tmp, &nfc->regs->ecc_ctl); + + return max_bitflips; +} + +static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, + struct nand_chip *chip, + const uint8_t *buf, int oob_required) +{ + struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller); + struct nand_ecc_ctrl *ecc = &chip->ecc; + struct nand_ecclayout *layout = ecc->layout; + struct sunxi_nand_hw_ecc *data = ecc->priv; + struct sunxi_nand_hw_rnd *rnd = chip->rnd.priv; + int offset; + u32 tmp; + int i; + int cnt; + + tmp = readl(&nfc->regs->ecc_ctl); + tmp &= ~(SUNXI_NAND_ECC_CTL_MODE_MASK | SUNXI_NAND_ECC_CTL_PIPELINE | SUNXI_NAND_ECC_CTL_BS_512B); + tmp |= SUNXI_NAND_ECC_CTL_ECC_EN | SUNXI_NAND_ECC_CTL_MODE(data->mode) | + SUNXI_NAND_ECC_CTL_EXCEPTION; + + writel(tmp, &nfc->regs->ecc_ctl); + + for (i = 0; i < mtd->writesize / ecc->size; i++) { + bool rndactiv = false; + u8 oob_buf[4]; + + if (i) + chip->cmdfunc(mtd, NAND_CMD_RNDIN, i * ecc->size, -1); + + nand_rnd_config(mtd, -1, i * ecc->size, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, buf + (i * ecc->size), ecc->size); + + offset = layout->eccpos[i * ecc->bytes] - 4 + mtd->writesize; + + /* Fill OOB data in */ + if (!oob_required) + memset(oob_buf, 0xff, 4); + else + memcpy(oob_buf, + chip->oob_poi + layout->oobfree[i].offset, + 4); + + + memcpy_toio(nfc->regs->user_data, oob_buf, 4); + + if (i) { + cnt = ecc->bytes + 4; + if (rnd && + nand_rnd_is_activ(mtd, -1, offset, &cnt) > 0 && + cnt == ecc->bytes + 4) + rndactiv = true; + } else { + cnt = ecc->bytes + 2; + if (rnd && + nand_rnd_is_activ(mtd, -1, offset + 2, &cnt) > 0 && + cnt == ecc->bytes + 2) + rndactiv = true; + } + + if (rndactiv) { + /* pre randomize to generate FF patterns on the NAND */ + if (!i) { + u16 state = rnd->subseeds[rnd->page % rnd->nseeds]; + state = sunxi_nfc_hwrnd_single_step(state, 15); + oob_buf[0] ^= state; + state = sunxi_nfc_hwrnd_step(rnd, state, 1); + oob_buf[1] ^= state; + memcpy_toio(nfc->regs->user_data, oob_buf, 4); + } + tmp = readl(&nfc->regs->ecc_ctl); + tmp &= ~(SUNXI_NAND_ECC_CTL_RND_DIRECTION | SUNXI_NAND_ECC_CTL_EXCEPTION); + tmp |= SUNXI_NAND_ECC_CTL_RND_EN; + writel(tmp, &nfc->regs->ecc_ctl); + } + + chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset, -1); + sunxi_nfc_wait_cmd_fifo_empty(nfc); + + tmp = SUNXI_NAND_CMD_DATA_TRANS | SUNXI_NAND_CMD_DATA_SWAP_METHOD | SUNXI_NAND_CMD_ACCESS_WR | + (1 << 30); + writel(tmp, &nfc->regs->cmd); + sunxi_nfc_wait_int(nfc, SUNXI_NAND_ST_CMD_INT, 0); + + writel(readl(&nfc->regs->ecc_ctl) & ~SUNXI_NAND_ECC_CTL_RND_EN, + &nfc->regs->ecc_ctl); + } + + if (oob_required) { + cnt = ecc->layout->oobfree[i].length; + if (cnt > 0) { + offset = mtd->writesize + + ecc->layout->oobfree[i].offset; + chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset, -1); + nand_rnd_config(mtd, -1, offset, NAND_RND_WRITE); + offset -= mtd->writesize; + nand_rnd_write_buf(mtd, chip->oob_poi + offset, cnt); + } + } + + nand_rnd_config(mtd, -1, -1, NAND_RND_WRITE); + + tmp = readl(&nfc->regs->ecc_ctl); + tmp &= ~SUNXI_NAND_ECC_CTL_ECC_EN; + + writel(tmp, &nfc->regs->ecc_ctl); + + return 0; +} + +static u16 sunxi_nfc_hw_ecc_rnd_steps(struct mtd_info *mtd, u16 state, + int column, int *left) +{ + struct nand_chip *chip = mtd->priv; + struct nand_ecc_ctrl *ecc = &chip->ecc; + struct sunxi_nand_hw_rnd *rnd = chip->rnd.priv; + int nblks = mtd->writesize / ecc->size; + int modsize = ecc->size; + int steps; + + if (column < mtd->writesize) { + steps = column % modsize; + *left = modsize - steps; + } else if (column < mtd->writesize + + (nblks * (ecc->bytes + 4))) { + column -= mtd->writesize; + steps = column % (ecc->bytes + 4); + *left = ecc->bytes + 4 - steps; + state = rnd->subseeds[rnd->page % rnd->nseeds]; + } else { + steps = column % 4096; + *left = mtd->writesize + mtd->oobsize - column; + } + + return sunxi_nfc_hwrnd_step(rnd, state, steps); +} + +static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd, + struct nand_chip *chip, + uint8_t *buf, int oob_required, + int page) +{ + struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller); + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(chip); + struct nand_ecc_ctrl *ecc = &chip->ecc; + struct sunxi_nand_hw_ecc *data = ecc->priv; + int steps = mtd->writesize / ecc->size; + unsigned int max_bitflips = 0; + uint8_t *oob = chip->oob_poi; + int offset = 0; + int status; + int cnt; + u32 tmp; + int i; + + status = nand_page_get_status(mtd, page); + if (status == NAND_PAGE_STATUS_UNKNOWN) { + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1); + sunxi_nfc_read_buf(mtd, sunxi_nand->buffer, + mtd->writesize + mtd->oobsize); + + if (nand_page_is_empty(mtd, sunxi_nand->buffer, + sunxi_nand->buffer + + mtd->writesize)) { + status = NAND_PAGE_EMPTY; + } else { + status = NAND_PAGE_FILLED; + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1); + } + + nand_page_set_status(mtd, page, status); + } + + if (status == NAND_PAGE_EMPTY) { + memset(buf, 0xff, mtd->writesize); + if (oob_required) + memset(chip->oob_poi, 0xff, mtd->oobsize); + return 0; + } + + tmp = readl(&nfc->regs->ecc_ctl); + tmp &= ~(SUNXI_NAND_ECC_CTL_MODE_MASK | SUNXI_NAND_ECC_CTL_PIPELINE | SUNXI_NAND_ECC_CTL_BS_512B); + tmp |= SUNXI_NAND_ECC_CTL_ECC_EN | SUNXI_NAND_ECC_CTL_MODE(data->mode) | + SUNXI_NAND_ECC_CTL_EXCEPTION; + + writel(tmp, &nfc->regs->ecc_ctl); + + for (i = 0; i < steps; i++) { + nand_rnd_config(mtd, page, offset, NAND_RND_READ); + nand_rnd_read_buf(mtd, NULL, ecc->size); + + cnt = ecc->bytes + 4; + if (nand_rnd_is_activ(mtd, page, offset, &cnt) > 0 && + cnt == ecc->bytes + 4) { + tmp = readl(&nfc->regs->ecc_ctl); + tmp &= ~(SUNXI_NAND_ECC_CTL_RND_DIRECTION | SUNXI_NAND_ECC_CTL_EXCEPTION); + tmp |= SUNXI_NAND_ECC_CTL_RND_EN; + writel(tmp, &nfc->regs->ecc_ctl); + } + + tmp = SUNXI_NAND_CMD_DATA_TRANS | SUNXI_NAND_CMD_DATA_SWAP_METHOD | (1 << 30); + writel(tmp, &nfc->regs->cmd); + sunxi_nfc_wait_int(nfc, SUNXI_NAND_ST_CMD_INT, 0); + memcpy_fromio(buf, &nfc->regs->ram0_base, ecc->size); + buf += ecc->size; + offset += ecc->size; + + writel(readl(&nfc->regs->ecc_ctl) & ~SUNXI_NAND_ECC_CTL_RND_EN, + &nfc->regs->ecc_ctl); + + if (readl(&nfc->regs->ecc_st) & 0x1) { + mtd->ecc_stats.failed++; + } else { + tmp = readl(&nfc->regs->err_cnt[0]) & 0xff; + mtd->ecc_stats.corrected += tmp; + max_bitflips = max_t(unsigned int, max_bitflips, tmp); + } + + if (oob_required) { + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1); + nand_rnd_config(mtd, -1, offset, NAND_RND_READ); + nand_rnd_read_buf(mtd, oob, ecc->bytes + ecc->prepad); + oob += ecc->bytes + ecc->prepad; + } + + offset += ecc->bytes + ecc->prepad; + } + + if (oob_required) { + cnt = mtd->oobsize - (oob - chip->oob_poi); + if (cnt > 0) { + chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1); + nand_rnd_config(mtd, page, offset, NAND_RND_READ); + nand_rnd_read_buf(mtd, oob, cnt); + } + } + + nand_rnd_config(mtd, -1, -1, NAND_RND_READ); + + writel(readl(&nfc->regs->ecc_ctl) & ~SUNXI_NAND_ECC_CTL_ECC_EN, + &nfc->regs->ecc_ctl); + + return max_bitflips; +} + +static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd, + struct nand_chip *chip, + const uint8_t *buf, + int oob_required) +{ + struct sunxi_nfc *nfc = to_sunxi_nfc(chip->controller); + struct nand_ecc_ctrl *ecc = &chip->ecc; + struct sunxi_nand_hw_ecc *data = ecc->priv; + struct sunxi_nand_hw_rnd *rnd = chip->rnd.priv; + int steps = mtd->writesize / ecc->size; + uint8_t *oob = chip->oob_poi; + int offset = 0; + int cnt; + u32 tmp; + int i; + + tmp = readl(&nfc->regs->ecc_ctl); + tmp &= ~(SUNXI_NAND_ECC_CTL_MODE_MASK | SUNXI_NAND_ECC_CTL_PIPELINE | SUNXI_NAND_ECC_CTL_BS_512B); + tmp |= SUNXI_NAND_ECC_CTL_ECC_EN | SUNXI_NAND_ECC_CTL_MODE(data->mode) | + SUNXI_NAND_ECC_CTL_EXCEPTION; + + writel(tmp, &nfc->regs->ecc_ctl); + + for (i = 0; i < steps; i++) { + nand_rnd_config(mtd, -1, offset, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, buf + (i * ecc->size), ecc->size); + offset += ecc->size; + + /* Fill OOB data in */ + if (oob_required) { + tmp = 0xffffffff; + memcpy_toio(nfc->regs->user_data, &tmp, + 4); + } else { + memcpy_toio(nfc->regs->user_data, oob , + 4); + } + + cnt = ecc->bytes + 4; + if (rnd && + nand_rnd_is_activ(mtd, rnd->page, offset, &cnt) > 0 && + cnt == ecc->bytes + 4) { + tmp = readl(&nfc->regs->ecc_ctl); + tmp &= ~(SUNXI_NAND_ECC_CTL_RND_DIRECTION | SUNXI_NAND_ECC_CTL_EXCEPTION); + tmp |= SUNXI_NAND_ECC_CTL_RND_EN; + writel(tmp, &nfc->regs->ecc_ctl); + } + + tmp = SUNXI_NAND_CMD_DATA_TRANS | SUNXI_NAND_CMD_DATA_SWAP_METHOD | SUNXI_NAND_CMD_ACCESS_WR | + (1 << 30); + writel(tmp, &nfc->regs->cmd); + sunxi_nfc_wait_int(nfc, SUNXI_NAND_ST_CMD_INT, 0); + + writel(readl(&nfc->regs->ecc_ctl) & ~SUNXI_NAND_ECC_CTL_RND_EN, + &nfc->regs->ecc_ctl); + + offset += ecc->bytes + ecc->prepad; + oob += ecc->bytes + ecc->prepad; + } + + if (oob_required) { + cnt = mtd->oobsize - (oob - chip->oob_poi); + if (cnt > 0) { + chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset, -1); + nand_rnd_config(mtd, -1, offset, NAND_RND_WRITE); + nand_rnd_write_buf(mtd, oob, cnt); + } + } + nand_rnd_config(mtd, -1, -1, NAND_RND_WRITE); + + tmp = readl(&nfc->regs->ecc_ctl); + tmp &= ~SUNXI_NAND_ECC_CTL_ECC_EN; + + writel(tmp, &nfc->regs->ecc_ctl); + + return 0; +} + +static u16 sunxi_nfc_hw_syndrome_ecc_rnd_steps(struct mtd_info *mtd, u16 state, + int column, int *left) +{ + struct nand_chip *chip = mtd->priv; + struct nand_ecc_ctrl *ecc = &chip->ecc; + struct sunxi_nand_hw_rnd *rnd = chip->rnd.priv; + int eccsteps = mtd->writesize / ecc->size; + int modsize = ecc->size + ecc->prepad + ecc->bytes; + int steps; + + if (column < (eccsteps * modsize)) { + steps = column % modsize; + *left = modsize - steps; + if (steps >= ecc->size) { + steps -= ecc->size; + state = rnd->subseeds[rnd->page % rnd->nseeds]; + } + } else { + steps = column % 4096; + *left = mtd->writesize + mtd->oobsize - column; + } + + return sunxi_nfc_hwrnd_step(rnd, state, steps); +} + +static u16 default_seeds[] = {0x4a80}; +#ifndef __UBOOT__ +static void sunxi_nand_rnd_ctrl_cleanup(struct nand_rnd_ctrl *rnd) +{ + struct sunxi_nand_hw_rnd *hwrnd = rnd->priv; + + if (hwrnd->seeds != default_seeds) + kfree(hwrnd->seeds); + kfree(hwrnd->subseeds); + kfree(rnd->layout); + kfree(hwrnd); +} +#endif + +static int sunxi_nand_rnd_ctrl_init(int node, struct mtd_info *mtd, + struct nand_rnd_ctrl *rnd, + struct nand_ecc_ctrl *ecc) +{ + struct sunxi_nand_hw_rnd *hwrnd; + struct nand_rnd_layout *layout = NULL; + int ret; + + hwrnd = kzalloc(sizeof(*hwrnd), GFP_KERNEL); + if (!hwrnd) + return -ENOMEM; + + hwrnd->seeds = default_seeds; + hwrnd->nseeds = ARRAY_SIZE(default_seeds); + + if(fdt_getprop(gd->fdt_blob, node, "nand-randomizer-seeds", &ret)){ + hwrnd->nseeds = ret / sizeof(*hwrnd->seeds); + hwrnd->seeds = kzalloc(hwrnd->nseeds * sizeof(*hwrnd->seeds), + GFP_KERNEL); + if (!hwrnd->seeds) { + ret = -ENOMEM; + goto err; + } + + ret = fdtdec_get_u16_array(gd->fdt_blob, node, "nand-randomizer-seeds", + hwrnd->seeds, hwrnd->nseeds); + if (ret) + goto err; + } + + switch (ecc->mode) { + case NAND_ECC_HW_SYNDROME: + hwrnd->step = sunxi_nfc_hw_syndrome_ecc_rnd_steps; + break; + + case NAND_ECC_HW: + hwrnd->step = sunxi_nfc_hw_ecc_rnd_steps; + + default: + layout = kzalloc(sizeof(*layout) + sizeof(struct nand_rndfree), + GFP_KERNEL); + if (!layout) { + ret = -ENOMEM; + goto err; + } + layout->nranges = 1; + layout->ranges[0].offset = mtd->writesize; + layout->ranges[0].length = 2; + rnd->layout = layout; + break; + } + + if (ecc->mode == NAND_ECC_HW_SYNDROME || ecc->mode == NAND_ECC_HW) { + int i; + + hwrnd->subseeds = kzalloc(hwrnd->nseeds * + sizeof(*hwrnd->subseeds), + GFP_KERNEL); + if (!hwrnd->subseeds) { + ret = -ENOMEM; + goto err; + } + + for (i = 0; i < hwrnd->nseeds; i++) + hwrnd->subseeds[i] = sunxi_nfc_hwrnd_step(hwrnd, + hwrnd->seeds[i], + ecc->size); + } + + rnd->config = sunxi_nfc_hwrnd_config; + rnd->read_buf = sunxi_nfc_hwrnd_read_buf; + rnd->write_buf = sunxi_nfc_hwrnd_write_buf; + rnd->priv = hwrnd; + + return 0; + +err: + kfree(hwrnd); + kfree(layout); + + return ret; +} + +static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip, + const struct nand_sdr_timings *timings) +{ + u32 min_clk_period = 0; + + /* T1 <=> tCLS */ + if (timings->tCLS_min > min_clk_period) + min_clk_period = timings->tCLS_min; + + /* T2 <=> tCLH */ + if (timings->tCLH_min > min_clk_period) + min_clk_period = timings->tCLH_min; + + /* T3 <=> tCS */ + if (timings->tCS_min > min_clk_period) + min_clk_period = timings->tCS_min; + + /* T4 <=> tCH */ + if (timings->tCH_min > min_clk_period) + min_clk_period = timings->tCH_min; + + /* T5 <=> tWP */ + if (timings->tWP_min > min_clk_period) + min_clk_period = timings->tWP_min; + + /* T6 <=> tWH */ + if (timings->tWH_min > min_clk_period) + min_clk_period = timings->tWH_min; + + /* T7 <=> tALS */ + if (timings->tALS_min > min_clk_period) + min_clk_period = timings->tALS_min; + + /* T8 <=> tDS */ + if (timings->tDS_min > min_clk_period) + min_clk_period = timings->tDS_min; + + /* T9 <=> tDH */ + if (timings->tDH_min > min_clk_period) + min_clk_period = timings->tDH_min; + + /* T10 <=> tRR */ + if (timings->tRR_min > (min_clk_period * 3)) + min_clk_period = (timings->tRR_min + 2) / 3; + + /* T11 <=> tALH */ + if (timings->tALH_min > min_clk_period) + min_clk_period = timings->tALH_min; + + /* T12 <=> tRP */ + if (timings->tRP_min > min_clk_period) + min_clk_period = timings->tRP_min; + + /* T13 <=> tREH */ + if (timings->tREH_min > min_clk_period) + min_clk_period = timings->tREH_min; + + /* T14 <=> tRC */ + if (timings->tRC_min > (min_clk_period * 2)) + min_clk_period = (timings->tRC_min + 1) / 2; + + /* T15 <=> tWC */ + if (timings->tWC_min > (min_clk_period * 2)) + min_clk_period = (timings->tWC_min + 1) / 2; + + + /* min_clk_period = (NAND-clk-period * 2) */ + if (min_clk_period < 1000) + min_clk_period = 1000; + + min_clk_period /= 1000; + chip->clk_rate = (2 * 1000000000) / min_clk_period; + + /* TODO: configure T16-T19 */ + + return 0; +} + +static int sunxi_nand_chip_init_timings(struct sunxi_nand_chip *chip) +{ + const struct nand_sdr_timings *timings; + int ret; + int mode; + + mode = onfi_get_async_timing_mode(&chip->nand); + if (mode == ONFI_TIMING_MODE_UNKNOWN) { + mode = chip->nand.onfi_timing_mode_ds; + } else { + uint8_t feature[ONFI_SUBFEATURE_PARAM_LEN] = {}; + + mode = fls(mode) - 1; + if (mode < 0) + mode = 0; + + feature[0] = mode; + ret = chip->nand.onfi_set_features(&chip->mtd, &chip->nand, + ONFI_FEATURE_ADDR_TIMING_MODE, + feature); + if (ret) + return ret; + } + + timings = onfi_async_timing_mode_to_sdr_timings(mode); + if (IS_ERR(timings)) + return PTR_ERR(timings); + + return sunxi_nand_chip_set_timings(chip, timings); +} + +static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd, + struct nand_ecc_ctrl *ecc) +{ + struct sunxi_nand_hw_ecc *data; + struct nand_ecclayout *layout; + int nsectors; + int ret; + + if (!ecc->strength || !ecc->size) + return -EINVAL; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + /* Add ECC info retrieval from DT */ + if (ecc->strength <= 16) { + ecc->strength = 16; + data->mode = 0; + } else if (ecc->strength <= 24) { + ecc->strength = 24; + data->mode = 1; + } else if (ecc->strength <= 28) { + ecc->strength = 28; + data->mode = 2; + } else if (ecc->strength <= 32) { + ecc->strength = 32; + data->mode = 3; + } else if (ecc->strength <= 40) { + ecc->strength = 40; + data->mode = 4; + } else if (ecc->strength <= 48) { + ecc->strength = 48; + data->mode = 5; + } else if (ecc->strength <= 56) { + ecc->strength = 56; + data->mode = 6; + } else if (ecc->strength <= 60) { + ecc->strength = 60; + data->mode = 7; + } else if (ecc->strength <= 64) { + ecc->strength = 64; + data->mode = 8; + } else { + pr_err("unsupported strength\n"); + ret = -ENOTSUPP; + goto err; + } + + /* HW ECC always request ECC bytes for 1024 bytes blocks */ + ecc->bytes = ((ecc->strength * fls(8 * 1024)) + 7) / 8; + + /* HW ECC always work with even numbers of ECC bytes */ + if (ecc->bytes % 2) + ecc->bytes++; + + layout = &data->layout; + nsectors = mtd->writesize / ecc->size; + + if (mtd->oobsize < ((ecc->bytes + 4) * nsectors)) { + ret = -EINVAL; + goto err; + } + + layout->eccbytes = (ecc->bytes * nsectors); + + ecc->layout = layout; + ecc->priv = data; + + return 0; + +err: + kfree(data); + + return ret; +} + +#ifndef __UBOOT__ +static void sunxi_nand_hw_common_ecc_ctrl_cleanup(struct nand_ecc_ctrl *ecc) +{ + kfree(ecc->priv); +} +#endif + +static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd, + struct nand_ecc_ctrl *ecc) +{ + struct nand_ecclayout *layout; + int nsectors; + int i, j; + int ret; + + ret = sunxi_nand_hw_common_ecc_ctrl_init(mtd, ecc); + if (ret) + return ret; + + ecc->read_page = sunxi_nfc_hw_ecc_read_page; + ecc->write_page = sunxi_nfc_hw_ecc_write_page; + layout = ecc->layout; + nsectors = mtd->writesize / ecc->size; + + for (i = 0; i < nsectors; i++) { + if (i) { + layout->oobfree[i].offset = + layout->oobfree[i - 1].offset + + layout->oobfree[i - 1].length + + ecc->bytes; + layout->oobfree[i].length = 4; + } else { + /* + * The first 2 bytes are used for BB markers, hence we + * only have 2 bytes available in the first user data + * section. + */ + layout->oobfree[i].length = 2; + layout->oobfree[i].offset = 2; + } + + for (j = 0; j < ecc->bytes; j++) + layout->eccpos[(ecc->bytes * i) + j] = + layout->oobfree[i].offset + + layout->oobfree[i].length + j; + } + + if (mtd->oobsize > (ecc->bytes + 4) * nsectors) { + layout->oobfree[nsectors].offset = + layout->oobfree[nsectors - 1].offset + + layout->oobfree[nsectors - 1].length + + ecc->bytes; + layout->oobfree[nsectors].length = mtd->oobsize - + ((ecc->bytes + 4) * nsectors); + } + + return 0; +} + +static int sunxi_nand_hw_syndrome_ecc_ctrl_init(struct mtd_info *mtd, + struct nand_ecc_ctrl *ecc) +{ + struct nand_ecclayout *layout; + int nsectors; + int i; + int ret; + + ret = sunxi_nand_hw_common_ecc_ctrl_init(mtd, ecc); + if (ret) + return ret; + + ecc->prepad = 4; + ecc->read_page = sunxi_nfc_hw_syndrome_ecc_read_page; + ecc->write_page = sunxi_nfc_hw_syndrome_ecc_write_page; + + layout = ecc->layout; + nsectors = mtd->writesize / ecc->size; + + for (i = 0; i < (ecc->bytes * nsectors); i++) + layout->eccpos[i] = i; + + layout->oobfree[0].length = mtd->oobsize - i; + layout->oobfree[0].offset = i; + + return 0; +} + +/** + * It maps 'enum nand_ecc_modes_t' found in include/linux/mtd/nand.h + * into the device tree binding of 'nand-ecc', so that MTD + * device driver can get nand ecc from device tree. + */ +static const char *nand_ecc_modes[] = { + [NAND_ECC_NONE] = "none", + [NAND_ECC_SOFT] = "soft", + [NAND_ECC_HW] = "hw", + [NAND_ECC_HW_SYNDROME] = "hw_syndrome", + [NAND_ECC_HW_OOB_FIRST] = "hw_oob_first", + [NAND_ECC_SOFT_BCH] = "soft_bch", +}; + +/** + * of_get_nand_ecc_mode - Get nand ecc mode for given device_node + * @np: Pointer to the given device_node + * + * The function gets ecc mode string from property 'nand-ecc-mode', + * and return its index in nand_ecc_modes table, or errno in error case. + */ +static inline int of_get_nand_ecc_mode(int node) +{ + const char *pm; + int len, i; + + pm = fdt_getprop(gd->fdt_blob, node, "nand-ecc-mode", &len); + if (!pm) + return -1; + for (i = 0; i < ARRAY_SIZE(nand_ecc_modes); i++) + if (!strcasecmp(pm, nand_ecc_modes[i])) + return i; + return -1; +} + + /** + * It maps 'enum nand_rnd_modes_t' found in include/linux/mtd/nand.h + * into the device tree binding of 'nand-rnd', so that MTD + * device driver can get nand rnd from device tree. + */ +static const char *nand_rnd_modes[] = { + [NAND_RND_NONE] = "none", + [NAND_RND_SOFT] = "soft", + [NAND_RND_HW] = "hw", +}; + +/** + * of_get_nand_rnd_mode - Get nand randomizer mode for given device_node + * @np: Pointer to the given device_node + * + * The function gets randomizer mode string from property 'nand-rnd-mode', + * and return its index in nand_rnd_modes table, or errno in error case. +*/ +static inline int of_get_nand_rnd_mode(int node) +{ + const char *pm; + int len, i; + + pm = fdt_getprop(gd->fdt_blob, node, "nand-rnd-mode", &len); + if (!pm) + return -1; + for (i = 0; i < ARRAY_SIZE(nand_rnd_modes); i++) + if (!strcasecmp(pm, nand_rnd_modes[i])) + return i; + return -1; +} + +#ifndef __UBOOT__ +static void sunxi_nand_rnd_cleanup(struct nand_rnd_ctrl *rnd) +{ + switch (rnd->mode) { + case NAND_RND_HW: + sunxi_nand_rnd_ctrl_cleanup(rnd); + break; + default: + break; + } +} +#endif + +static int sunxi_nand_rnd_init(int node, struct mtd_info *mtd, + struct nand_rnd_ctrl *rnd, + struct nand_ecc_ctrl *ecc) +{ + int ret; + + rnd->mode = NAND_RND_NONE; + + ret = of_get_nand_rnd_mode(node); + if (ret >= 0) + rnd->mode = ret; + + switch (rnd->mode) { + case NAND_RND_HW: + return sunxi_nand_rnd_ctrl_init(node, mtd, rnd, ecc); + default: + break; + } + + return 0; +} + +#ifndef __UBOOT__ +static void sunxi_nand_ecc_cleanup(struct nand_ecc_ctrl *ecc) +{ + switch (ecc->mode) { + case NAND_ECC_HW: + case NAND_ECC_HW_SYNDROME: + sunxi_nand_hw_common_ecc_ctrl_cleanup(ecc); + break; + case NAND_ECC_NONE: + kfree(ecc->layout); + default: + break; + } +} +#endif + +static int sunxi_nand_ecc_init(int node, struct mtd_info *mtd, + struct nand_ecc_ctrl *ecc) +{ + struct nand_chip *nand = mtd->priv; + s32 strength; + s32 blk_size; + int ret; + + blk_size = fdtdec_get_int(gd->fdt_blob, node, "nand-ecc-step-size", -1); + strength = fdtdec_get_int(gd->fdt_blob, node, "nand-ecc-strength", -1); + if ((blk_size | strength) > -1){ + ecc->size = blk_size; + ecc->strength = strength; + } else { + ecc->size = nand->ecc_step_ds; + ecc->strength = nand->ecc_strength_ds; + } + + ecc->mode = NAND_ECC_HW; + + ret = of_get_nand_ecc_mode(node); + if (ret >= 0) + ecc->mode = ret; + + switch (ecc->mode) { + case NAND_ECC_SOFT_BCH: + if (!ecc->size || !ecc->strength) + return -EINVAL; + ecc->bytes = ((ecc->strength * fls(8 * ecc->size)) + 7) / 8; + break; + case NAND_ECC_HW: + ret = sunxi_nand_hw_ecc_ctrl_init(mtd, ecc); + if (ret) + return ret; + break; + case NAND_ECC_HW_SYNDROME: + ret = sunxi_nand_hw_syndrome_ecc_ctrl_init(mtd, ecc); + if (ret) + return ret; + break; + case NAND_ECC_NONE: + ecc->layout = kzalloc(sizeof(*ecc->layout), GFP_KERNEL); + if (!ecc->layout) + return -ENOMEM; + ecc->layout->oobfree[0].length = mtd->oobsize; + case NAND_ECC_SOFT: + break; + default: + return -EINVAL; + } + + return 0; +} + +static int sunxi_nand_chip_init(int node, struct sunxi_nfc *nfc, int devnum) +{ + const struct nand_sdr_timings *timings; + struct sunxi_nand_chip *chip; + struct mtd_info *mtd; + struct nand_chip *nand; + int nsels; + int ret; + int i; + u32 tmp[8]; + + if(!fdt_getprop(gd->fdt_blob, node, "reg", &nsels)) + return -EINVAL; + + nsels /= sizeof(u32); + if (!nsels) + return -EINVAL; + + chip = kzalloc(sizeof(*chip) + + (nsels * sizeof(struct sunxi_nand_chip_sel)), + GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->nsels = nsels; + chip->selected = -1; + + for (i = 0; i < nsels; i++) { + ret = fdtdec_get_int_array(gd->fdt_blob, node, "reg", tmp, + nsels); + if(ret) + return ret; + + if (tmp[i] > 7) + return -EINVAL; + + if (test_and_set_bit(tmp[i], &nfc->assigned_cs)) + return -EINVAL; + + chip->sels[i].cs = tmp[i]; + + if(fdtdec_get_int_array(gd->fdt_blob, node, "allwinner,rb", tmp, + nsels) && tmp[i] < 2){ + chip->sels[i].rb.type = RB_NATIVE; + chip->sels[i].rb.info.nativeid = tmp[i]; + } else { + chip->sels[i].rb.type = RB_NONE; + } + } + + timings = onfi_async_timing_mode_to_sdr_timings(0); + if (IS_ERR(timings)) + return PTR_ERR(timings); + + ret = sunxi_nand_chip_set_timings(chip, timings); + + nand = &chip->nand; + + nand->chip_delay = 200; + nand->controller = &nfc->controller; + nand->select_chip = sunxi_nfc_select_chip; + nand->cmd_ctrl = sunxi_nfc_cmd_ctrl; + nand->read_buf = sunxi_nfc_read_buf; + nand->write_buf = sunxi_nfc_write_buf; + nand->read_byte = sunxi_nfc_read_byte; + + if(fdtdec_get_bool(gd->fdt_blob, node, "nand-on-flash-bbt")) + nand->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB; + + mtd = &nand_info[devnum]; + mtd->priv = nand; + + + ret = nand_scan_ident(mtd, nsels, NULL); + if (ret){ + return ret; + } + + chip->buffer = kzalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL); + if (!chip->buffer) + return -ENOMEM; + + ret = sunxi_nand_chip_init_timings(chip); + if (ret) + return ret; + + ret = nand_pst_create(mtd); + if (ret) + return ret; + + ret = sunxi_nand_ecc_init(node, mtd, &nand->ecc); + printf("%d\n", ret); + if (ret) + return ret; + + ret = sunxi_nand_rnd_init(node, mtd, &nand->rnd, &nand->ecc); + if (ret) + return ret; + + ret = nand_scan_tail(mtd); + if (ret) + return ret; + + /*if(fdt_getprop(gd->fdt_blob, node, "nand-name", NULL)){ + snprintf(chip->default_name, MAX_NAME_SIZE, + DEFAULT_NAME_FORMAT, chip->sels[i].cs); + mtd->name = chip->default_name; + }*/ + + ret = nand_register(devnum); + + if (ret){ + pr_err("%s:failed to register\n", __func__); + return ret; + } + + list_add_tail(&chip->node, &nfc->chips); + + return 0; +} + +#ifndef __UBOOT__ +static void sunxi_nand_chips_cleanup(struct sunxi_nfc *nfc) +{ + struct sunxi_nand_chip *chip; + + while (!list_empty(&nfc->chips)) { + chip = list_first_entry(&nfc->chips, struct sunxi_nand_chip, + node); + nand_release(&chip->mtd); + sunxi_nand_ecc_cleanup(&chip->nand.ecc); + sunxi_nand_rnd_cleanup(&chip->nand.rnd); + kfree(chip->buffer); + } +} +#endif /* !__UBOOT__ */ + +static int sunxi_nand_chips_init(int node, struct sunxi_nfc *nfc) +{ + int ret, offset; + /*TODO check maximum chips*/ + for (offset = fdt_first_subnode(gd->fdt_blob, node); + offset >= 0; + offset = fdt_next_subnode(gd->fdt_blob, offset)) { + ret = sunxi_nand_chip_init(offset, nfc, 0 ); + if (ret) + return ret; + } + + return 0; +} + +static int sunxi_nfc_init(struct sunxi_nfc *nfc) +{ + int node; + int ret; + + spin_lock_init(&nfc->controller.lock); + init_waitqueue_head(&nfc->controller.wq); + INIT_LIST_HEAD(&nfc->chips); + + node = fdtdec_next_compatible(gd->fdt_blob, 0, + COMPAT_SUNXI_NAND); + if (node < 0){ + pr_err("%s:unable to find nfc in device tree\n", __func__); + goto err; + } + + if(!fdtdec_get_is_enabled(gd->fdt_blob, node)){ + pr_err("%s:nfc disabled in device tree\n", __func__); + goto err; + } + + nfc->regs = (struct sunxi_nand * const)fdtdec_get_addr(gd->fdt_blob, + node, "reg"); + if ((fdt_addr_t)nfc->regs == FDT_ADDR_T_NONE) { + pr_err("%s:unable to find nfc address in device tree\n", __func__); + goto err; + } + + /* clock enable*/ + sunxi_set_clk_rate(NAND_MAX_CLOCK); + sunxi_nfc_rst(nfc); + + writel(0, &nfc->regs->intr); + + /* + * TODO: replace these magic values with proper flags as soon as we + * know what they are encoding. + */ + writel(0x100, &nfc->regs->timing_ctl); + writel(0x7ff, &nfc->regs->timing_cfg); + + ret = sunxi_nand_chips_init(node, nfc); + if (ret) { + pr_err("%s:failed to init nand chips\n", __func__); + goto err; + } + + return 0; + +err: + kfree(nfc); + return ret; +} + +void sunxi_nand_init(void) +{ + struct sunxi_nfc *nfc; + + nfc = kzalloc(sizeof(*nfc), GFP_KERNEL); + if (!nfc) + return; + + sunxi_nfc_init(nfc); +} + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Boris BREZILLON"); +MODULE_DESCRIPTION("Allwinner NAND Flash Controller driver"); diff --git a/include/fdtdec.h b/include/fdtdec.h index 6590470..c0662d9 100644 --- a/include/fdtdec.h +++ b/include/fdtdec.h @@ -177,6 +177,7 @@ enum fdt_compat_id { COMPAT_INTEL_QRK_MRC, /* Intel Quark MRC */ COMPAT_SOCIONEXT_XHCI, /* Socionext UniPhier xHCI */ COMPAT_INTEL_PCH, /* Intel PCH */ + COMPAT_SUNXI_NAND, /* SUNXI NAND controller */
COMPAT_COUNT, }; @@ -567,6 +568,18 @@ const char *fdtdec_get_compatible(enum fdt_compat_id id); int fdtdec_lookup_phandle(const void *blob, int node, const char *prop_name);
/** + * @param blob FDT blob + * @param node node to examine + * @param prop_name name of property to find + * @param array array to fill with data + * @param count number of array elements + * @return 0 if ok, or -FDT_ERR_NOTFOUND if the property is not found, + * or -FDT_ERR_BADLAYOUT if not enough data + */ +int fdtdec_get_u16_array(const void *blob, int node, const char *prop_name, + u16 *array, int count); + +/** * Look up a property in a node and return its contents in an integer * array of given length. The property must have at least enough data for * the array (4*count bytes). It may have more, but this will be ignored. diff --git a/lib/fdtdec.c b/lib/fdtdec.c index 80b897a..45dc9fd 100644 --- a/lib/fdtdec.c +++ b/lib/fdtdec.c @@ -76,6 +76,7 @@ static const char * const compat_names[COMPAT_COUNT] = { COMPAT(INTEL_QRK_MRC, "intel,quark-mrc"), COMPAT(SOCIONEXT_XHCI, "socionext,uniphier-xhci"), COMPAT(COMPAT_INTEL_PCH, "intel,bd82x6x"), + COMPAT(COMPAT_SUNXI_NAND, "allwinner,sun4i-nand"), };
const char *fdtdec_get_compatible(enum fdt_compat_id id) @@ -618,6 +619,22 @@ static const void *get_prop_check_min_len(const void *blob, int node, return cell; }
+int fdtdec_get_u16_array(const void *blob, int node, const char *prop_name, + u16 *array, int count) +{ + const u16 *cell; + int i, err = 0; + + debug("%s: %s\n", __func__, prop_name); + cell = get_prop_check_min_len(blob, node, prop_name, + sizeof(u16) * count, &err); + if (!err) { + for (i = 0; i < count; i++) + array[i] = be16_to_cpu(cell[i]); + } + return err; +} + int fdtdec_get_int_array(const void *blob, int node, const char *prop_name, u32 *array, int count) {

On Fri, 5 Jun 2015 13:52:40 +0200 Roy Spliet r.spliet@ultimaker.com wrote:
Heavily based on BBrezillon's (downstream) driver. Most noticable differences
- No per-partition ECC settings. Partitions in U-boot are quite different from Linux
- U-boot register definitions, shared with sunxi_nand_spl
- FDT parsing in-line, there's no framework method yet
I'll try to review this driver next week.

On Sun, 14 Jun 2015 13:42:12 +0200 Boris Brezillon boris.brezillon@free-electrons.com wrote:
On Fri, 5 Jun 2015 13:52:40 +0200 Roy Spliet r.spliet@ultimaker.com wrote:
Heavily based on BBrezillon's (downstream) driver. Most noticable differences
- No per-partition ECC settings. Partitions in U-boot are quite different from Linux
- U-boot register definitions, shared with sunxi_nand_spl
- FDT parsing in-line, there's no framework method yet
I'll try to review this driver next week.
BTW, I don't know if you've based you work on Yassin's work, but if that's the case, then you should at least mention it in the license header, and maybe you should consider adding a S-o-B line with his name.

Signed-off-by: Roy Spliet r.spliet@ultimaker.com --- arch/arm/dts/sun7i-a20-olinuxino-lime.dts | 41 ++++++++++++++ arch/arm/dts/sun7i-a20.dtsi | 90 +++++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+)
diff --git a/arch/arm/dts/sun7i-a20-olinuxino-lime.dts b/arch/arm/dts/sun7i-a20-olinuxino-lime.dts index 6592cb2..cc5e65d 100644 --- a/arch/arm/dts/sun7i-a20-olinuxino-lime.dts +++ b/arch/arm/dts/sun7i-a20-olinuxino-lime.dts @@ -181,3 +181,44 @@ usb2_vbus-supply = <®_usb2_vbus>; status = "okay"; }; + +&nfc { + pinctrl-names = "default"; + pinctrl-0 = <&nand_pins_a &nand_cs0_pins_a &nand_rb0_pins_a>; + status = "okay"; + + nand@0 { + #address-cells = <2>; + #size-cells = <2>; + reg = <0>; + allwinner,rb = <0>; + nand-ecc-mode = "hw"; + nand-ecc-strength = <40>; + nand-ecc-step-size = <1024>; + nand-rnd-mode = "hw"; + nand-randomizer-seeds = /bits/ 16 < + 0x2b75 0x0bd0 0x5ca3 0x62d1 0x1c93 0x07e9 0x2162 0x3a72 + 0x0d67 0x67f9 0x1be7 0x077d 0x032f 0x0dac 0x2716 0x2436 + 0x7922 0x1510 0x3860 0x5287 0x480f 0x4252 0x1789 0x5a2d + 0x2a49 0x5e10 0x437f 0x4b4e 0x2f45 0x216e 0x5cb7 0x7130 + 0x2a3f 0x60e4 0x4dc9 0x0ef0 0x0f52 0x1bb9 0x6211 0x7a56 + 0x226d 0x4ea7 0x6f36 0x3692 0x38bf 0x0c62 0x05eb 0x4c55 + 0x60f4 0x728c 0x3b6f 0x2037 0x7f69 0x0936 0x651a 0x4ceb + 0x6218 0x79f3 0x383f 0x18d9 0x4f05 0x5c82 0x2912 0x6f17 + 0x6856 0x5938 0x1007 0x61ab 0x3e7f 0x57c2 0x542f 0x4f62 + 0x7454 0x2eac 0x7739 0x42d4 0x2f90 0x435a 0x2e52 0x2064 + 0x637c 0x66ad 0x2c90 0x0bad 0x759c 0x0029 0x0986 0x7126 + 0x1ca7 0x1605 0x386a 0x27f5 0x1380 0x6d75 0x24c3 0x0f8e + 0x2b7a 0x1418 0x1fd1 0x7dc1 0x2d8e 0x43af 0x2267 0x7da3 + 0x4e3d 0x1338 0x50db 0x454d 0x764d 0x40a3 0x42e6 0x262b + 0x2d2e 0x1aea 0x2e17 0x173d 0x3a6e 0x71bf 0x25f9 0x0a5d + 0x7c57 0x0fbe 0x46ce 0x4939 0x6b17 0x37bb 0x3e91 0x76db>; + onfi,nand-timing-mode = <0x1f>; +/* + main@400000 { + label = "main"; + reg = /bits/ 64 <0x400000 0xffc00000>; + }; +*/ + }; +}; diff --git a/arch/arm/dts/sun7i-a20.dtsi b/arch/arm/dts/sun7i-a20.dtsi index d4ba772..af89575 100644 --- a/arch/arm/dts/sun7i-a20.dtsi +++ b/arch/arm/dts/sun7i-a20.dtsi @@ -612,6 +612,17 @@ clocks = <&ahb_gates 17>; status = "disabled"; }; + + nfc: nand@01c03000 { + compatible = "allwinner,sun4i-nand"; + reg = <0x01c03000 0x1000>; + interrupts = <0 37 4>; + clocks = <&ahb_gates 13>, <&nand_clk>; + clock-names = "ahb", "mod"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + };
mdio: mdio@01c0b080 { compatible = "allwinner,sun4i-a10-mdio"; @@ -1020,6 +1031,85 @@ allwinner,drive = <SUN4I_PINCTRL_10_MA>; allwinner,pull = <SUN4I_PINCTRL_NO_PULL>; }; + nand_pins_a: nand_base0@0 { + allwinner,pins = "PC0", "PC1", "PC2", + "PC5", "PC8", "PC9", "PC10", + "PC11", "PC12", "PC13", "PC14", + "PC15", "PC16"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_cs0_pins_a: nand_cs@0 { + allwinner,pins = "PC4"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_cs1_pins_a: nand_cs@1 { + allwinner,pins = "PC3"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_cs2_pins_a: nand_cs@2 { + allwinner,pins = "PC17"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_cs3_pins_a: nand_cs@3 { + allwinner,pins = "PC18"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_cs4_pins_a: nand_cs@4 { + allwinner,pins = "PC19"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_cs5_pins_a: nand_cs@5 { + allwinner,pins = "PC20"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_cs6_pins_a: nand_cs@6 { + allwinner,pins = "PC21"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_cs7_pins_a: nand_cs@7 { + allwinner,pins = "PC22"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_rb0_pins_a: nand_rb@0 { + allwinner,pins = "PC6"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; + + nand_rb1_pins_a: nand_rb@1 { + allwinner,pins = "PC7"; + allwinner,function = "nand0"; + allwinner,drive = <0>; + allwinner,pull = <0>; + }; };
timer@01c20c00 {

On Fri, 5 Jun 2015 13:52:41 +0200 Roy Spliet r.spliet@ultimaker.com wrote:
Signed-off-by: Roy Spliet r.spliet@ultimaker.com
arch/arm/dts/sun7i-a20-olinuxino-lime.dts | 41 ++++++++++++++ arch/arm/dts/sun7i-a20.dtsi | 90 +++++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+)
diff --git a/arch/arm/dts/sun7i-a20-olinuxino-lime.dts b/arch/arm/dts/sun7i-a20-olinuxino-lime.dts index 6592cb2..cc5e65d 100644 --- a/arch/arm/dts/sun7i-a20-olinuxino-lime.dts +++ b/arch/arm/dts/sun7i-a20-olinuxino-lime.dts @@ -181,3 +181,44 @@ usb2_vbus-supply = <®_usb2_vbus>; status = "okay"; };
+&nfc {
- pinctrl-names = "default";
- pinctrl-0 = <&nand_pins_a &nand_cs0_pins_a &nand_rb0_pins_a>;
- status = "okay";
- nand@0 {
#address-cells = <2>;
#size-cells = <2>;
reg = <0>;
allwinner,rb = <0>;
nand-ecc-mode = "hw";
nand-ecc-strength = <40>;
nand-ecc-step-size = <1024>;
nand-rnd-mode = "hw";
nand-randomizer-seeds = /bits/ 16 <
0x2b75 0x0bd0 0x5ca3 0x62d1 0x1c93 0x07e9 0x2162 0x3a72
0x0d67 0x67f9 0x1be7 0x077d 0x032f 0x0dac 0x2716 0x2436
0x7922 0x1510 0x3860 0x5287 0x480f 0x4252 0x1789 0x5a2d
0x2a49 0x5e10 0x437f 0x4b4e 0x2f45 0x216e 0x5cb7 0x7130
0x2a3f 0x60e4 0x4dc9 0x0ef0 0x0f52 0x1bb9 0x6211 0x7a56
0x226d 0x4ea7 0x6f36 0x3692 0x38bf 0x0c62 0x05eb 0x4c55
0x60f4 0x728c 0x3b6f 0x2037 0x7f69 0x0936 0x651a 0x4ceb
0x6218 0x79f3 0x383f 0x18d9 0x4f05 0x5c82 0x2912 0x6f17
0x6856 0x5938 0x1007 0x61ab 0x3e7f 0x57c2 0x542f 0x4f62
0x7454 0x2eac 0x7739 0x42d4 0x2f90 0x435a 0x2e52 0x2064
0x637c 0x66ad 0x2c90 0x0bad 0x759c 0x0029 0x0986 0x7126
0x1ca7 0x1605 0x386a 0x27f5 0x1380 0x6d75 0x24c3 0x0f8e
0x2b7a 0x1418 0x1fd1 0x7dc1 0x2d8e 0x43af 0x2267 0x7da3
0x4e3d 0x1338 0x50db 0x454d 0x764d 0x40a3 0x42e6 0x262b
0x2d2e 0x1aea 0x2e17 0x173d 0x3a6e 0x71bf 0x25f9 0x0a5d
0x7c57 0x0fbe 0x46ce 0x4939 0x6b17 0x37bb 0x3e91 0x76db>;
onfi,nand-timing-mode = <0x1f>;
Can we use the same binding we have in mainline Linux (not my branch). In mainline, the onfi,nand-timing-mode has been dropped in favor of ONFI timing mode definition in the nand_ids table. ECC strength and step size should also be extracted from the nand_ids table, unless you want to force something different (which doesn't seem to be the case here). Moreover, the randomizer stuff haven't been reviewed/accepted yet. To support the randomizer stuff in the meantime, you could define the randomizer-seeds table in the board config header and use sunxi specific commands to select the randomizer mode and randomizer seeds table (if you have several tables).

Signed-off-by: Roy Spliet r.spliet@ultimaker.com --- board/sunxi/Kconfig | 8 +++----- drivers/mtd/Makefile | 1 + include/configs/sunxi-common.h | 33 +++++++++++++++++++++++++-------- 3 files changed, 29 insertions(+), 13 deletions(-)
diff --git a/board/sunxi/Kconfig b/board/sunxi/Kconfig index cf58d73..37bbd0f 100644 --- a/board/sunxi/Kconfig +++ b/board/sunxi/Kconfig @@ -269,17 +269,15 @@ config MMC_SUNXI_SLOT_EXTRA slot or emmc on mmc1 - mmc3. Setting this to 1, 2 or 3 will enable support for this.
-config SPL_NAND_SUPPORT - bool "SPL/NAND mode support" +config NAND_SUNXI + bool "NAND support" depends on SPL default n ---help--- This enables support for booting from NAND internal memory. U-Boot SPL doesn't detect where is it load from, therefore this option is needed to properly load image from - flash. Option also disables MMC functionality on U-Boot due to - initialization errors encountered, when both controllers are - enabled. + flash.
config USB0_VBUS_PIN string "Vbus enable pin for usb0 (otg)" diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 5467a95..124b0f9 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -18,3 +18,4 @@ obj-$(CONFIG_FTSMC020) += ftsmc020.o obj-$(CONFIG_FLASH_CFI_LEGACY) += jedec_flash.o obj-$(CONFIG_MW_EEPROM) += mw_eeprom.o obj-$(CONFIG_ST_SMI) += st_smi.o +obj-$(CONFIG_MTD_UBI) += ubi/ diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h index cce0441..ec28c40 100644 --- a/include/configs/sunxi-common.h +++ b/include/configs/sunxi-common.h @@ -113,7 +113,7 @@ #endif
/* 4MB of malloc() pool */ -#define CONFIG_SYS_MALLOC_LEN (CONFIG_ENV_SIZE + (4 << 20)) +#define CONFIG_SYS_MALLOC_LEN (CONFIG_ENV_SIZE + (64 << 20))
/* * Miscellaneous configurable options @@ -326,10 +326,31 @@ extern int soft_i2c_gpio_scl; #define CONFIG_ENV_IS_NOWHERE #endif
-#ifdef CONFIG_SPL_NAND_SUPPORT +#ifdef CONFIG_NAND_SUNXI +#ifndef CONFIG_NAND_SUNXI_GPC_PORTS +#error "No NAND GPC ports defined, NAND unsupported" +#endif /* CONFIG_NAND_SUNXI_GPC_PORTS */ #define CONFIG_NAND #define CONFIG_SYS_NAND_SELF_INIT -#define CONFIG_NAND_SUNXI +#define CONFIG_SYS_MAX_NAND_DEVICE 8 + +#define CONFIG_SYS_NAND_ONFI_DETECTION + +/* Requirements for UBI */ +#define CONFIG_RBTREE +#define CONFIG_LZO +#define CONFIG_CMD_MTDPARTS +#define CONFIG_CMD_UBI +#define CONFIG_CMD_UBIFS +#define CONFIG_MTD_DEVICE + +#define CONFIG_SPL_NAND_SUPPORT + +#define CONFIG_MTD_PARTITIONS +/* +#define CONFIG_MTD_DEBUG +#define CONFIG_MTD_DEBUG_VERBOSE MTD_DEBUG_LEVEL3 +*/ #define CONFIG_CMD_SPL_WRITE_SIZE 0x000400 #define CONFIG_SYS_NAND_U_BOOT_OFFS 0x008000
@@ -338,11 +359,7 @@ extern int soft_i2c_gpio_scl; #define CONFIG_NAND_SUNXI_ECC_STEP 1024 #define CONFIG_NAND_SUNXI_ECC_STRENGTH 40 #define CONFIG_NAND_SUNXI_ADDR_CYCLES 5 - -#ifndef CONFIG_NAND_SUNXI_GPC_PORTS -#error "No NAND GPC ports defined, NAND unsupported" -#endif -#endif /* CONFIG_SPL_NAND_SUPPORT */ +#endif /* CONFIG_NAND_SUNXI */
#define CONFIG_MISC_INIT_R #define CONFIG_SYS_CONSOLE_IS_IN_ENV

Hi,
On 05-06-15 13:52, Roy Spliet wrote:
Signed-off-by: Roy Spliet r.spliet@ultimaker.com
board/sunxi/Kconfig | 8 +++----- drivers/mtd/Makefile | 1 + include/configs/sunxi-common.h | 33 +++++++++++++++++++++++++-------- 3 files changed, 29 insertions(+), 13 deletions(-)
diff --git a/board/sunxi/Kconfig b/board/sunxi/Kconfig index cf58d73..37bbd0f 100644 --- a/board/sunxi/Kconfig +++ b/board/sunxi/Kconfig @@ -269,17 +269,15 @@ config MMC_SUNXI_SLOT_EXTRA slot or emmc on mmc1 - mmc3. Setting this to 1, 2 or 3 will enable support for this.
-config SPL_NAND_SUPPORT
- bool "SPL/NAND mode support"
+config NAND_SUNXI
- bool "NAND support" depends on SPL default n ---help--- This enables support for booting from NAND internal memory. U-Boot SPL doesn't detect where is it load from, therefore this option is needed to properly load image from
flash. Option also disables MMC functionality on U-Boot due to
initialization errors encountered, when both controllers are
enabled.
flash.
config USB0_VBUS_PIN string "Vbus enable pin for usb0 (otg)"
diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 5467a95..124b0f9 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -18,3 +18,4 @@ obj-$(CONFIG_FTSMC020) += ftsmc020.o obj-$(CONFIG_FLASH_CFI_LEGACY) += jedec_flash.o obj-$(CONFIG_MW_EEPROM) += mw_eeprom.o obj-$(CONFIG_ST_SMI) += st_smi.o +obj-$(CONFIG_MTD_UBI) += ubi/ diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h index cce0441..ec28c40 100644 --- a/include/configs/sunxi-common.h +++ b/include/configs/sunxi-common.h @@ -113,7 +113,7 @@ #endif
/* 4MB of malloc() pool */ -#define CONFIG_SYS_MALLOC_LEN (CONFIG_ENV_SIZE + (4 << 20)) +#define CONFIG_SYS_MALLOC_LEN (CONFIG_ENV_SIZE + (64 << 20))
/*
- Miscellaneous configurable options
Please update the comment about malloc_pool size to match :)
Regards,
Hans
@@ -326,10 +326,31 @@ extern int soft_i2c_gpio_scl; #define CONFIG_ENV_IS_NOWHERE #endif
-#ifdef CONFIG_SPL_NAND_SUPPORT +#ifdef CONFIG_NAND_SUNXI +#ifndef CONFIG_NAND_SUNXI_GPC_PORTS +#error "No NAND GPC ports defined, NAND unsupported" +#endif /* CONFIG_NAND_SUNXI_GPC_PORTS */ #define CONFIG_NAND #define CONFIG_SYS_NAND_SELF_INIT -#define CONFIG_NAND_SUNXI +#define CONFIG_SYS_MAX_NAND_DEVICE 8
+#define CONFIG_SYS_NAND_ONFI_DETECTION
+/* Requirements for UBI */ +#define CONFIG_RBTREE +#define CONFIG_LZO +#define CONFIG_CMD_MTDPARTS +#define CONFIG_CMD_UBI +#define CONFIG_CMD_UBIFS +#define CONFIG_MTD_DEVICE
+#define CONFIG_SPL_NAND_SUPPORT
+#define CONFIG_MTD_PARTITIONS +/* +#define CONFIG_MTD_DEBUG +#define CONFIG_MTD_DEBUG_VERBOSE MTD_DEBUG_LEVEL3 +*/ #define CONFIG_CMD_SPL_WRITE_SIZE 0x000400 #define CONFIG_SYS_NAND_U_BOOT_OFFS 0x008000
@@ -338,11 +359,7 @@ extern int soft_i2c_gpio_scl; #define CONFIG_NAND_SUNXI_ECC_STEP 1024 #define CONFIG_NAND_SUNXI_ECC_STRENGTH 40 #define CONFIG_NAND_SUNXI_ADDR_CYCLES 5
-#ifndef CONFIG_NAND_SUNXI_GPC_PORTS -#error "No NAND GPC ports defined, NAND unsupported" -#endif -#endif /* CONFIG_SPL_NAND_SUPPORT */ +#endif /* CONFIG_NAND_SUNXI */
#define CONFIG_MISC_INIT_R #define CONFIG_SYS_CONSOLE_IS_IN_ENV

On Sat, 2015-06-06 at 17:13 +0200, Hans de Goede wrote:
@@ -113,7 +113,7 @@ #endif
/* 4MB of malloc() pool */ -#define CONFIG_SYS_MALLOC_LEN (CONFIG_ENV_SIZE + (4 << 20)) +#define CONFIG_SYS_MALLOC_LEN (CONFIG_ENV_SIZE + (64 << 20))
/*
- Miscellaneous configurable options
Please update the comment about malloc_pool size to match :)
...and explain in the commit log why this needs increasing as part of this change.
Ian.

Oh yes, thanks for pointing this one out. I increased the malloc pool during debugging as google hinted towards UBI being rather memory-hungry, but forgot to double-check whether it was required. I just did, and as it turns out it really only needs to be increased to 8MiB for UBI to work in my set-up. As we have plenty of memory anyway, should I allocate some extra headroom for set-ups I haven't tested (e.g. many partitions, large bad-block tables etc.) and go for 16MiB instead?
Roy
Op 06-06-15 om 17:36 schreef Ian Campbell:
On Sat, 2015-06-06 at 17:13 +0200, Hans de Goede wrote:
@@ -113,7 +113,7 @@ #endif
/* 4MB of malloc() pool */ -#define CONFIG_SYS_MALLOC_LEN (CONFIG_ENV_SIZE + (4 << 20)) +#define CONFIG_SYS_MALLOC_LEN (CONFIG_ENV_SIZE + (64 << 20))
/* * Miscellaneous configurable options
Please update the comment about malloc_pool size to match :)
...and explain in the commit log why this needs increasing as part of this change.
Ian.

On Mon, 2015-06-08 at 09:38 +0200, Roy Spliet wrote:
Please try and avoid sending HTML Mail as well as top posting.
Oh yes, thanks for pointing this one out. I increased the malloc pool during debugging as google hinted towards UBI being rather memory-hungry, but forgot to double-check whether it was required. I just did, and as it turns out it really only needs to be increased to 8MiB for UBI to work in my set-up. As we have plenty of memory anyway, should I allocate some extra headroom for set-ups I haven't tested (e.g. many partitions, large bad-block tables etc.) and go for 16MiB instead?
Either value WFM if the rationale for it is in the commit message.
Ian.

Assumes a UBI partition called boot
Signed-off-by: Roy Spliet r.spliet@ultimaker.com --- include/config_distro_bootcmd.h | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+)
diff --git a/include/config_distro_bootcmd.h b/include/config_distro_bootcmd.h index 3a360ca4..361b914 100644 --- a/include/config_distro_bootcmd.h +++ b/include/config_distro_bootcmd.h @@ -72,6 +72,21 @@ BOOT_TARGET_DEVICES_references_MMC_without_CONFIG_CMD_MMC #endif
+#ifdef CONFIG_CMD_NAND +#define BOOTENV_DEV_NAND(devtypeu, devtypel, instance) \ + "bootcmd_nand=" \ + "ubi part " #devtypel #instance "_main; " \ + "ubifsmount ubi:boot; " \ + "run scan_ubifs_for_script\0" +#define BOOTENV_DEV_NAME_NAND(devtypeu, devtypel, instance) #devtypel " " +#else +#define BOOTENV_SHARED_NAND +#define BOOTENV_DEV_NAND \ + BOOT_TARGET_DEVICES_references_NAND_without_CONFIG_CMD_NAND +#define BOOTENV_DEV_NAME_NAND \ + BOOT_TARGET_DEVICES_references_NAND_without_CONFIG_CMD_NAND +#endif + #ifdef CONFIG_CMD_SATA #define BOOTENV_SHARED_SATA BOOTENV_SHARED_BLKDEV(sata) #define BOOTENV_DEV_SATA BOOTENV_DEV_BLKDEV @@ -202,6 +217,17 @@ "echo SCRIPT FAILED: continuing...; " \ "fi\0" \ \ + "scan_ubifs_for_script=" \ + "for script in ${boot_scripts}; do " \ + "for prefix in ${boot_prefixes}; do " \ + "if ubifsload ${scriptaddr} " \ + "${prefix}${script}; then " \ + "echo Found U-Boot script " \ + "${prefix}${script}; " \ + "source ${scriptaddr}; " \ + "fi; " \ + "done; " \ + "done\0" \ "boot_a_script=" \ "load ${devtype} ${devnum}:${bootpart} " \ "${scriptaddr} ${prefix}${script}; " \

Based on the default layout of the android image used at least on Olimex Lime
Signed-off-by: Roy Spliet r.spliet@ultimaker.com --- include/configs/sunxi-common.h | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h index ec28c40..b38f2f5 100644 --- a/include/configs/sunxi-common.h +++ b/include/configs/sunxi-common.h @@ -404,8 +404,15 @@ extern int soft_i2c_gpio_scl; #define BOOT_TARGET_DEVICES_USB(func) #endif
+#ifdef CONFIG_NAND +#define BOOT_TARGET_DEVICES_NAND(func) func(NAND, nand , 0) +#else +#define BOOT_TARGET_DEVICES_NAND(func) +#endif + #define BOOT_TARGET_DEVICES(func) \ BOOT_TARGET_DEVICES_MMC(func) \ + BOOT_TARGET_DEVICES_NAND(func) \ BOOT_TARGET_DEVICES_SCSI(func) \ BOOT_TARGET_DEVICES_USB(func) \ func(PXE, pxe, na) \ @@ -441,6 +448,8 @@ extern int soft_i2c_gpio_scl; MEM_LAYOUT_ENV_SETTINGS \ "fdtfile=" CONFIG_DEFAULT_DEVICE_TREE ".dtb\0" \ "console=ttyS0,115200\0" \ + "mtdids=nand0=mtd2\0" \ + "mtdparts=mtdparts=mtd2:0xffc00000@0x400000(nand0_main)\0" \ BOOTENV
#else /* ifndef CONFIG_SPL_BUILD */

Hello,
On 5 June 2015 at 13:52, Roy Spliet r.spliet@ultimaker.com wrote:
Based on the default layout of the android image used at least on Olimex Lime
Signed-off-by: Roy Spliet r.spliet@ultimaker.com
include/configs/sunxi-common.h | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h index ec28c40..b38f2f5 100644 --- a/include/configs/sunxi-common.h +++ b/include/configs/sunxi-common.h @@ -404,8 +404,15 @@ extern int soft_i2c_gpio_scl; #define BOOT_TARGET_DEVICES_USB(func) #endif
+#ifdef CONFIG_NAND +#define BOOT_TARGET_DEVICES_NAND(func) func(NAND, nand , 0) +#else +#define BOOT_TARGET_DEVICES_NAND(func) +#endif
#define BOOT_TARGET_DEVICES(func) \ BOOT_TARGET_DEVICES_MMC(func) \
BOOT_TARGET_DEVICES_NAND(func) \ BOOT_TARGET_DEVICES_SCSI(func) \ BOOT_TARGET_DEVICES_USB(func) \ func(PXE, pxe, na) \
@@ -441,6 +448,8 @@ extern int soft_i2c_gpio_scl; MEM_LAYOUT_ENV_SETTINGS \ "fdtfile=" CONFIG_DEFAULT_DEVICE_TREE ".dtb\0" \ "console=ttyS0,115200\0" \
"mtdids=nand0=mtd2\0" \
"mtdparts=mtdparts=mtd2:0xffc00000@0x400000(nand0_main)\0" \ BOOTENV
From what I heard the nand boot partition size should be specified in
nand pages rather than bytes because the boot rom loads a fixed number of pages and just uses the start of each page regardless of page size.
I did not find any document regarding the nand boot partition layout so I would like to see some input from somebody familiar with the driver.
While it is fine for testing to hand-edit the environment the final nand support should have
1) way to express the boot partition size in nand pages 2) way to make the main partition start at the end of boot partition and extend to the end of the flash
This should probably also go to Linux.
Thanks
Michal

Hello Michal,
Op 07-06-15 om 18:48 schreef Michal Suchanek:
Hello,
On 5 June 2015 at 13:52, Roy Spliet r.spliet@ultimaker.com wrote:
Based on the default layout of the android image used at least on Olimex Lime
Signed-off-by: Roy Spliet r.spliet@ultimaker.com
include/configs/sunxi-common.h | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h index ec28c40..b38f2f5 100644 --- a/include/configs/sunxi-common.h +++ b/include/configs/sunxi-common.h @@ -404,8 +404,15 @@ extern int soft_i2c_gpio_scl; #define BOOT_TARGET_DEVICES_USB(func) #endif
+#ifdef CONFIG_NAND +#define BOOT_TARGET_DEVICES_NAND(func) func(NAND, nand , 0) +#else +#define BOOT_TARGET_DEVICES_NAND(func) +#endif
- #define BOOT_TARGET_DEVICES(func) \ BOOT_TARGET_DEVICES_MMC(func) \
BOOT_TARGET_DEVICES_NAND(func) \ BOOT_TARGET_DEVICES_SCSI(func) \ BOOT_TARGET_DEVICES_USB(func) \ func(PXE, pxe, na) \
@@ -441,6 +448,8 @@ extern int soft_i2c_gpio_scl; MEM_LAYOUT_ENV_SETTINGS \ "fdtfile=" CONFIG_DEFAULT_DEVICE_TREE ".dtb\0" \ "console=ttyS0,115200\0" \
"mtdids=nand0=mtd2\0" \
"mtdparts=mtdparts=mtd2:0xffc00000@0x400000(nand0_main)\0" \ BOOTENV
From what I heard the nand boot partition size should be specified in nand pages rather than bytes because the boot rom loads a fixed number of pages and just uses the start of each page regardless of page size.
Although I believe you have the facts mostly right, they do not apply in this situation. What you are looking for is the partition definition for U-boot. At this point the boot rom (BROM) is no longer active and thus it's inner workings are mostly irrelevant. The NAND partition lay-out for the boards I have seen (various Olimex + Cubietruck..) are as follows: 0-2MB U-Boot-SPL + U-Boot 2-4MB U-Boot SPL + U-Boot (for recovery) 4MB+ Main file system
The mtdparts env variable defines only the main file system because that's the only place with relevant files for U-Boot. This said: I have replaced the default file system with a "custom" UBI FS containing just a boot and a rootfs partition. I am unsure whether these defaults apply for the Android image that ships with the Olimex boards. But then again: that image also ships with a boot-loader, so do we care?
I did not find any document regarding the nand boot partition layout so I would like to see some input from somebody familiar with the driver.
While it is fine for testing to hand-edit the environment the final nand support should have
- way to express the boot partition size in nand pages
- way to make the main partition start at the end of boot partition
and extend to the end of the flash
This should probably also go to Linux.
Linux can define partitions in the device tree. I'd prefer to carry this approach to U-boot, but current U-Boot has the partitioning part of the MTD code hacked away and replaced by custom mtdparts code without OF support. Doing this proper requires getting rid of the latter, which will likely break a lot of legacy. So as far as I agree, I have to warn that is a longer-term project, extended further by the ongoing (justified) discussion we are having regarding the MTD partitioning code in upstream Linux that may or may not lead to parts of the framework being re-architected.
Roy
Thanks
Michal

On 8 June 2015 at 10:38, Roy Spliet r.spliet@ultimaker.com wrote:
Hello Michal,
Op 07-06-15 om 18:48 schreef Michal Suchanek:
Hello,
On 5 June 2015 at 13:52, Roy Spliet r.spliet@ultimaker.com wrote:
Based on the default layout of the android image used at least on Olimex Lime
Signed-off-by: Roy Spliet r.spliet@ultimaker.com
include/configs/sunxi-common.h | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h index ec28c40..b38f2f5 100644 --- a/include/configs/sunxi-common.h +++ b/include/configs/sunxi-common.h @@ -404,8 +404,15 @@ extern int soft_i2c_gpio_scl; #define BOOT_TARGET_DEVICES_USB(func) #endif
+#ifdef CONFIG_NAND +#define BOOT_TARGET_DEVICES_NAND(func) func(NAND, nand , 0) +#else +#define BOOT_TARGET_DEVICES_NAND(func) +#endif
#define BOOT_TARGET_DEVICES(func) \ BOOT_TARGET_DEVICES_MMC(func) \
BOOT_TARGET_DEVICES_NAND(func) \ BOOT_TARGET_DEVICES_SCSI(func) \ BOOT_TARGET_DEVICES_USB(func) \ func(PXE, pxe, na) \
@@ -441,6 +448,8 @@ extern int soft_i2c_gpio_scl; MEM_LAYOUT_ENV_SETTINGS \ "fdtfile=" CONFIG_DEFAULT_DEVICE_TREE ".dtb\0" \ "console=ttyS0,115200\0" \
"mtdids=nand0=mtd2\0" \
"mtdparts=mtdparts=mtd2:0xffc00000@0x400000(nand0_main)\0" \ BOOTENV
From what I heard the nand boot partition size should be specified in nand pages rather than bytes because the boot rom loads a fixed number of pages and just uses the start of each page regardless of page size.
Although I believe you have the facts mostly right, they do not apply in this situation. What you are looking for is the partition definition for U-boot. At this point the boot rom (BROM) is no longer active and thus it's inner workings are mostly irrelevant. The NAND partition lay-out for the boards I have seen (various Olimex + Cubietruck..) are as follows: 0-2MB U-Boot-SPL + U-Boot
OK, so 0-2 MB has to be read (at least in part) by brom to load the SPL so it must be in the format brom understands. The part containing the SPL which is read by brom has certain number of pages. The u-boot part can contain whatever we define.
2-4MB U-Boot SPL + U-Boot (for recovery) 4MB+ Main file system
The main filesystem has to be in format suitable for UBIFS. It has to follow u-boot part possibly with some space in between.
If support for non-uniform flash format is not available then there are basically two options:
1) SPL supports brom format and u-boot binary is part of boot partition. SPL can read/write itself and u-boot and u-boot can read/write the main partition
2) SPL and u-boot support the main partition format. While u-boot is written to the boot partition its pages are written in the format suitable for ubifs. This will get hairy when you want to actually write u-boot and SPL.
The problem with counting the partition sizes in bytes is obvious - whatever you do the brom reads predefined number of pages from the start of the nand to load the SPL and unless you know maximum page size of every nand ever made that is compatible with Allwinner brom you cannot tell what is even the maximum size of this part in bytes.
Thanks
Michal

Dear Michal,
Op 08-06-15 om 10:54 schreef Michal Suchanek:
On 8 June 2015 at 10:38, Roy Spliet r.spliet@ultimaker.com wrote:
Hello Michal,
Op 07-06-15 om 18:48 schreef Michal Suchanek:
Hello,
On 5 June 2015 at 13:52, Roy Spliet r.spliet@ultimaker.com wrote:
Based on the default layout of the android image used at least on Olimex Lime
Signed-off-by: Roy Spliet r.spliet@ultimaker.com
include/configs/sunxi-common.h | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h index ec28c40..b38f2f5 100644 --- a/include/configs/sunxi-common.h +++ b/include/configs/sunxi-common.h @@ -404,8 +404,15 @@ extern int soft_i2c_gpio_scl; #define BOOT_TARGET_DEVICES_USB(func) #endif
+#ifdef CONFIG_NAND +#define BOOT_TARGET_DEVICES_NAND(func) func(NAND, nand , 0) +#else +#define BOOT_TARGET_DEVICES_NAND(func) +#endif
- #define BOOT_TARGET_DEVICES(func) \ BOOT_TARGET_DEVICES_MMC(func) \
BOOT_TARGET_DEVICES_NAND(func) \ BOOT_TARGET_DEVICES_SCSI(func) \ BOOT_TARGET_DEVICES_USB(func) \ func(PXE, pxe, na) \
@@ -441,6 +448,8 @@ extern int soft_i2c_gpio_scl; MEM_LAYOUT_ENV_SETTINGS \ "fdtfile=" CONFIG_DEFAULT_DEVICE_TREE ".dtb\0" \ "console=ttyS0,115200\0" \
"mtdids=nand0=mtd2\0" \
"mtdparts=mtdparts=mtd2:0xffc00000@0x400000(nand0_main)\0" \ BOOTENV
From what I heard the nand boot partition size should be specified in nand pages rather than bytes because the boot rom loads a fixed number of pages and just uses the start of each page regardless of page size.
Although I believe you have the facts mostly right, they do not apply in this situation. What you are looking for is the partition definition for U-boot. At this point the boot rom (BROM) is no longer active and thus it's inner workings are mostly irrelevant. The NAND partition lay-out for the boards I have seen (various Olimex + Cubietruck..) are as follows: 0-2MB U-Boot-SPL + U-Boot
OK, so 0-2 MB has to be read (at least in part) by brom to load the SPL so it must be in the format brom understands. The part containing the SPL which is read by brom has certain number of pages. The u-boot part can contain whatever we define.
2-4MB U-Boot SPL + U-Boot (for recovery) 4MB+ Main file system
The main filesystem has to be in format suitable for UBIFS. It has to follow u-boot part possibly with some space in between.
If support for non-uniform flash format is not available then there are basically two options:
- SPL supports brom format and u-boot binary is part of boot
partition. SPL can read/write itself and u-boot and u-boot can read/write the main partition
- SPL and u-boot support the main partition format. While u-boot is
written to the boot partition its pages are written in the format suitable for ubifs. This will get hairy when you want to actually write u-boot and SPL.
The problem with counting the partition sizes in bytes is obvious - whatever you do the brom reads predefined number of pages from the start of the nand to load the SPL and unless you know maximum page size of every nand ever made that is compatible with Allwinner brom you cannot tell what is even the maximum size of this part in bytes.]
The solution already implemented now and carried to Hans' tree is 1) with the size of SPL aligned to 8KB blocks (the largest of the page sizes attempted). The modes tried by BROM are listed in [1]. SPL currently tries only a single mode whose params are defined in sunxi-common.h, but this is likely to change to a "brute-force" approach to resemble BROM in the near future. Yours,
Roy
Thanks
Michal
[1] https://linux-sunxi.org/NAND

Hi Roy Thank you for working on this, I would like to suggest if you could implement separate control commands to switch ECC and Randomisation modes per partitions. I know this is not the best approach but it will provide more controls.
On 8 Jun 2015, at 7:11 pm, Roy Spliet r.spliet@ultimaker.com wrote:
Dear Michal,
Op 08-06-15 om 10:54 schreef Michal Suchanek:
On 8 June 2015 at 10:38, Roy Spliet r.spliet@ultimaker.com wrote:
Hello Michal,
Op 07-06-15 om 18:48 schreef Michal Suchanek:
Hello,
On 5 June 2015 at 13:52, Roy Spliet r.spliet@ultimaker.com wrote:
Based on the default layout of the android image used at least on Olimex Lime
Signed-off-by: Roy Spliet r.spliet@ultimaker.com
include/configs/sunxi-common.h | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h index ec28c40..b38f2f5 100644 --- a/include/configs/sunxi-common.h +++ b/include/configs/sunxi-common.h @@ -404,8 +404,15 @@ extern int soft_i2c_gpio_scl; #define BOOT_TARGET_DEVICES_USB(func) #endif
+#ifdef CONFIG_NAND +#define BOOT_TARGET_DEVICES_NAND(func) func(NAND, nand , 0) +#else +#define BOOT_TARGET_DEVICES_NAND(func) +#endif
#define BOOT_TARGET_DEVICES(func) \ BOOT_TARGET_DEVICES_MMC(func) \
BOOT_TARGET_DEVICES_NAND(func) \ BOOT_TARGET_DEVICES_SCSI(func) \ BOOT_TARGET_DEVICES_USB(func) \ func(PXE, pxe, na) \
@@ -441,6 +448,8 @@ extern int soft_i2c_gpio_scl; MEM_LAYOUT_ENV_SETTINGS \ "fdtfile=" CONFIG_DEFAULT_DEVICE_TREE ".dtb\0" \ "console=ttyS0,115200\0" \
"mtdids=nand0=mtd2\0" \
"mtdparts=mtdparts=mtd2:0xffc00000@0x400000(nand0_main)\0" \ BOOTENV
From what I heard the nand boot partition size should be specified in nand pages rather than bytes because the boot rom loads a fixed number of pages and just uses the start of each page regardless of page size.
Although I believe you have the facts mostly right, they do not apply in this situation. What you are looking for is the partition definition for U-boot. At this point the boot rom (BROM) is no longer active and thus it's inner workings are mostly irrelevant. The NAND partition lay-out for the boards I have seen (various Olimex + Cubietruck..) are as follows: 0-2MB U-Boot-SPL + U-Boot
OK, so 0-2 MB has to be read (at least in part) by brom to load the SPL so it must be in the format brom understands. The part containing the SPL which is read by brom has certain number of pages. The u-boot part can contain whatever we define.
2-4MB U-Boot SPL + U-Boot (for recovery) 4MB+ Main file system
The main filesystem has to be in format suitable for UBIFS. It has to follow u-boot part possibly with some space in between.
If support for non-uniform flash format is not available then there are basically two options:
- SPL supports brom format and u-boot binary is part of boot
partition. SPL can read/write itself and u-boot and u-boot can read/write the main partition
- SPL and u-boot support the main partition format. While u-boot is
written to the boot partition its pages are written in the format suitable for ubifs. This will get hairy when you want to actually write u-boot and SPL.
The problem with counting the partition sizes in bytes is obvious - whatever you do the brom reads predefined number of pages from the start of the nand to load the SPL and unless you know maximum page size of every nand ever made that is compatible with Allwinner brom you cannot tell what is even the maximum size of this part in bytes.]
The solution already implemented now and carried to Hans' tree is 1) with the size of SPL aligned to 8KB blocks (the largest of the page sizes attempted). The modes tried by BROM are listed in [1]. SPL currently tries only a single mode whose params are defined in sunxi-common.h, but this is likely to change to a "brute-force" approach to resemble BROM in the near future. Yours,
Roy
Thanks
Michal
[1] https://linux-sunxi.org/NAND
--
IMAGINE IT >> MAKE IT
Meet us online at Twitter http://twitter.com/ultimaker, Facebook http://facebook.com/ultimaker, Google+ http://google.com/+Ultimaker
www.ultimaker.com
-- You received this message because you are subscribed to the Google Groups "linux-sunxi" group. To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe@googlegroups.com. For more options, visit https://groups.google.com/d/optout.

Hello Yassin,
Op 08-06-15 om 12:48 schreef Yassin:
Hi Roy Thank you for working on this, I would like to suggest if you could implement separate control commands to switch ECC and Randomisation modes per partitions. I know this is not the best approach but it will provide more controls.
Thanks for the suggestion. However, for now I do not see the immediate need for this in U-boot. U-boot should really only care about one thing: load Linux. Therefore, the only partition it needs to be able to read is whatever partition is used for storing boot data on. And if we only care about one NAND partition, we don't need per-partition settings. I agree that it would be nice to have eventually, but I think this problem first needs to be solved in Linux properly. Then in U-boot we can sync MTD up with Linux and we get the support we need "for free". I'd personally recommend to take this approach over now first hacking up all sorts of new commands in U-boot, as the latter will create more problems with syncing up MTD and doing the right thing on the longer run. Yours,
Roy
On 8 Jun 2015, at 7:11 pm, Roy Spliet r.spliet@ultimaker.com wrote:
Dear Michal,
Op 08-06-15 om 10:54 schreef Michal Suchanek:
On 8 June 2015 at 10:38, Roy Spliet r.spliet@ultimaker.com wrote:
Hello Michal,
Op 07-06-15 om 18:48 schreef Michal Suchanek:
Hello,
On 5 June 2015 at 13:52, Roy Spliet r.spliet@ultimaker.com wrote:
Based on the default layout of the android image used at least on Olimex Lime
Signed-off-by: Roy Spliet r.spliet@ultimaker.com
include/configs/sunxi-common.h | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h index ec28c40..b38f2f5 100644 --- a/include/configs/sunxi-common.h +++ b/include/configs/sunxi-common.h @@ -404,8 +404,15 @@ extern int soft_i2c_gpio_scl; #define BOOT_TARGET_DEVICES_USB(func) #endif
+#ifdef CONFIG_NAND +#define BOOT_TARGET_DEVICES_NAND(func) func(NAND, nand , 0) +#else +#define BOOT_TARGET_DEVICES_NAND(func) +#endif
- #define BOOT_TARGET_DEVICES(func) \ BOOT_TARGET_DEVICES_MMC(func) \
BOOT_TARGET_DEVICES_NAND(func) \ BOOT_TARGET_DEVICES_SCSI(func) \ BOOT_TARGET_DEVICES_USB(func) \ func(PXE, pxe, na) \
@@ -441,6 +448,8 @@ extern int soft_i2c_gpio_scl; MEM_LAYOUT_ENV_SETTINGS \ "fdtfile=" CONFIG_DEFAULT_DEVICE_TREE ".dtb\0" \ "console=ttyS0,115200\0" \
"mtdids=nand0=mtd2\0" \
"mtdparts=mtdparts=mtd2:0xffc00000@0x400000(nand0_main)\0" \ BOOTENV
From what I heard the nand boot partition size should be specified in nand pages rather than bytes because the boot rom loads a fixed number of pages and just uses the start of each page regardless of page size.
Although I believe you have the facts mostly right, they do not apply in this situation. What you are looking for is the partition definition for U-boot. At this point the boot rom (BROM) is no longer active and thus it's inner workings are mostly irrelevant. The NAND partition lay-out for the boards I have seen (various Olimex + Cubietruck..) are as follows: 0-2MB U-Boot-SPL + U-Boot
OK, so 0-2 MB has to be read (at least in part) by brom to load the SPL so it must be in the format brom understands. The part containing the SPL which is read by brom has certain number of pages. The u-boot part can contain whatever we define.
2-4MB U-Boot SPL + U-Boot (for recovery) 4MB+ Main file system
The main filesystem has to be in format suitable for UBIFS. It has to follow u-boot part possibly with some space in between.
If support for non-uniform flash format is not available then there are basically two options:
- SPL supports brom format and u-boot binary is part of boot
partition. SPL can read/write itself and u-boot and u-boot can read/write the main partition
- SPL and u-boot support the main partition format. While u-boot is
written to the boot partition its pages are written in the format suitable for ubifs. This will get hairy when you want to actually write u-boot and SPL.
The problem with counting the partition sizes in bytes is obvious - whatever you do the brom reads predefined number of pages from the start of the nand to load the SPL and unless you know maximum page size of every nand ever made that is compatible with Allwinner brom you cannot tell what is even the maximum size of this part in bytes.]
The solution already implemented now and carried to Hans' tree is 1) with the size of SPL aligned to 8KB blocks (the largest of the page sizes attempted). The modes tried by BROM are listed in [1]. SPL currently tries only a single mode whose params are defined in sunxi-common.h, but this is likely to change to a "brute-force" approach to resemble BROM in the near future. Yours,
Roy
Thanks
Michal
[1] https://linux-sunxi.org/NAND
--
IMAGINE IT >> MAKE IT
Meet us online at Twitter http://twitter.com/ultimaker, Facebook http://facebook.com/ultimaker, Google+ http://google.com/+Ultimaker
www.ultimaker.com
-- You received this message because you are subscribed to the Google Groups "linux-sunxi" group. To unsubscribe from this group and stop receiving emails from it, send an email to linux-sunxi+unsubscribe@googlegroups.com. For more options, visit https://groups.google.com/d/optout.

Roy, Yassin,
On Mon, 08 Jun 2015 13:35:04 +0200 Roy Spliet r.spliet@ultimaker.com wrote:
Hello Yassin,
Op 08-06-15 om 12:48 schreef Yassin:
Hi Roy Thank you for working on this, I would like to suggest if you could implement separate control commands to switch ECC and Randomisation modes per partitions. I know this is not the best approach but it will provide more controls.
Thanks for the suggestion. However, for now I do not see the immediate need for this in U-boot. U-boot should really only care about one thing: load Linux. Therefore, the only partition it needs to be able to read is whatever partition is used for storing boot data on. And if we only care about one NAND partition, we don't need per-partition settings. I agree that it would be nice to have eventually, but I think this problem first needs to be solved in Linux properly. Then in U-boot we can sync MTD up with Linux and we get the support we need "for free". I'd personally recommend to take this approach over now first hacking up all sorts of new commands in U-boot, as the latter will create more problems with syncing up MTD and doing the right thing on the longer run.
Actually, the more I think about the more I agree with Yassin's suggestion. We currently don't have any standard way to attach a specific setting to a partition (even in Linux, and I hope I'll be able to work on this aspect soon). So for now, I really think we should reuse the existing/standard way of declaring partitions in u-boot (mtdparts + mtdids variables + the mtdparts command) and a sunxi specific commands to configure the ECC and Randomizer config. Once we have settled on something in Linux, we will be able to port it to u-boot and get rid of these sunxi specific command, but in the meantime this will allow us to boot a linux kernel (and even flash an SPL) from u-boot without introducing heavy changes in the u-boot MTD layer.
Best Regards,
Boris

Hi,
On 14-06-15 13:31, Boris Brezillon wrote:
Roy, Yassin,
On Mon, 08 Jun 2015 13:35:04 +0200 Roy Spliet r.spliet@ultimaker.com wrote:
Hello Yassin,
Op 08-06-15 om 12:48 schreef Yassin:
Hi Roy Thank you for working on this, I would like to suggest if you could implement separate control commands to switch ECC and Randomisation modes per partitions. I know this is not the best approach but it will provide more controls.
Thanks for the suggestion. However, for now I do not see the immediate need for this in U-boot. U-boot should really only care about one thing: load Linux. Therefore, the only partition it needs to be able to read is whatever partition is used for storing boot data on. And if we only care about one NAND partition, we don't need per-partition settings. I agree that it would be nice to have eventually, but I think this problem first needs to be solved in Linux properly. Then in U-boot we can sync MTD up with Linux and we get the support we need "for free". I'd personally recommend to take this approach over now first hacking up all sorts of new commands in U-boot, as the latter will create more problems with syncing up MTD and doing the right thing on the longer run.
Actually, the more I think about the more I agree with Yassin's suggestion. We currently don't have any standard way to attach a specific setting to a partition (even in Linux, and I hope I'll be able to work on this aspect soon). So for now, I really think we should reuse the existing/standard way of declaring partitions in u-boot (mtdparts + mtdids variables + the mtdparts command) and a sunxi specific commands to configure the ECC and Randomizer config. Once we have settled on something in Linux, we will be able to port it to u-boot and get rid of these sunxi specific command, but in the meantime this will allow us to boot a linux kernel (and even flash an SPL) from u-boot without introducing heavy changes in the u-boot MTD layer.
Ack, note we do not even need any sunxi specific commands, we will have separate nand code for the SPL and uboot itself, since the SPL needs a simple mini read-only implementation. So we can compile the parameters for the boot block vs the rest into the spl resp. uboot binary directly.
Regards,
Hans

Hi Roy,
On 08-06-15 10:38, Roy Spliet wrote:
Hello Michal,
Op 07-06-15 om 18:48 schreef Michal Suchanek:
Hello,
On 5 June 2015 at 13:52, Roy Spliet r.spliet@ultimaker.com wrote:
Based on the default layout of the android image used at least on Olimex Lime
Signed-off-by: Roy Spliet r.spliet@ultimaker.com
include/configs/sunxi-common.h | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h index ec28c40..b38f2f5 100644 --- a/include/configs/sunxi-common.h +++ b/include/configs/sunxi-common.h @@ -404,8 +404,15 @@ extern int soft_i2c_gpio_scl; #define BOOT_TARGET_DEVICES_USB(func) #endif
+#ifdef CONFIG_NAND +#define BOOT_TARGET_DEVICES_NAND(func) func(NAND, nand , 0) +#else +#define BOOT_TARGET_DEVICES_NAND(func) +#endif
- #define BOOT_TARGET_DEVICES(func) \ BOOT_TARGET_DEVICES_MMC(func) \
BOOT_TARGET_DEVICES_NAND(func) \ BOOT_TARGET_DEVICES_SCSI(func) \ BOOT_TARGET_DEVICES_USB(func) \ func(PXE, pxe, na) \
@@ -441,6 +448,8 @@ extern int soft_i2c_gpio_scl; MEM_LAYOUT_ENV_SETTINGS \ "fdtfile=" CONFIG_DEFAULT_DEVICE_TREE ".dtb\0" \ "console=ttyS0,115200\0" \
"mtdids=nand0=mtd2\0" \
"mtdparts=mtdparts=mtd2:0xffc00000@0x400000(nand0_main)\0" \ BOOTENV
From what I heard the nand boot partition size should be specified in nand pages rather than bytes because the boot rom loads a fixed number of pages and just uses the start of each page regardless of page size.
Although I believe you have the facts mostly right, they do not apply in this situation. What you are looking for is the partition definition for U-boot. At this point the boot rom (BROM) is no longer active and thus it's inner workings are mostly irrelevant. The NAND partition lay-out for the boards I have seen (various Olimex + Cubietruck..) are as follows: 0-2MB U-Boot-SPL + U-Boot 2-4MB U-Boot SPL + U-Boot (for recovery) 4MB+ Main file system
The mtdparts env variable defines only the main file system because that's the only place with relevant files for U-Boot. This said: I have replaced the default file system with a "custom" UBI FS containing just a boot and a rootfs partition. I am unsure whether these defaults apply for the Android image that ships with the Olimex boards. But then again: that image also ships with a boot-loader, so do we care?
I did not find any document regarding the nand boot partition layout so I would like to see some input from somebody familiar with the driver.
While it is fine for testing to hand-edit the environment the final nand support should have
- way to express the boot partition size in nand pages
- way to make the main partition start at the end of boot partition
and extend to the end of the flash
This should probably also go to Linux.
Linux can define partitions in the device tree. I'd prefer to carry this approach to U-boot, but current U-Boot has the partitioning part of the MTD code hacked away and replaced by custom mtdparts code without OF support. Doing this proper requires getting rid of the latter, which will likely break a lot of legacy. So as far as I agree, I have to warn that is a longer-term project, extended further by the ongoing (justified) discussion we are having regarding the MTD partitioning code in upstream Linux that may or may not lead to parts of the framework being re-architected.
Last time I discussed this with some kernel people, they pointed me to the fact that the kernel actually parses partition info provided by u-boot through the u-boot mtdparts command, and that that is the preferred way to pass partition info to the kernel, so rather then adding dts partition info to u-boot we would need to teach the existing kernel mtdpart code to deal with separate ecc / random setting and maybe use a list of hardcoded partitition names for which to use the brom compatible settings. These are all things which we need to figure out on the kernel side, it would be good to get a discussion started on this with the kernel mtd people.
Note that we will also need to have a u-boot env partition somewhare either at the nandpart level, or as a ubi volume, assuming the u-boot env code can deal with ubi volumes (we are in the full u-boot environment when reading the env.)
Regards,
Hans
Roy
Thanks
Michal

Hello Hans,
Op 08-06-15 om 15:16 schreef Hans de Goede:
Hi Roy,
On 08-06-15 10:38, Roy Spliet wrote:
Hello Michal,
Op 07-06-15 om 18:48 schreef Michal Suchanek:
Hello,
On 5 June 2015 at 13:52, Roy Spliet r.spliet@ultimaker.com wrote:
Based on the default layout of the android image used at least on Olimex Lime
Signed-off-by: Roy Spliet r.spliet@ultimaker.com
include/configs/sunxi-common.h | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h index ec28c40..b38f2f5 100644 --- a/include/configs/sunxi-common.h +++ b/include/configs/sunxi-common.h @@ -404,8 +404,15 @@ extern int soft_i2c_gpio_scl; #define BOOT_TARGET_DEVICES_USB(func) #endif
+#ifdef CONFIG_NAND +#define BOOT_TARGET_DEVICES_NAND(func) func(NAND, nand , 0) +#else +#define BOOT_TARGET_DEVICES_NAND(func) +#endif
- #define BOOT_TARGET_DEVICES(func) \ BOOT_TARGET_DEVICES_MMC(func) \
BOOT_TARGET_DEVICES_NAND(func) \ BOOT_TARGET_DEVICES_SCSI(func) \ BOOT_TARGET_DEVICES_USB(func) \ func(PXE, pxe, na) \
@@ -441,6 +448,8 @@ extern int soft_i2c_gpio_scl; MEM_LAYOUT_ENV_SETTINGS \ "fdtfile=" CONFIG_DEFAULT_DEVICE_TREE ".dtb\0" \ "console=ttyS0,115200\0" \
"mtdids=nand0=mtd2\0" \
- "mtdparts=mtdparts=mtd2:0xffc00000@0x400000(nand0_main)\0" \ BOOTENV
From what I heard the nand boot partition size should be specified in nand pages rather than bytes because the boot rom loads a fixed number of pages and just uses the start of each page regardless of page size.
Although I believe you have the facts mostly right, they do not apply in this situation. What you are looking for is the partition definition for U-boot. At this point the boot rom (BROM) is no longer active and thus it's inner workings are mostly irrelevant. The NAND partition lay-out for the boards I have seen (various Olimex
- Cubietruck..)
are as follows: 0-2MB U-Boot-SPL + U-Boot 2-4MB U-Boot SPL + U-Boot (for recovery) 4MB+ Main file system
The mtdparts env variable defines only the main file system because that's the only place with relevant files for U-Boot. This said: I have replaced the default file system with a "custom" UBI FS containing just a boot and a rootfs partition. I am unsure whether these defaults apply for the Android image that ships with the Olimex boards. But then again: that image also ships with a boot-loader, so do we care?
I did not find any document regarding the nand boot partition layout so I would like to see some input from somebody familiar with the driver.
While it is fine for testing to hand-edit the environment the final nand support should have
- way to express the boot partition size in nand pages
- way to make the main partition start at the end of boot partition
and extend to the end of the flash
This should probably also go to Linux.
Linux can define partitions in the device tree. I'd prefer to carry this approach to U-boot, but current U-Boot has the partitioning part of the MTD code hacked away and replaced by custom mtdparts code without OF support. Doing this proper requires getting rid of the latter, which will likely break a lot of legacy. So as far as I agree, I have to warn that is a longer-term project, extended further by the ongoing (justified) discussion we are having regarding the MTD partitioning code in upstream Linux that may or may not lead to parts of the framework being re-architected.
Last time I discussed this with some kernel people, they pointed me to the fact that the kernel actually parses partition info provided by u-boot through the u-boot mtdparts command, and that that is the preferred way to pass partition info to the kernel, so rather then adding dts partition info to u-boot we would need to teach the existing kernel mtdpart code to deal with separate ecc / random setting and maybe use a list of hardcoded partitition names for which to use the brom compatible settings. These are all things which we need to figure out on the kernel side, it would be good to get a discussion started on this with the kernel mtd people.
I am unaware of the kernel developers' preferences, but [1] shows that the kernel gained OF (DTS) partition parsing in 2008. We do need to teach the kernel (and U-Boot) the trick of parsing mtd partitions, but with ARM/Linaro pushing DTS as the preferred way to describe hardware we might have to consider this a separate discussion point.
Note that we will also need to have a u-boot env partition somewhare either at the nandpart level, or as a ubi volume, assuming the u-boot env code can deal with ubi volumes (we are in the full u-boot environment when reading the env.)
Thanks, this is a relevant addition and something that needs investigation. Unfortunately I can probably not pull this wagon too much further, given I am about to continue my career in academics starting next month. I'd be happy to think along and start the discussion, but I might not be around long enough to work much on the implementation. Yours,
Roy
Regards,
Hans
Roy
Thanks
Michal

Hi Michal,
On Sun, 7 Jun 2015 18:48:26 +0200 Michal Suchanek hramrach@gmail.com wrote:
Hello,
On 5 June 2015 at 13:52, Roy Spliet r.spliet@ultimaker.com wrote:
Based on the default layout of the android image used at least on Olimex Lime
Signed-off-by: Roy Spliet r.spliet@ultimaker.com
include/configs/sunxi-common.h | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h index ec28c40..b38f2f5 100644 --- a/include/configs/sunxi-common.h +++ b/include/configs/sunxi-common.h @@ -404,8 +404,15 @@ extern int soft_i2c_gpio_scl; #define BOOT_TARGET_DEVICES_USB(func) #endif
+#ifdef CONFIG_NAND +#define BOOT_TARGET_DEVICES_NAND(func) func(NAND, nand , 0) +#else +#define BOOT_TARGET_DEVICES_NAND(func) +#endif
#define BOOT_TARGET_DEVICES(func) \ BOOT_TARGET_DEVICES_MMC(func) \
BOOT_TARGET_DEVICES_NAND(func) \ BOOT_TARGET_DEVICES_SCSI(func) \ BOOT_TARGET_DEVICES_USB(func) \ func(PXE, pxe, na) \
@@ -441,6 +448,8 @@ extern int soft_i2c_gpio_scl; MEM_LAYOUT_ENV_SETTINGS \ "fdtfile=" CONFIG_DEFAULT_DEVICE_TREE ".dtb\0" \ "console=ttyS0,115200\0" \
"mtdids=nand0=mtd2\0" \
"mtdparts=mtdparts=mtd2:0xffc00000@0x400000(nand0_main)\0" \ BOOTENV
From what I heard the nand boot partition size should be specified in nand pages rather than bytes because the boot rom loads a fixed number of pages and just uses the start of each page regardless of page size.
AFAIK, the mtdparts format only allows you to express partition offsets and sizes in bytes, and even if we had to change for something else, we should choose NAND blocks rather than NAND pages. The reason partitions should be block aligned in because the you can't erase specific pages in a block, which means that if you define 2 partitions sharing the same block, you won't be able to update one partition without potentially corrupting the other one.
I did not find any document regarding the nand boot partition layout so I would like to see some input from somebody familiar with the driver.
AFAIR, the mtd partition code checks for block alignment anyway, so you shouldn't be allowed to create two partitions sharing the same block.
While it is fine for testing to hand-edit the environment the final nand support should have
- way to express the boot partition size in nand pages
Why should we add that ? The conversion from a number of blocks to a number bytes is pretty straightforward (number_of_blocks * block_size_in_bytes).
- way to make the main partition start at the end of boot partition
and extend to the end of the flash
Again, that's not a good idea, the main partition should be aligned on a block (see the above explanation).
Best Regards,
Boris

On 14 June 2015 at 13:25, Boris Brezillon boris.brezillon@free-electrons.com wrote:
Hi Michal,
On Sun, 7 Jun 2015 18:48:26 +0200 Michal Suchanek hramrach@gmail.com wrote:
Hello,
On 5 June 2015 at 13:52, Roy Spliet r.spliet@ultimaker.com wrote:
Based on the default layout of the android image used at least on Olimex Lime
Signed-off-by: Roy Spliet r.spliet@ultimaker.com
include/configs/sunxi-common.h | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h index ec28c40..b38f2f5 100644 --- a/include/configs/sunxi-common.h +++ b/include/configs/sunxi-common.h @@ -404,8 +404,15 @@ extern int soft_i2c_gpio_scl; #define BOOT_TARGET_DEVICES_USB(func) #endif
+#ifdef CONFIG_NAND +#define BOOT_TARGET_DEVICES_NAND(func) func(NAND, nand , 0) +#else +#define BOOT_TARGET_DEVICES_NAND(func) +#endif
#define BOOT_TARGET_DEVICES(func) \ BOOT_TARGET_DEVICES_MMC(func) \
BOOT_TARGET_DEVICES_NAND(func) \ BOOT_TARGET_DEVICES_SCSI(func) \ BOOT_TARGET_DEVICES_USB(func) \ func(PXE, pxe, na) \
@@ -441,6 +448,8 @@ extern int soft_i2c_gpio_scl; MEM_LAYOUT_ENV_SETTINGS \ "fdtfile=" CONFIG_DEFAULT_DEVICE_TREE ".dtb\0" \ "console=ttyS0,115200\0" \
"mtdids=nand0=mtd2\0" \
"mtdparts=mtdparts=mtd2:0xffc00000@0x400000(nand0_main)\0" \ BOOTENV
From what I heard the nand boot partition size should be specified in nand pages rather than bytes because the boot rom loads a fixed number of pages and just uses the start of each page regardless of page size.
AFAIK, the mtdparts format only allows you to express partition offsets and sizes in bytes, and even if we had to change for something else, we should choose NAND blocks rather than NAND pages. The reason partitions should be block aligned in because the you can't erase specific pages in a block, which means that if you define 2 partitions sharing the same block, you won't be able to update one partition without potentially corrupting the other one.
However, if the number of pages the boot0 partition takes up is not block aligned it means we cannot use the medium for booting and can just use 1 big partition anyway.
I did not find any document regarding the nand boot partition layout so I would like to see some input from somebody familiar with the driver.
AFAIR, the mtd partition code checks for block alignment anyway, so you shouldn't be allowed to create two partitions sharing the same block.
While it is fine for testing to hand-edit the environment the final nand support should have
- way to express the boot partition size in nand pages
Why should we add that ? The conversion from a number of blocks to a number bytes is pretty straightforward (number_of_blocks * block_size_in_bytes).
Because the block size is not the same on all flash chips, obviously.
If there is only one block size that can ever be reasonably supported due to other constraints then it's fine to just hardcode it.
- way to make the main partition start at the end of boot partition
and extend to the end of the flash
Again, that's not a good idea, the main partition should be aligned on a block (see the above explanation).
Since these are block aligned anyway there should be no problem.
And if you can express the size in blocks you can just make the main partition start the next block after boot0. What is missing is the ability to extend to the end of medium to be feature compatible with Chinese software that uses this to flash same firmware to tablets with different nand size. I think this is generally desirable anyway.
Thanks
Michal

On Sun, 14 Jun 2015 13:56:56 +0200 Michal Suchanek hramrach@gmail.com wrote:
On 14 June 2015 at 13:25, Boris Brezillon boris.brezillon@free-electrons.com wrote:
Hi Michal,
On Sun, 7 Jun 2015 18:48:26 +0200 Michal Suchanek hramrach@gmail.com wrote:
Hello,
On 5 June 2015 at 13:52, Roy Spliet r.spliet@ultimaker.com wrote:
Based on the default layout of the android image used at least on Olimex Lime
Signed-off-by: Roy Spliet r.spliet@ultimaker.com
include/configs/sunxi-common.h | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h index ec28c40..b38f2f5 100644 --- a/include/configs/sunxi-common.h +++ b/include/configs/sunxi-common.h @@ -404,8 +404,15 @@ extern int soft_i2c_gpio_scl; #define BOOT_TARGET_DEVICES_USB(func) #endif
+#ifdef CONFIG_NAND +#define BOOT_TARGET_DEVICES_NAND(func) func(NAND, nand , 0) +#else +#define BOOT_TARGET_DEVICES_NAND(func) +#endif
#define BOOT_TARGET_DEVICES(func) \ BOOT_TARGET_DEVICES_MMC(func) \
BOOT_TARGET_DEVICES_NAND(func) \ BOOT_TARGET_DEVICES_SCSI(func) \ BOOT_TARGET_DEVICES_USB(func) \ func(PXE, pxe, na) \
@@ -441,6 +448,8 @@ extern int soft_i2c_gpio_scl; MEM_LAYOUT_ENV_SETTINGS \ "fdtfile=" CONFIG_DEFAULT_DEVICE_TREE ".dtb\0" \ "console=ttyS0,115200\0" \
"mtdids=nand0=mtd2\0" \
"mtdparts=mtdparts=mtd2:0xffc00000@0x400000(nand0_main)\0" \ BOOTENV
From what I heard the nand boot partition size should be specified in nand pages rather than bytes because the boot rom loads a fixed number of pages and just uses the start of each page regardless of page size.
AFAIK, the mtdparts format only allows you to express partition offsets and sizes in bytes, and even if we had to change for something else, we should choose NAND blocks rather than NAND pages. The reason partitions should be block aligned in because the you can't erase specific pages in a block, which means that if you define 2 partitions sharing the same block, you won't be able to update one partition without potentially corrupting the other one.
However, if the number of pages the boot0 partition takes up is not block aligned it means we cannot use the medium for booting and can just use 1 big partition anyway.
Hm, I don't get your point. You can have a boot0 partition taking one NAND block, and several other partitions used for other purpose (though having a single UBI partition is a better approach).
I did not find any document regarding the nand boot partition layout so I would like to see some input from somebody familiar with the driver.
AFAIR, the mtd partition code checks for block alignment anyway, so you shouldn't be allowed to create two partitions sharing the same block.
While it is fine for testing to hand-edit the environment the final nand support should have
- way to express the boot partition size in nand pages
Why should we add that ? The conversion from a number of blocks to a number bytes is pretty straightforward (number_of_blocks * block_size_in_bytes).
Because the block size is not the same on all flash chips, obviously.
And that's why partitions are defined in the board dts, and not the SoC dtsi...
If there is only one block size that can ever be reasonably supported due to other constraints then it's fine to just hardcode it.
I think we already had this discussion on the #linux-sunxi channel, and you're trying to use a generic config from things that are really board specific. What's the problem with having different partition layout depending on the board ? Note that once you have defined your boot0 + u-boot + u-boot-env + UBI partitions, if you want to have a generic layout in the UBI device, you can define standard names for you UBI volumes.
- way to make the main partition start at the end of boot partition
and extend to the end of the flash
Again, that's not a good idea, the main partition should be aligned on a block (see the above explanation).
Since these are block aligned anyway there should be no problem.
And if you can express the size in blocks you can just make the main partition start the next block after boot0. What is missing is the ability to extend to the end of medium to be feature compatible with Chinese software that uses this to flash same firmware to tablets with different nand size. I think this is generally desirable anyway.
If they decide to use one single dtb for all their board revisions (even when they decide to change the NAND chip type), then yes, they should define a boot0 partition taking at least the size of the maximum block size. But that's only my opinion, so if MTD maintainers (both u-boot and linux ones) accept to support MTD partition offsets and sizes defined in number blocks, then go for it.
Best Regards,
Boris

On 14 June 2015 at 14:18, Boris Brezillon boris.brezillon@free-electrons.com wrote:
On Sun, 14 Jun 2015 13:56:56 +0200 Michal Suchanek hramrach@gmail.com wrote:
On 14 June 2015 at 13:25, Boris Brezillon boris.brezillon@free-electrons.com wrote:
Hi Michal,
On Sun, 7 Jun 2015 18:48:26 +0200 Michal Suchanek hramrach@gmail.com wrote:
Hello,
On 5 June 2015 at 13:52, Roy Spliet r.spliet@ultimaker.com wrote:
Based on the default layout of the android image used at least on Olimex Lime
Signed-off-by: Roy Spliet r.spliet@ultimaker.com
include/configs/sunxi-common.h | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h index ec28c40..b38f2f5 100644 --- a/include/configs/sunxi-common.h +++ b/include/configs/sunxi-common.h @@ -404,8 +404,15 @@ extern int soft_i2c_gpio_scl; #define BOOT_TARGET_DEVICES_USB(func) #endif
+#ifdef CONFIG_NAND +#define BOOT_TARGET_DEVICES_NAND(func) func(NAND, nand , 0) +#else +#define BOOT_TARGET_DEVICES_NAND(func) +#endif
#define BOOT_TARGET_DEVICES(func) \ BOOT_TARGET_DEVICES_MMC(func) \
BOOT_TARGET_DEVICES_NAND(func) \ BOOT_TARGET_DEVICES_SCSI(func) \ BOOT_TARGET_DEVICES_USB(func) \ func(PXE, pxe, na) \
@@ -441,6 +448,8 @@ extern int soft_i2c_gpio_scl; MEM_LAYOUT_ENV_SETTINGS \ "fdtfile=" CONFIG_DEFAULT_DEVICE_TREE ".dtb\0" \ "console=ttyS0,115200\0" \
"mtdids=nand0=mtd2\0" \
"mtdparts=mtdparts=mtd2:0xffc00000@0x400000(nand0_main)\0" \ BOOTENV
From what I heard the nand boot partition size should be specified in nand pages rather than bytes because the boot rom loads a fixed number of pages and just uses the start of each page regardless of page size.
AFAIK, the mtdparts format only allows you to express partition offsets and sizes in bytes, and even if we had to change for something else, we should choose NAND blocks rather than NAND pages. The reason partitions should be block aligned in because the you can't erase specific pages in a block, which means that if you define 2 partitions sharing the same block, you won't be able to update one partition without potentially corrupting the other one.
However, if the number of pages the boot0 partition takes up is not block aligned it means we cannot use the medium for booting and can just use 1 big partition anyway.
Hm, I don't get your point. You can have a boot0 partition taking one NAND block, and several other partitions used for other purpose (though having a single UBI partition is a better approach).
and how do you tell the kernel that these partitions follow the boot0 taking up one block when you only count in bytes?
I did not find any document regarding the nand boot partition layout so I would like to see some input from somebody familiar with the driver.
AFAIR, the mtd partition code checks for block alignment anyway, so you shouldn't be allowed to create two partitions sharing the same block.
While it is fine for testing to hand-edit the environment the final nand support should have
- way to express the boot partition size in nand pages
Why should we add that ? The conversion from a number of blocks to a number bytes is pretty straightforward (number_of_blocks * block_size_in_bytes).
Because the block size is not the same on all flash chips, obviously.
And that's why partitions are defined in the board dts, and not the SoC dtsi...
If there is only one block size that can ever be reasonably supported due to other constraints then it's fine to just hardcode it.
I think we already had this discussion on the #linux-sunxi channel, and you're trying to use a generic config from things that are really board specific. What's the problem with having different partition layout depending on the board ?
Why do we have nand chip detection then?
We can just write the chip parameters in DT and have everything in one place at least.
If we cannot use the detected parameters to infer the related values in DT then the detection is useless for DT based archs.
Thanks
Michal

On Sun, 14 Jun 2015 19:42:58 +0200 Michal Suchanek hramrach@gmail.com wrote:
On 14 June 2015 at 14:18, Boris Brezillon boris.brezillon@free-electrons.com wrote:
On Sun, 14 Jun 2015 13:56:56 +0200 Michal Suchanek hramrach@gmail.com wrote:
On 14 June 2015 at 13:25, Boris Brezillon boris.brezillon@free-electrons.com wrote:
Hi Michal,
On Sun, 7 Jun 2015 18:48:26 +0200 Michal Suchanek hramrach@gmail.com wrote:
Hello,
On 5 June 2015 at 13:52, Roy Spliet r.spliet@ultimaker.com wrote:
Based on the default layout of the android image used at least on Olimex Lime
Signed-off-by: Roy Spliet r.spliet@ultimaker.com
include/configs/sunxi-common.h | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h index ec28c40..b38f2f5 100644 --- a/include/configs/sunxi-common.h +++ b/include/configs/sunxi-common.h @@ -404,8 +404,15 @@ extern int soft_i2c_gpio_scl; #define BOOT_TARGET_DEVICES_USB(func) #endif
+#ifdef CONFIG_NAND +#define BOOT_TARGET_DEVICES_NAND(func) func(NAND, nand , 0) +#else +#define BOOT_TARGET_DEVICES_NAND(func) +#endif
#define BOOT_TARGET_DEVICES(func) \ BOOT_TARGET_DEVICES_MMC(func) \
BOOT_TARGET_DEVICES_NAND(func) \ BOOT_TARGET_DEVICES_SCSI(func) \ BOOT_TARGET_DEVICES_USB(func) \ func(PXE, pxe, na) \
@@ -441,6 +448,8 @@ extern int soft_i2c_gpio_scl; MEM_LAYOUT_ENV_SETTINGS \ "fdtfile=" CONFIG_DEFAULT_DEVICE_TREE ".dtb\0" \ "console=ttyS0,115200\0" \
"mtdids=nand0=mtd2\0" \
"mtdparts=mtdparts=mtd2:0xffc00000@0x400000(nand0_main)\0" \ BOOTENV
From what I heard the nand boot partition size should be specified in nand pages rather than bytes because the boot rom loads a fixed number of pages and just uses the start of each page regardless of page size.
AFAIK, the mtdparts format only allows you to express partition offsets and sizes in bytes, and even if we had to change for something else, we should choose NAND blocks rather than NAND pages. The reason partitions should be block aligned in because the you can't erase specific pages in a block, which means that if you define 2 partitions sharing the same block, you won't be able to update one partition without potentially corrupting the other one.
However, if the number of pages the boot0 partition takes up is not block aligned it means we cannot use the medium for booting and can just use 1 big partition anyway.
Hm, I don't get your point. You can have a boot0 partition taking one NAND block, and several other partitions used for other purpose (though having a single UBI partition is a better approach).
and how do you tell the kernel that these partitions follow the boot0 taking up one block when you only count in bytes?
Oh come on! You just specify the appropriate size and offset in bytes.
I did not find any document regarding the nand boot partition layout so I would like to see some input from somebody familiar with the driver.
AFAIR, the mtd partition code checks for block alignment anyway, so you shouldn't be allowed to create two partitions sharing the same block.
While it is fine for testing to hand-edit the environment the final nand support should have
- way to express the boot partition size in nand pages
Why should we add that ? The conversion from a number of blocks to a number bytes is pretty straightforward (number_of_blocks * block_size_in_bytes).
Because the block size is not the same on all flash chips, obviously.
And that's why partitions are defined in the board dts, and not the SoC dtsi...
If there is only one block size that can ever be reasonably supported due to other constraints then it's fine to just hardcode it.
I think we already had this discussion on the #linux-sunxi channel, and you're trying to use a generic config from things that are really board specific. What's the problem with having different partition layout depending on the board ?
Why do we have nand chip detection then?
There are a lot of other stuff detected from the NAND chip ID (or ONFI or JEDEC standard), but remember that the partition stuff are supposed to be common to all the MTD devices, and I'm not sure all of them can use the erase size (or block size unit) to express their partitions offsets/sizes. Moreover, having the partition layout expressed in bytes is IMO clearer than having it expressed in blocks.
We can just write the chip parameters in DT and have everything in one place at least.
Yes, and we could also declare USB devices in the DT.
If we cannot use the detected parameters to infer the related values in DT then the detection is useless for DT based archs.
Again, you're not fair, the chip detection is used for a lot of things (including detecting the NAND block size), and the fact that this information is not used for partition definition, is really a minor detail.
This being said, if you really want to change that, then propose a new DT binding and a new formalism for the mtdparts parameter, and discuss it with the MTD maintainers. We don't need these changes for the sunxi NAND driver to work, so I'll let you deal with that aspect.
Best Regards,
Boris

Hi Roy,
Thanks for your work on this!
On 05-06-15 13:52, Roy Spliet wrote:
Following up on earlier SPL patches, here a series based on Yassin Jaffer's work to bring NAND support to U-boot. RFC because I know that the sunxi nand configuration options are dependent on a work-in-progress by Daniel - trying to deliver a single SPL for both MMC and NAND boot.
I've just tested Daniel's patches on a board with an eMMC, fixed 2 small bugs related to eMMC handling and merged them into u-boot-sunxi/next.
So it would probably be best to base your next version of the patches on top of u-boot-sunxi/next which other then having Daniel's patches is following u-boot/master atm.
Given I have spent the past few weeks learning how Boris' original driver works, I have probably grown a bit blind for potentially existing issues. Please provide plenty of feedback so I can bring this in good shape.
Patches tested on an Olimex Lime with 4GB Hynix nand. Has a partition as defined in sunxi-common.h with two UBIFS partitions (boot, rootfs) set up in Linux. To me it feels self-explanatory how to set this up, but I've spent the last few weeks full-time on this, implying my view of the world and how it revolves around MTD is rather distorted. Feel free to ask directed questions if you desire assistance in setting up and/or testing.
From the sunxi side these patches look good, I've one quite minor nitpick which I will explain in a reply to the patch in question. I think the most work here is getting the generic mtd patches ready and accepted by Scott Wood (the u-boot mtd maintainer), so please coordinate this further with him.
Once those bits are in I'm more then happy to merge the sunxi side of things.
As for merging the mtd bits, since the u-boot mtd code is somewhat of a copy of the Linux mtd code it may be best to simply sync the existing mtd code up with the upstream kernel mtd code, and work on getting the missing mtd bits merged into the upstream kernel first, and then merge the changes back into the u-boot mtd code, that assumes that the u-boot mtd code still is somewhat in sync with the kernel code atm though, and from your comments I'm getting the impression that it is not...
Anyways I will leave figuring this out between you and Scott. Note that we could use some help with upstreaming the kernel bits regardless of this. If you plan to work on this, please coordinate with Boris as Boris will likely start working on the mtd kernel code again soonish.
Regards,
Hans

Hi,
On 05-06-15 13:52, Roy Spliet wrote:
Following up on earlier SPL patches, here a series based on Yassin Jaffer's work to bring NAND support to U-boot. RFC because I know that the sunxi nand configuration options are dependent on a work-in-progress by Daniel - trying to deliver a single SPL for both MMC and NAND boot. Given I have spent the past few weeks learning how Boris' original driver works, I have probably grown a bit blind for potentially existing issues. Please provide plenty of feedback so I can bring this in good shape.
Patches tested on an Olimex Lime with 4GB Hynix nand. Has a partition as defined in sunxi-common.h with two UBIFS partitions (boot, rootfs) set up in Linux. To me it feels self-explanatory how to set this up, but I've spent the last few weeks full-time on this, implying my view of the world and how it revolves around MTD is rather distorted. Feel free to ask directed questions if youdesire assistance in setting up and/or testing.
p.s.
If you want me to I can merge this, or the next version into my sunxi-wip branch so that people can play with it. Chances are I will do that anyways one of these days to play with it myself.
Talking about playing with this, can you provide a quick step by step on howto setup the ubifs stuff under Linux, assuming a kernel + dts with working nand support are present ?
Regards,
Hans

Hi All,
I'm eventually joining the discussion (better late than never ;-)).
You'll find my comments in each patch (or answers to those patches).
On Fri, 5 Jun 2015 13:52:33 +0200 Roy Spliet r.spliet@ultimaker.com wrote:
Following up on earlier SPL patches, here a series based on Yassin Jaffer's work to bring NAND support to U-boot. RFC because I know that the sunxi nand configuration options are dependent on a work-in-progress by Daniel - trying to deliver a single SPL for both MMC and NAND boot. Given I have spent the past few weeks learning how Boris' original driver works, I have probably grown a bit blind for potentially existing issues. Please provide plenty of feedback so I can bring this in good shape.
Patches tested on an Olimex Lime with 4GB Hynix nand. Has a partition as defined in sunxi-common.h with two UBIFS partitions (boot, rootfs) set up in Linux. To me it feels self-explanatory how to set this up, but I've spent the last few weeks full-time on this, implying my view of the world and how it revolves around MTD is rather distorted. Feel free to ask directed questions if you desire assistance in setting up and/or testing. Thanks!
That would be great to have a summary of the changelog in the cover letter (you can generate it with --cover-letter when you generate your patches using git format-patch).
Best Regards,
Boris
participants (7)
-
Boris Brezillon
-
Hans de Goede
-
Ian Campbell
-
Michal Suchanek
-
Roy Spliet
-
Scott Wood
-
Yassin