Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

[RFC PATCH 6/9] mtd: nand: add sunxi NFC dt bindings doc

16 views
Skip to first unread message

Boris BREZILLON

unread,
Jan 8, 2014, 9:30:01 AM1/8/14
to
Add the sunxi NAND Flash Controller dt bindings documentation.

Signed-off-by: Boris BREZILLON <b.bre...@overkiz.com>
---
.../devicetree/bindings/mtd/sunxi-nand.txt | 71 ++++++++++++++++++++
1 file changed, 71 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mtd/sunxi-nand.txt

diff --git a/Documentation/devicetree/bindings/mtd/sunxi-nand.txt b/Documentation/devicetree/bindings/mtd/sunxi-nand.txt
new file mode 100644
index 0000000..c3206fc
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/sunxi-nand.txt
@@ -0,0 +1,71 @@
+Allwinner NAND Flash Controller (NFC)
+
+Required properties:
+- compatible : "allwinner,sun4i-nfc".
+- reg : shall contain registers location and length for data and reg.
+- interrupts : shall define the NFC interrupt.
+- #address-cells: shall be set to 1. Encode the nand CS.
+- #size-cells : shall be set to 0.
+- clocks : shall reference NFC clocks.
+- clock-names : NFC internal clock names. Shall contain :
+ * "ahb_clk" : AHB gating clock
+ * "sclk" : NFC clock
+
+Optional children nodes:
+Children nodes represent the available nand chips.
+
+Required properties:
+- reg : shall contain the CS ids (a given chip might use several CS)
+- tCLS-min : see Documentation/devicetree/mtd/nand.txt
+- tCLH-min : Documentation/devicetree/mtd/nand.txt
+- tCS-min : see Documentation/devicetree/mtd/nand.txt
+- tCH-min : see Documentation/devicetree/mtd/nand.txt
+- tWP-min : see Documentation/devicetree/mtd/nand.txt
+- tWH-min : see Documentation/devicetree/mtd/nand.txt
+- tALS-min : see Documentation/devicetree/mtd/nand.txt
+- tDS-min : see Documentation/devicetree/mtd/nand.txt
+- tDH-min : see Documentation/devicetree/mtd/nand.txt
+- tRR-min : see Documentation/devicetree/mtd/nand.txt
+- tALH-min : see Documentation/devicetree/mtd/nand.txt
+- tRP-min : see Documentation/devicetree/mtd/nand.txt
+- tREH-min : see Documentation/devicetree/mtd/nand.txt
+- tRC-min : see Documentation/devicetree/mtd/nand.txt
+- tWC-min : see Documentation/devicetree/mtd/nand.txt
+
+Optional properties:
+- allwinner,rb : shall contain the native Ready/Busy ids.
+ or
+- rb-gpios : shall contain the gpios used as R/B pins.
+
+see Documentation/devicetree/mtd/nand.txt for generic bindings.
+
+
+Examples:
+nfc: nand@01c03000 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&nand_pins_a &nand_cs0_pins_a &nand_rb0_pins_a>;
+ status = "okay";
+
+ nand@0 {
+ reg = <0>;
+ allwinner,rb = <0>;
+ nand-ecc-mode = "soft_bch";
+
+ /* nand timings */
+ tCLS-min = <6>;
+ tCLH-min = <3>;
+ tCS-min = <20>;
+ tCH-min = <5>;
+ tWP-min = <8>;
+ tWH-min = <6>;
+ tALS-min = <6>;
+ tDS-min = <6>;
+ tDH-min = <2>;
+ tRR-min = <20>;
+ tALH-min = <3>;
+ tRP-min = <8>;
+ tREH-min = <6>;
+ tRC-min = <16>;
+ tWC-min = <16>;
+ };
+};
--
1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majo...@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

Boris BREZILLON

unread,
Jan 8, 2014, 9:30:02 AM1/8/14
to
Define the NAND pinctrl configs.

Signed-off-by: Boris BREZILLON <b.bre...@overkiz.com>
---
arch/arm/boot/dts/sun7i-a20.dtsi | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)

diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi
index c00a577..34b1948 100644
--- a/arch/arm/boot/dts/sun7i-a20.dtsi
+++ b/arch/arm/boot/dts/sun7i-a20.dtsi
@@ -389,6 +389,30 @@
allwinner,drive = <0>;
allwinner,pull = <0>;
};
+
+ 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_rb0_pins_a: nand_rb@0 {
+ allwinner,pins = "PC6";
+ allwinner,function = "nand0";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
};

timer@01c20c00 {

Boris BREZILLON

unread,
Jan 8, 2014, 9:40:02 AM1/8/14
to
Add a NAND timing properties to NAND dt doc.

Signed-off-by: Boris BREZILLON <b.bre...@overkiz.com>
---
Documentation/devicetree/bindings/mtd/nand.txt | 34 ++++++++++++++++++++++++
1 file changed, 34 insertions(+)

diff --git a/Documentation/devicetree/bindings/mtd/nand.txt b/Documentation/devicetree/bindings/mtd/nand.txt
index 03855c8..0dff600 100644
--- a/Documentation/devicetree/bindings/mtd/nand.txt
+++ b/Documentation/devicetree/bindings/mtd/nand.txt
@@ -5,3 +5,37 @@
"soft_bch".
- nand-bus-width : 8 or 16 bus width if not present 8
- nand-on-flash-bbt: boolean to enable on flash bbt option if not present false
+
+nand timings definition expressed in nanoseconds (found in NAND datasheets):
+ - tCLS-min : CLE setup time
+ - tCLH-min : CLE Hold time
+ - tCS-min : CE# setup time
+ - tCH-min : CE# hold time
+ - tWP-min : WE# pulse width
+ - tALS-min : ALE setup time
+ - tALH-min : ALE hold time
+ - tDS-min : Data setup time
+ - tDH-min : Data hold time
+ - tWC-min : Write cycle time
+ - tWH-min : WE# high hold time
+ - tR-max : Data transfer from cell to register
+ - tAR-min : ALE to RE# delay
+ - tCLR-min : CLE to RE# delay
+ - tRR-min : Ready to RE# low
+ - tRP-min : RE# pulse width
+ - tWB-max : WE# high to busy
+ - tRC-min : Read cycle time
+ - tREA-max : RE# access time
+ - tRHZ-max : RE# high to output high Z
+ - tCHZ-max : CE# high to output high Z
+ - tRHOH-min : RE# high to output hold
+ - tRLOH-min : RE# low to output hold
+ - tCOH-min : RE# or CE# high to output hold
+ - tREH-min : RE# high hold time
+ - tWHR-min : WE# high to RE# low
+ - tRHW-min : RE# high to WE# low
+ - tIR-min : Output high Z to RE# low
+ - tCR-min : CE# low to RE# low
+ - tADL-min : Address to data loading time
+ - tRST-max : Device resetting time
+ - tWW-min : Write protection time

Boris BREZILLON

unread,
Jan 8, 2014, 9:40:02 AM1/8/14
to
Hello,

This series add the sunxi NFC support with up to 8 NAND chip connected.
I'm still in the early stages drivers development and some key features are
missing, but it's usable (I tested it on the cubietruck board).

Here's what's missing:
- HW ECC support
- DMA support
- HW randomization support
- many more improvements

This series depends on Emilio's patch series implementing mod0 clks
(http://lists.infradead.org/pipermail/linux-arm-kernel/2013-July/185478.html)
+ an other patch not yet posted
(http://git.elopez.com.ar/linux/commits/5b4eb3ac406b9c98965714d40e8dd6da943d1ab0)


Best Regards,

Boris

Boris BREZILLON (9):
mtd: nand: retrieve ECC requirements from Hynix READ ID byte 4
mtd: nand: define struct nand_timings
of: mtd: add NAND timings retrieval support
of: mtd: add NAND timings bindings documentation
mtd: nand: add sunxi NFC support
mtd: nand: add sunxi NFC dt bindings doc
ARM: dt/sunxi: add NFC node to Allwinner A20 SoC
ARM: dt/sunxi: add NFC pinctrl pin definitions
ARM: sunxi/dt: enable NAND on cubietruck board

Documentation/devicetree/bindings/mtd/nand.txt | 34 +
.../devicetree/bindings/mtd/sunxi-nand.txt | 71 ++
arch/arm/boot/dts/sun7i-a20-cubietruck.dts | 33 +
arch/arm/boot/dts/sun7i-a20.dtsi | 35 +
drivers/mtd/nand/Kconfig | 6 +
drivers/mtd/nand/Makefile | 1 +
drivers/mtd/nand/nand_base.c | 37 ++
drivers/mtd/nand/sunxi_nfc.c | 700 ++++++++++++++++++++
drivers/of/of_mtd.c | 47 ++
include/linux/mtd/nand.h | 44 ++
include/linux/of_mtd.h | 9 +
11 files changed, 1017 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mtd/sunxi-nand.txt
create mode 100644 drivers/mtd/nand/sunxi_nfc.c

Boris BREZILLON

unread,
Jan 8, 2014, 9:40:02 AM1/8/14
to
Add NAND Flash controller node definition to the A20 SoC.

Signed-off-by: Boris BREZILLON <b.bre...@overkiz.com>
---
arch/arm/boot/dts/sun7i-a20.dtsi | 11 +++++++++++
1 file changed, 11 insertions(+)

diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi
index 8e4cdcc..c00a577 100644
--- a/arch/arm/boot/dts/sun7i-a20.dtsi
+++ b/arch/arm/boot/dts/sun7i-a20.dtsi
@@ -299,6 +299,17 @@
#size-cells = <1>;
ranges;

+ nfc: nand@01c03000 {
+ compatible = "allwinner,sun4i-nfc";
+ reg = <0x01c03000 0x1000>;
+ interrupts = <0 37 1>;
+ clocks = <&ahb_gates 13>, <&nand_clk>;
+ clock-names = "ahb_clk", "sclk";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+ };
+
emac: ethernet@01c0b000 {
compatible = "allwinner,sun4i-emac";
reg = <0x01c0b000 0x1000>;

Boris BREZILLON

unread,
Jan 8, 2014, 9:40:03 AM1/8/14
to
Add a function to retrieve NAND timings from a given DT node.

Signed-off-by: Boris BREZILLON <b.bre...@overkiz.com>
---
drivers/of/of_mtd.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++
include/linux/of_mtd.h | 9 +++++++++
2 files changed, 56 insertions(+)

diff --git a/drivers/of/of_mtd.c b/drivers/of/of_mtd.c
index a27ec94..52e07fd 100644
--- a/drivers/of/of_mtd.c
+++ b/drivers/of/of_mtd.c
@@ -83,3 +83,50 @@ bool of_get_nand_on_flash_bbt(struct device_node *np)
return of_property_read_bool(np, "nand-on-flash-bbt");
}
EXPORT_SYMBOL_GPL(of_get_nand_on_flash_bbt);
+
+/**
+ * of_get_nand_timings - Get nand timings for the given device_node
+ * @np: Pointer to the given device_node
+ *
+ * return 0 on success errno other wise
+ */
+int of_get_nand_timings(struct device_node *np, struct nand_timings *timings)
+{
+ memset(timings, 0, sizeof(*timings));
+
+ of_property_read_u32(np, "tCLS-min", &timings->tCLS_min);
+ of_property_read_u32(np, "tCLH-min", &timings->tCLH_min);
+ of_property_read_u32(np, "tCS-min", &timings->tCS_min);
+ of_property_read_u32(np, "tCH-min", &timings->tCH_min);
+ of_property_read_u32(np, "tWP-min", &timings->tWP_min);
+ of_property_read_u32(np, "tALS-min", &timings->tALS_min);
+ of_property_read_u32(np, "tALH-min", &timings->tALH_min);
+ of_property_read_u32(np, "tDS-min", &timings->tDS_min);
+ of_property_read_u32(np, "tDH-min", &timings->tDH_min);
+ of_property_read_u32(np, "tWC-min", &timings->tWC_min);
+ of_property_read_u32(np, "tWH-min", &timings->tWH_min);
+ of_property_read_u32(np, "tR-max", &timings->tR_max);
+ of_property_read_u32(np, "tAR-min", &timings->tAR_min);
+ of_property_read_u32(np, "tCLR-min", &timings->tCLR_min);
+ of_property_read_u32(np, "tRR-min", &timings->tRR_min);
+ of_property_read_u32(np, "tRP-min", &timings->tRP_min);
+ of_property_read_u32(np, "tWB-max", &timings->tWB_max);
+ of_property_read_u32(np, "tRC-min", &timings->tRC_min);
+ of_property_read_u32(np, "tREA-max", &timings->tREA_max);
+ of_property_read_u32(np, "tRHZ-max", &timings->tRHZ_max);
+ of_property_read_u32(np, "tCHZ-max", &timings->tCHZ_max);
+ of_property_read_u32(np, "tRHOH-min", &timings->tRHOH_min);
+ of_property_read_u32(np, "tRLOH-min", &timings->tRLOH_min);
+ of_property_read_u32(np, "tCOH-min", &timings->tCOH_min);
+ of_property_read_u32(np, "tREH-min", &timings->tREH_min);
+ of_property_read_u32(np, "tWHR-min", &timings->tWHR_min);
+ of_property_read_u32(np, "tRHW-min", &timings->tRHW_min);
+ of_property_read_u32(np, "tIR-min", &timings->tIR_min);
+ of_property_read_u32(np, "tCR-min", &timings->tCR_min);
+ of_property_read_u32(np, "tADL-min", &timings->tADL_min);
+ of_property_read_u32(np, "tRST-max", &timings->tRST_max);
+ of_property_read_u32(np, "tWW-min", &timings->tWW_min);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(of_get_nand_timings);
diff --git a/include/linux/of_mtd.h b/include/linux/of_mtd.h
index 6f10e93..ecedb5f 100644
--- a/include/linux/of_mtd.h
+++ b/include/linux/of_mtd.h
@@ -9,12 +9,15 @@
#ifndef __LINUX_OF_MTD_H
#define __LINUX_OF_NET_H

+#include <linux/mtd/nand.h>
+
#ifdef CONFIG_OF_MTD

#include <linux/of.h>
int of_get_nand_ecc_mode(struct device_node *np);
int of_get_nand_bus_width(struct device_node *np);
bool of_get_nand_on_flash_bbt(struct device_node *np);
+int of_get_nand_timings(struct device_node *np, struct nand_timings *timings);

#else /* CONFIG_OF_MTD */

@@ -33,6 +36,12 @@ static inline bool of_get_nand_on_flash_bbt(struct device_node *np)
return false;
}

+static inline int of_get_nand_timings(struct device_node *np,
+ struct nand_timings *timings)
+{
+ return -ENOSYS;
+}
+
#endif /* CONFIG_OF_MTD */

#endif /* __LINUX_OF_MTD_H */

Boris BREZILLON

unread,
Jan 8, 2014, 9:40:03 AM1/8/14
to
Define a struct containing the standard NAND timings as described in NAND
datasheets.

Signed-off-by: Boris BREZILLON <b.bre...@overkiz.com>
---
include/linux/mtd/nand.h | 44 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 44 insertions(+)

diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 9e6c8f9..3dda312 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -805,4 +805,48 @@ static inline bool nand_is_slc(struct nand_chip *chip)
{
return chip->bits_per_cell == 1;
}
+
+/**
+ * struct nand_timings - NAND chip timing definitions
+ *
+ * This struct defines the timing requirements of a NAND chip.
+ * These informations can be found in every NAND datasheets.
+ *
+ * All fields are optional and depend on the hardware driver requirements
+ */
+struct nand_timings {
+ u32 tCLS_min;
+ u32 tCLH_min;
+ u32 tCS_min;
+ u32 tCH_min;
+ u32 tWP_min;
+ u32 tALS_min;
+ u32 tALH_min;
+ u32 tDS_min;
+ u32 tDH_min;
+ u32 tWC_min;
+ u32 tWH_min;
+ u32 tR_max;
+ u32 tAR_min;
+ u32 tCLR_min;
+ u32 tRR_min;
+ u32 tRP_min;
+ u32 tWB_max;
+ u32 tRC_min;
+ u32 tREA_max;
+ u32 tRHZ_max;
+ u32 tCHZ_max;
+ u32 tRHOH_min;
+ u32 tRLOH_min;
+ u32 tCOH_min;
+ u32 tREH_min;
+ u32 tWHR_min;
+ u32 tRHW_min;
+ u32 tIR_min;
+ u32 tCR_min;
+ u32 tADL_min;
+ u32 tRST_max;
+ u32 tWW_min;
+};
+
#endif /* __LINUX_MTD_NAND_H */

Boris BREZILLON

unread,
Jan 8, 2014, 9:40:03 AM1/8/14
to
The Hynix nand flashes store their ECC requirements in byte 4 of its id
(returned on READ ID command).

Signed-off-by: Boris BREZILLON <b.bre...@overkiz.com>
---
drivers/mtd/nand/nand_base.c | 37 +++++++++++++++++++++++++++++++++++++
1 file changed, 37 insertions(+)

diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index bd39f7b..15069ec 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -3202,6 +3202,43 @@ static void nand_decode_ext_id(struct mtd_info *mtd, struct nand_chip *chip,
else
mtd->erasesize = (64 * 1024) << tmp;
*busw = 0;
+
+ /* Retrieve ECC infos */
+ switch ((id_data[4] >> 4) & 0x7) {
+ case 1:
+ chip->ecc_step_ds = 512;
+ chip->ecc_strength_ds = 1;
+ break;
+ case 2:
+ chip->ecc_step_ds = 512;
+ chip->ecc_strength_ds = 2;
+ break;
+ case 3:
+ chip->ecc_step_ds = 512;
+ chip->ecc_strength_ds = 4;
+ break;
+ case 4:
+ chip->ecc_step_ds = 512;
+ chip->ecc_strength_ds = 8;
+ break;
+ case 5:
+ chip->ecc_step_ds = 1024;
+ chip->ecc_strength_ds = 24;
+ break;
+ case 6:
+ chip->ecc_step_ds = 1024;
+ chip->ecc_strength_ds = 32;
+ break;
+ case 7:
+ chip->ecc_step_ds = 1024;
+ chip->ecc_strength_ds = 40;
+ break;
+ case 0:
+ default:
+ chip->ecc_step_ds = 0;
+ chip->ecc_strength_ds = 0;
+ break;
+ }
} else {
/* Calc pagesize */
mtd->writesize = 1024 << (extid & 0x03);

Boris BREZILLON

unread,
Jan 8, 2014, 9:40:03 AM1/8/14
to
Add the sunxi NAND Flash Controller driver.

Signed-off-by: Boris BREZILLON <b.bre...@overkiz.com>
---
drivers/mtd/nand/Kconfig | 6 +
drivers/mtd/nand/Makefile | 1 +
drivers/mtd/nand/sunxi_nfc.c | 700 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 707 insertions(+)
create mode 100644 drivers/mtd/nand/sunxi_nfc.c

diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 93ae6a6..784dd42 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -510,4 +510,10 @@ config MTD_NAND_XWAY
Enables support for NAND Flash chips on Lantiq XWAY SoCs. NAND is attached
to the External Bus Unit (EBU).

+config MTD_NAND_SUNXI
+ tristate "Support for NAND on Allwinner SoCs"
+ depends on ARCH_SUNXI
+ help
+ Enables support for NAND Flash chips on Allwinner SoCs.
+
endif # MTD_NAND
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 542b568..e8b210d 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -49,5 +49,6 @@ obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o
obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/
obj-$(CONFIG_MTD_NAND_XWAY) += xway_nand.o
obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/
+obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nfc.o

nand-objs := nand_base.o nand_bbt.o
diff --git a/drivers/mtd/nand/sunxi_nfc.c b/drivers/mtd/nand/sunxi_nfc.c
new file mode 100644
index 0000000..1c7a511
--- /dev/null
+++ b/drivers/mtd/nand/sunxi_nfc.c
@@ -0,0 +1,700 @@
+/*
+ * Copyright (C) 2013 Boris BREZILLON <b.bre...@overkiz.com>
+ *
+ * Derived from Qiang Yu work:
+ * https://github.com/yuq/sunxi-nfc-mtd
+ * Copyright (C) 2013 Qiang Yu <yuq...@gmail.com>
+ *
+ * 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.
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/of_mtd.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+
+#define NFC_REG_CTL 0x0000
+#define NFC_REG_ST 0x0004
+#define NFC_REG_INT 0x0008
+#define NFC_REG_TIMING_CTL 0x000C
+#define NFC_REG_TIMING_CFG 0x0010
+#define NFC_REG_ADDR_LOW 0x0014
+#define NFC_REG_ADDR_HIGH 0x0018
+#define NFC_REG_SECTOR_NUM 0x001C
+#define NFC_REG_CNT 0x0020
+#define NFC_REG_CMD 0x0024
+#define NFC_REG_RCMD_SET 0x0028
+#define NFC_REG_WCMD_SET 0x002C
+#define NFC_REG_IO_DATA 0x0030
+#define NFC_REG_ECC_CTL 0x0034
+#define NFC_REG_ECC_ST 0x0038
+#define NFC_REG_DEBUG 0x003C
+#define NFC_REG_ECC_CNT0 0x0040
+#define NFC_REG_ECC_CNT1 0x0044
+#define NFC_REG_ECC_CNT2 0x0048
+#define NFC_REG_ECC_CNT3 0x004c
+#define NFC_REG_USER_DATA_BASE 0x0050
+#define NFC_REG_SPARE_AREA 0x00A0
+#define NFC_RAM0_BASE 0x0400
+#define NFC_RAM1_BASE 0x0800
+
+/*define bit use in NFC_CTL*/
+#define NFC_EN (1 << 0)
+#define NFC_RESET (1 << 1)
+#define NFC_BUS_WIDYH (1 << 2)
+#define NFC_RB_SEL (1 << 3)
+#define NFC_CE_SEL (7 << 24)
+#define NFC_CE_CTL (1 << 6)
+#define NFC_CE_CTL1 (1 << 7)
+#define NFC_PAGE_SIZE (0xf << 8)
+#define NFC_SAM (1 << 12)
+#define NFC_RAM_METHOD (1 << 14)
+#define NFC_DEBUG_CTL (1 << 31)
+
+/*define bit use in NFC_ST*/
+#define NFC_RB_B2R (1 << 0)
+#define NFC_CMD_INT_FLAG (1 << 1)
+#define NFC_DMA_INT_FLAG (1 << 2)
+#define NFC_CMD_FIFO_STATUS (1 << 3)
+#define NFC_STA (1 << 4)
+#define NFC_NATCH_INT_FLAG (1 << 5)
+#define NFC_RB_STATE0 (1 << 8)
+#define NFC_RB_STATE1 (1 << 9)
+#define NFC_RB_STATE2 (1 << 10)
+#define NFC_RB_STATE3 (1 << 11)
+
+/*define bit use in NFC_INT*/
+#define NFC_B2R_INT_ENABLE (1 << 0)
+#define NFC_CMD_INT_ENABLE (1 << 1)
+#define NFC_DMA_INT_ENABLE (1 << 2)
+#define NFC_INT_MASK (NFC_B2R_INT_ENABLE | \
+ NFC_CMD_INT_ENABLE | \
+ NFC_DMA_INT_ENABLE)
+
+
+/*define bit use in NFC_CMD*/
+#define NFC_CMD_LOW_BYTE (0xff << 0)
+#define NFC_CMD_HIGH_BYTE (0xff << 8)
+#define NFC_ADR_NUM (0x7 << 16)
+#define NFC_SEND_ADR (1 << 19)
+#define NFC_ACCESS_DIR (1 << 20)
+#define NFC_DATA_TRANS (1 << 21)
+#define NFC_SEND_CMD1 (1 << 22)
+#define NFC_WAIT_FLAG (1 << 23)
+#define NFC_SEND_CMD2 (1 << 24)
+#define NFC_SEQ (1 << 25)
+#define NFC_DATA_SWAP_METHOD (1 << 26)
+#define NFC_ROW_AUTO_INC (1 << 27)
+#define NFC_SEND_CMD3 (1 << 28)
+#define NFC_SEND_CMD4 (1 << 29)
+#define NFC_CMD_TYPE (3 << 30)
+
+/* define bit use in NFC_RCMD_SET*/
+#define NFC_READ_CMD (0xff << 0)
+#define NFC_RANDOM_READ_CMD0 (0xff << 8)
+#define NFC_RANDOM_READ_CMD1 (0xff << 16)
+
+/*define bit use in NFC_WCMD_SET*/
+#define NFC_PROGRAM_CMD (0xff << 0)
+#define NFC_RANDOM_WRITE_CMD (0xff << 8)
+#define NFC_READ_CMD0 (0xff << 16)
+#define NFC_READ_CMD1 (0xff << 24)
+
+/*define bit use in NFC_ECC_CTL*/
+#define NFC_ECC_EN (1 << 0)
+#define NFC_ECC_PIPELINE (1 << 3)
+#define NFC_ECC_EXCEPTION (1 << 4)
+#define NFC_ECC_BLOCK_SIZE (1 << 5)
+#define NFC_RANDOM_EN (1 << 9)
+#define NFC_RANDOM_DIRECTION (1 << 10)
+#define NFC_ECC_MODE_SHIFT 12
+#define NFC_ECC_MODE (0xf << NFC_ECC_MODE_SHIFT)
+#define NFC_RANDOM_SEED (0x7fff << 16)
+
+
+
+enum sunxi_nand_rb_type {
+ RB_NONE,
+ RB_NATIVE,
+ RB_GPIO,
+};
+
+struct sunxi_nand_rb {
+ enum sunxi_nand_rb_type type;
+ union {
+ int gpio;
+ int nativeid;
+ } info;
+};
+
+struct sunxi_nand_chip_sel {
+ u8 cs;
+ struct sunxi_nand_rb rb;
+};
+
+#define DEFAULT_NAME_FORMAT "nand@%d"
+#define MAX_NAME_SIZE (sizeof("nand@") + 2)
+
+struct sunxi_nand_chip {
+ struct list_head node;
+ struct nand_chip nand;
+ struct mtd_info mtd;
+ char default_name[MAX_NAME_SIZE];
+ 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 mtd_info *mtd)
+{
+ return container_of(mtd, struct sunxi_nand_chip, mtd);
+}
+
+struct sunxi_nfc {
+ struct nand_hw_control controller;
+ void __iomem *regs;
+ int irq;
+ struct clk *ahb_clk;
+ struct clk *sclk;
+ unsigned long assigned_cs;
+ struct list_head chips;
+ struct completion complete;
+};
+
+static inline struct sunxi_nfc *to_sunxi_nfc(struct nand_hw_control *ctrl)
+{
+ return container_of(ctrl, struct sunxi_nfc, controller);
+}
+
+static irqreturn_t sunxi_nfc_interrupt(int irq, void *dev_id)
+{
+ struct sunxi_nfc *nfc = dev_id;
+ u32 st = readl(nfc->regs + NFC_REG_ST);
+ u32 ien = readl(nfc->regs + NFC_REG_INT);
+
+ if (!(ien & st))
+ return IRQ_NONE;
+
+ if ((ien & st) == ien)
+ complete(&nfc->complete);
+
+ writel(st & NFC_INT_MASK, nfc->regs + NFC_REG_ST);
+ writel(~st & ien & NFC_INT_MASK, nfc->regs + NFC_REG_INT);
+
+ return IRQ_HANDLED;
+}
+
+static int sunxi_nfc_wait_int(struct sunxi_nfc *nfc, u32 flags,
+ unsigned int timeout_ms)
+{
+ init_completion(&nfc->complete);
+
+ writel(flags, nfc->regs + NFC_REG_INT);
+ if (!timeout_ms)
+ wait_for_completion(&nfc->complete);
+ else if (!wait_for_completion_timeout(&nfc->complete,
+ msecs_to_jiffies(timeout_ms)))
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static int sunxi_nfc_dev_ready(struct mtd_info *mtd)
+{
+ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(mtd);
+ 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 + NFC_REG_ST) &
+ (NFC_RB_STATE0 << rb->info.nativeid));
+ if (ret)
+ break;
+
+ sunxi_nfc_wait_int(nfc, NFC_RB_B2R, timeo);
+ ret = !!(readl(nfc->regs + NFC_REG_ST) &
+ (NFC_RB_STATE0 << rb->info.nativeid));
+ break;
+ case RB_GPIO:
+ ret = gpio_get_value(rb->info.gpio);
+ break;
+ case RB_NONE:
+ default:
+ ret = 0;
+ dev_err(&mtd->dev, "cannot check R/B NAND status!");
+ break;
+ }
+
+ return ret;
+}
+
+static void sunxi_nfc_select_chip(struct mtd_info *mtd, int chip)
+{
+ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(mtd);
+ 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 + NFC_REG_CTL) &
+ ~(NFC_CE_SEL | NFC_RB_SEL | NFC_EN);
+
+ if (chip >= 0) {
+ sel = &sunxi_nand->sels[chip];
+
+ ctl |= (sel->cs << 24) | NFC_EN |
+ (((sunxi_nand->nand.page_shift - 10) & 0xf) << 8);
+ if (sel->rb.type == RB_NONE) {
+ sunxi_nand->nand.dev_ready = NULL;
+ } else {
+ sunxi_nand->nand.dev_ready = sunxi_nfc_dev_ready;
+ if (sel->rb.type == RB_NATIVE)
+ ctl |= (sel->rb.info.nativeid << 3);
+ }
+ }
+
+ writel(ctl, nfc->regs + NFC_REG_CTL);
+ clk_set_rate(nfc->sclk, sunxi_nand->clk_rate);
+
+ sunxi_nand->selected = chip;
+}
+
+static void sunxi_nfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(mtd);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
+ int cnt;
+ int offs = 0;
+
+ while (len > 0) {
+ cnt = len > 1024 ? 1024 : len;
+ while ((readl(nfc->regs + NFC_REG_ST) & NFC_CMD_FIFO_STATUS))
+ ;
+ writel(cnt, nfc->regs + NFC_REG_CNT);
+ writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_SEQ,
+ nfc->regs + NFC_REG_CMD);
+ sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
+ memcpy_fromio(buf + offs, nfc->regs + NFC_RAM0_BASE, cnt);
+ offs += cnt;
+ len -= cnt;
+ }
+}
+
+static void sunxi_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf,
+ int len)
+{
+ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(mtd);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
+ int cnt;
+ int offs = 0;
+
+ while (len > 0) {
+ cnt = len > 1024 ? 1024 : len;
+ while ((readl(nfc->regs + NFC_REG_ST) & NFC_CMD_FIFO_STATUS))
+ ;
+ writel(cnt, nfc->regs + NFC_REG_CNT);
+ memcpy_toio(nfc->regs + NFC_RAM0_BASE, buf + offs, cnt);
+ writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD |
+ NFC_ACCESS_DIR | NFC_SEQ, nfc->regs + NFC_REG_CMD);
+ sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
+ offs += cnt;
+ len -= cnt;
+ }
+}
+
+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 sunxi_nand_chip *sunxi_nand = to_sunxi_nand(mtd);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
+ u32 tmp;
+
+ while ((readl(nfc->regs + NFC_REG_ST) & NFC_CMD_FIFO_STATUS))
+ ;
+
+ if (ctrl & NAND_CTRL_CHANGE) {
+ tmp = readl(nfc->regs + NFC_REG_CTL);
+ if (ctrl & NAND_NCE)
+ tmp |= NFC_CE_CTL;
+ else
+ tmp &= ~NFC_CE_CTL;
+ writel(tmp, nfc->regs + NFC_REG_CTL);
+ }
+
+ if (dat == NAND_CMD_NONE)
+ return;
+
+ if (ctrl & NAND_CLE) {
+ writel(NFC_SEND_CMD1 | dat, nfc->regs + NFC_REG_CMD);
+ } else {
+ writel(dat, nfc->regs + NFC_REG_ADDR_LOW);
+ writel(NFC_SEND_ADR, nfc->regs + NFC_REG_CMD);
+ }
+
+ sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
+}
+
+static int sunxi_nand_chip_init_timings(struct sunxi_nand_chip *chip,
+ struct device_node *np)
+{
+ struct nand_timings timings;
+ u32 min_clk_period = 0;
+ int ret;
+
+ ret = of_get_nand_timings(np, &timings);
+ if (ret)
+ return ret;
+
+ /* NFC timings defined in Allwinner Datasheets */
+
+ /* 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)
+ chip->clk_rate = 20000000;
+ else
+ chip->clk_rate = (2 * 1000000000) / min_clk_period;
+
+ /* TODO: configure T16-T19 */
+
+ return 0;
+}
+
+static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
+ struct device_node *np)
+{
+ struct sunxi_nand_chip *chip;
+ struct mtd_part_parser_data ppdata;
+ struct mtd_info *mtd;
+ struct nand_chip *nand;
+ int nsels;
+ int ret;
+ int i;
+ u32 tmp;
+
+ if (!of_get_property(np, "reg", &nsels))
+ return -EINVAL;
+
+ nsels /= sizeof(u32);
+ if (!nsels)
+ return -EINVAL;
+
+ chip = devm_kzalloc(dev,
+ 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 = of_property_read_u32_index(np, "reg", i, &tmp);
+ if (ret)
+ return ret;
+
+ if (tmp > 7)
+ return -EINVAL;
+
+ if (test_and_set_bit(tmp, &nfc->assigned_cs))
+ return -EINVAL;
+
+ chip->sels[i].cs = tmp;
+
+ if (!of_property_read_u32_index(np, "allwinner,rb", i, &tmp) &&
+ tmp < 2) {
+ chip->sels[i].rb.type = RB_NATIVE;
+ chip->sels[i].rb.info.nativeid = tmp;
+ } else {
+ ret = of_get_named_gpio(np, "rb-gpios", i);
+ if (ret >= 0) {
+ chip->sels[i].rb.type = RB_GPIO;
+ chip->sels[i].rb.info.gpio = tmp;
+ ret = devm_gpio_request(dev, tmp, "nand-rb");
+ if (ret)
+ return ret;
+ } else {
+ chip->sels[i].rb.type = RB_NONE;
+ }
+ }
+ }
+
+ ret = sunxi_nand_chip_init_timings(chip, np);
+ if (ret)
+ return ret;
+
+ nand = &chip->nand;
+ nand->IO_ADDR_R = nand->IO_ADDR_W = nfc->regs + NFC_REG_IO_DATA;
+ 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;
+
+ nand->ecc.mode = of_get_nand_ecc_mode(np);
+ if (of_get_nand_on_flash_bbt(np))
+ nand->bbt_options |= NAND_BBT_USE_FLASH;
+
+ mtd = &chip->mtd;
+ mtd->priv = nand;
+ mtd->owner = THIS_MODULE;
+
+ ret = nand_scan_ident(mtd, nsels, NULL);
+ if (ret)
+ return ret;
+
+ if (nand->ecc.mode == NAND_ECC_SOFT_BCH) {
+ nand->ecc.size = nand->ecc_step_ds;
+ nand->ecc.bytes = (((nand->ecc_strength_ds *
+ fls(8 * nand->ecc_step_ds)) + 7) / 8);
+ }
+
+ ret = nand_scan_tail(mtd);
+ if (ret)
+ return ret;
+
+ if (of_property_read_string(np, "nand-name", &mtd->name)) {
+ snprintf(chip->default_name, MAX_NAME_SIZE,
+ DEFAULT_NAME_FORMAT, chip->sels[i].cs);
+ mtd->name = chip->default_name;
+ }
+
+ ppdata.of_node = np;
+ ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
+ if (!ret)
+ return ret;
+
+ list_add_tail(&chip->node, &nfc->chips);
+
+ return 0;
+}
+
+static int sunxi_nand_chips_init(struct device *dev, struct sunxi_nfc *nfc)
+{
+ struct device_node *np = dev->of_node;
+ struct device_node *nand_np;
+ int nchips = of_get_child_count(np);
+ int ret;
+
+ if (nchips > 8)
+ return -EINVAL;
+
+ for_each_child_of_node(np, nand_np) {
+ ret = sunxi_nand_chip_init(dev, nfc, nand_np);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int sunxi_nfc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *r;
+ struct sunxi_nfc *nfc;
+ int ret;
+
+ nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL);
+ if (!nfc) {
+ dev_err(dev, "failed to allocate NFC struct\n");
+ return -ENOMEM;
+ }
+
+ spin_lock_init(&nfc->controller.lock);
+ init_waitqueue_head(&nfc->controller.wq);
+ INIT_LIST_HEAD(&nfc->chips);
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ nfc->regs = devm_ioremap_resource(dev, r);
+ if (IS_ERR(nfc->regs)) {
+ dev_err(dev, "failed to remap iomem\n");
+ return PTR_ERR(nfc->regs);
+ }
+
+ nfc->irq = platform_get_irq(pdev, 0);
+ if (nfc->irq < 0) {
+ dev_err(dev, "failed to retrieve irq\n");
+ return nfc->irq;
+ }
+
+ nfc->ahb_clk = devm_clk_get(dev, "ahb_clk");
+ if (IS_ERR(nfc->ahb_clk)) {
+ dev_err(dev, "failed to retrieve ahb_clk\n");
+ return PTR_ERR(nfc->ahb_clk);
+ }
+
+ ret = clk_prepare_enable(nfc->ahb_clk);
+ if (ret)
+ return ret;
+
+ nfc->sclk = devm_clk_get(dev, "sclk");
+ if (IS_ERR(nfc->sclk)) {
+ dev_err(dev, "failed to retrieve nand_clk\n");
+ ret = PTR_ERR(nfc->sclk);
+ goto out_ahb_clk_unprepare;
+ }
+
+ ret = clk_prepare_enable(nfc->sclk);
+ if (ret)
+ goto out_ahb_clk_unprepare;
+
+ /* Reset NFC */
+ writel(readl(nfc->regs + NFC_REG_CTL) | NFC_RESET,
+ nfc->regs + NFC_REG_CTL);
+ while (readl(nfc->regs + NFC_REG_CTL) & NFC_RESET)
+ ;
+
+ writel(0, nfc->regs + NFC_REG_INT);
+ ret = devm_request_irq(dev, nfc->irq, sunxi_nfc_interrupt,
+ 0, "sunxi-nfc", nfc);
+ if (ret)
+ goto out_sclk_unprepare;
+
+ platform_set_drvdata(pdev, nfc);
+
+ writel(0x100, nfc->regs + NFC_REG_TIMING_CTL);
+ writel(0x7ff, nfc->regs + NFC_REG_TIMING_CFG);
+
+ ret = sunxi_nand_chips_init(dev, nfc);
+ if (ret) {
+ dev_err(dev, "failed to init nand chips\n");
+ goto out_sclk_unprepare;
+ }
+
+ return 0;
+
+out_sclk_unprepare:
+ clk_disable_unprepare(nfc->sclk);
+out_ahb_clk_unprepare:
+ clk_disable_unprepare(nfc->ahb_clk);
+
+ return ret;
+}
+
+static const struct of_device_id sunxi_nfc_ids[] = {
+ { .compatible = "allwinner,sun4i-nfc" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, sunxi_nfc_ids);
+
+static struct platform_driver sunxi_nfc_driver = {
+ .driver = {
+ .name = "sunxi_nfc",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(sunxi_nfc_ids),
+ },
+ .probe = sunxi_nfc_probe,
+};
+module_platform_driver(sunxi_nfc_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Boris BREZILLON");
+MODULE_DESCRIPTION("Allwinner NAND Flash Controller driver");
+MODULE_ALIAS("platform:sunxi_nfc");

Boris BREZILLON

unread,
Jan 8, 2014, 10:30:03 AM1/8/14
to
Enable the NFC and describe the NAND flash connected to this controller.

Signed-off-by: Boris BREZILLON <b.bre...@overkiz.com>
---
arch/arm/boot/dts/sun7i-a20-cubietruck.dts | 33 ++++++++++++++++++++++++++++
1 file changed, 33 insertions(+)

diff --git a/arch/arm/boot/dts/sun7i-a20-cubietruck.dts b/arch/arm/boot/dts/sun7i-a20-cubietruck.dts
index 8a1009d..08ee7d3 100644
--- a/arch/arm/boot/dts/sun7i-a20-cubietruck.dts
+++ b/arch/arm/boot/dts/sun7i-a20-cubietruck.dts
@@ -18,7 +18,40 @@
model = "Cubietech Cubietruck";
compatible = "cubietech,cubietruck", "allwinner,sun7i-a20";

+ chosen {
+ bootargs = "console=ttyS0,115200 earlyprintk root=/dev/ram0 rootfstype=ext2 rw";
+ };
+
soc@01c00000 {
+ nfc: nand@01c03000 {
+
pinctrl@01c20800 {
led_pins_cubietruck: led_pins@0 {
allwinner,pins = "PH7", "PH11", "PH20", "PH21";

boris brezillon

unread,
Jan 8, 2014, 10:40:02 AM1/8/14
to
On 08/01/2014 16:28, Boris BREZILLON wrote:
> Enable the NFC and describe the NAND flash connected to this controller.
>
> Signed-off-by: Boris BREZILLON <b.bre...@overkiz.com>
> ---
> arch/arm/boot/dts/sun7i-a20-cubietruck.dts | 33 ++++++++++++++++++++++++++++
> 1 file changed, 33 insertions(+)
>
> diff --git a/arch/arm/boot/dts/sun7i-a20-cubietruck.dts b/arch/arm/boot/dts/sun7i-a20-cubietruck.dts
> index 8a1009d..08ee7d3 100644
> --- a/arch/arm/boot/dts/sun7i-a20-cubietruck.dts
> +++ b/arch/arm/boot/dts/sun7i-a20-cubietruck.dts
> @@ -18,7 +18,40 @@
> model = "Cubietech Cubietruck";
> compatible = "cubietech,cubietruck", "allwinner,sun7i-a20";
>
> + chosen {
> + bootargs = "console=ttyS0,115200 earlyprintk root=/dev/ram0 rootfstype=ext2 rw";
> + };
> +

Sorry, I messed up with the bootargs.
I'll remove it in the next version.

boris brezillon

unread,
Jan 8, 2014, 11:40:01 AM1/8/14
to
On 08/01/2014 17:30, Rob Herring wrote:
> These all need to be documented.
They're document in PATCH 4/9 (but the description might be a bit
light).
> These apply to which compatible
> strings?
Actually this could apply to any nand chips.
The NAND Flash Controller driver is then responsible for converting
these values to use it.
> They should also be suffixed with the unit the property is in
> (i.e. -ms, -us, -ns).

Sure, I'll add the unit (which is -ns BTW).

Thanks.

Best Regards,

Boris
>
> Rob

Rob Herring

unread,
Jan 8, 2014, 11:40:02 AM1/8/14
to
On Wed, Jan 8, 2014 at 8:21 AM, Boris BREZILLON <b.bre...@overkiz.com> wrote:
These all need to be documented. These apply to which compatible
strings? They should also be suffixed with the unit the property is in
(i.e. -ms, -us, -ns).

Rob

Jason Gunthorpe

unread,
Jan 8, 2014, 1:40:01 PM1/8/14
to
On Wed, Jan 08, 2014 at 03:21:58PM +0100, Boris BREZILLON wrote:

> +int of_get_nand_timings(struct device_node *np, struct nand_timings *timings)
> +{
> + memset(timings, 0, sizeof(*timings));
> + of_property_read_u32(np, "tCLS-min", &timings->tCLS_min);
> + of_property_read_u32(np, "tCLH-min", &timings->tCLH_min);
> + of_property_read_u32(np, "tCS-min", &timings->tCS_min);
[..]

A while ago when discussing another controller it was pointed out
these values are all auto-probable directly from the NAND via a ONFI
defined GET FEATURE @0x01 query, and adding these timings to the DT
was NAK'd..

Basically you set the interface to the slowest ONFI timing mode, do
the GET FEATURE to the NAND chip and then increase the interface speed
to the highest mutually supported ONFI mode.

Is there some reason you need to encode this in the DT?

Jason

boris brezillon

unread,
Jan 8, 2014, 2:10:02 PM1/8/14
to
Hello Jason,

Le 08/01/2014 19:34, Jason Gunthorpe a �crit :
> On Wed, Jan 08, 2014 at 03:21:58PM +0100, Boris BREZILLON wrote:
>
>> +int of_get_nand_timings(struct device_node *np, struct nand_timings *timings)
>> +{
>> + memset(timings, 0, sizeof(*timings));
>> + of_property_read_u32(np, "tCLS-min", &timings->tCLS_min);
>> + of_property_read_u32(np, "tCLH-min", &timings->tCLH_min);
>> + of_property_read_u32(np, "tCS-min", &timings->tCS_min);
> [..]
>
> A while ago when discussing another controller it was pointed out
> these values are all auto-probable directly from the NAND via a ONFI
> defined GET FEATURE @0x01 query, and adding these timings to the DT
> was NAK'd..
>
> Basically you set the interface to the slowest ONFI timing mode, do
> the GET FEATURE to the NAND chip and then increase the interface speed
> to the highest mutually supported ONFI mode.

> Is there some reason you need to encode this in the DT?

What if the NAND does not support the ONFI interface (and this is
exactly the case for the NAND available on the cubietruck board:
H27UCG8T2ATR).

But I'm glag to hear about this ONFI feature :).
I'll add a function to retrieve these timings if the NAND support
the ONFI interface.

Best Regards,

Boris

Jason Gunthorpe

unread,
Jan 8, 2014, 2:20:01 PM1/8/14
to
On Wed, Jan 08, 2014 at 08:00:02PM +0100, boris brezillon wrote:
> Hello Jason,
>
> Le 08/01/2014 19:34, Jason Gunthorpe a ?crit :
> >On Wed, Jan 08, 2014 at 03:21:58PM +0100, Boris BREZILLON wrote:
> >
> >>+int of_get_nand_timings(struct device_node *np, struct nand_timings *timings)
> >>+{
> >>+ memset(timings, 0, sizeof(*timings));
> >>+ of_property_read_u32(np, "tCLS-min", &timings->tCLS_min);
> >>+ of_property_read_u32(np, "tCLH-min", &timings->tCLH_min);
> >>+ of_property_read_u32(np, "tCS-min", &timings->tCS_min);
> >[..]
> >
> >A while ago when discussing another controller it was pointed out
> >these values are all auto-probable directly from the NAND via a ONFI
> >defined GET FEATURE @0x01 query, and adding these timings to the DT
> >was NAK'd..
> >
> >Basically you set the interface to the slowest ONFI timing mode, do
> >the GET FEATURE to the NAND chip and then increase the interface speed
> >to the highest mutually supported ONFI mode.
>
> >Is there some reason you need to encode this in the DT?
>
> What if the NAND does not support the ONFI interface (and this is
> exactly the case for the NAND available on the cubietruck board:
> H27UCG8T2ATR).

Sounds like a good reason to me!

You might want to check if you can boil down the DT timings from the
huge list to just an ONFI mode number..

I'd echo Rob's comments, the property needs to include the units
in the name, and I strongly recommend picoseconds for these
values.

Also, you might want to check that the ONFI names for these parameters
are used, not a vendor specific name to try and avoid confusion.

boris brezillon

unread,
Jan 8, 2014, 2:30:02 PM1/8/14
to
Le 08/01/2014 15:22, Boris BREZILLON a �crit :
BTW, I recently was one of the sources of a merge issue in the clk
subsystem because of unsorted Makefile lines.
Shouldn't we try to sort this Makefile too ?

Arnd Bergmann

unread,
Jan 8, 2014, 4:40:02 PM1/8/14
to
On Wednesday 08 January 2014, Boris BREZILLON wrote:
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mtd/sunxi-nand.txt
> @@ -0,0 +1,71 @@
> +Allwinner NAND Flash Controller (NFC)
> +
> +Required properties:
> +- compatible : "allwinner,sun4i-nfc".
> +- reg : shall contain registers location and length for data and reg.
> +- interrupts : shall define the NFC interrupt.
> +- #address-cells: shall be set to 1. Encode the nand CS.
> +- #size-cells : shall be set to 0.
> +- clocks : shall reference NFC clocks.
> +- clock-names : NFC internal clock names. Shall contain :
> + * "ahb_clk" : AHB gating clock
> + * "sclk" : NFC clock
> +

One small request: Can we try to avoid the "NFC" name here? I think it's
too overloaded and people may confuse it with near-field communication,
which I'm sure will be supported in sunxi based devices at some point.

It doesn't hurt to also mention that the function block is called
nfc, but I think references to it are better named "nand", which is
less confusing.

Arnd

boris brezillon

unread,
Jan 9, 2014, 3:40:02 AM1/9/14
to
Sure, but the sunxi driver needs at least 19 of them...

>
> I'd echo Rob's comments, the property needs to include the units
> in the name, and I strongly recommend picoseconds for these
> values.

Agreed, picosecond is a more future-proof unit.

>
> Also, you might want to check that the ONFI names for these parameters
> are used, not a vendor specific name to try and avoid confusion.

I'll check it.

Thanks.

Best Regards,

Boris

boris brezillon

unread,
Jan 9, 2014, 3:40:02 AM1/9/14
to
On 08/01/2014 22:28, Arnd Bergmann wrote:
> On Wednesday 08 January 2014, Boris BREZILLON wrote:
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/mtd/sunxi-nand.txt
>> @@ -0,0 +1,71 @@
>> +Allwinner NAND Flash Controller (NFC)
>> +
>> +Required properties:
>> +- compatible : "allwinner,sun4i-nfc".
>> +- reg : shall contain registers location and length for data and reg.
>> +- interrupts : shall define the NFC interrupt.
>> +- #address-cells: shall be set to 1. Encode the nand CS.
>> +- #size-cells : shall be set to 0.
>> +- clocks : shall reference NFC clocks.
>> +- clock-names : NFC internal clock names. Shall contain :
>> + * "ahb_clk" : AHB gating clock
>> + * "sclk" : NFC clock
>> +
> One small request: Can we try to avoid the "NFC" name here? I think it's
> too overloaded and people may confuse it with near-field communication,
> which I'm sure will be supported in sunxi based devices at some point.
>
> It doesn't hurt to also mention that the function block is called
> nfc, but I think references to it are better named "nand", which is
> less confusing.

Sure, I'll remove references to the NFC acronym:
- change compatible string to "allwinner,sun4i-nand"
- avoid NFC references in the doc
- rename the driver into sunxi-nand.c (formerly sunxi_nfc.c)

Do you see any other references to this acronym ?


Best Regards,

Boris

Arnd Bergmann

unread,
Jan 9, 2014, 5:10:02 AM1/9/14
to
On Thursday 09 January 2014, boris brezillon wrote:
> Sure, I'll remove references to the NFC acronym:
> - change compatible string to "allwinner,sun4i-nand"
> - avoid NFC references in the doc
> - rename the driver into sunxi-nand.c (formerly sunxi_nfc.c)
>
> Do you see any other references to this acronym ?
>

Those are the important ones, thanks!

Regarding the binding, I would mention nfc once there to help people
that happen to look for that.

Jason Gunthorpe

unread,
Jan 9, 2014, 12:40:02 PM1/9/14
to
On Thu, Jan 09, 2014 at 09:36:18AM +0100, boris brezillon wrote:

> >You might want to check if you can boil down the DT timings from the
> >huge list to just an ONFI mode number..
>
> Sure, but the sunxi driver needs at least 19 of them...

So does mvebu's NAND driver..

What I ment was you could have a

onfi,nand-timing-mode = 0

in the DT. Each of the modes defines all ~19 parameters, higher modes
are faster.

Pick a mode value that fits all the parameters of the connected
non-ONFI flash.

This would be instead of defining each parameter
individually.. Provide some helpers to convert from a onfi mode number
to all the onfi defined timing parameters so that drivers can
configure the HW..

boris brezillon

unread,
Jan 11, 2014, 8:40:01 AM1/11/14
to
On 08/01/2014 15:21, Boris BREZILLON wrote:
> Hello,
>
> This series add the sunxi NFC support with up to 8 NAND chip connected.
> I'm still in the early stages drivers development and some key features are
> missing, but it's usable (I tested it on the cubietruck board).
>
> Here's what's missing:
> - HW ECC support
> - DMA support
> - HW randomization support
> - many more improvements
>
> This series depends on Emilio's patch series implementing mod0 clks
> (http://lists.infradead.org/pipermail/linux-arm-kernel/2013-July/185478.html)
> + an other patch not yet posted
> (http://git.elopez.com.ar/linux/commits/5b4eb3ac406b9c98965714d40e8dd6da943d1ab0)

During my reasearch regarding the HW ECC and HW randomizer of the Allwinner
NAND flash controller I found this document describing the Altera NAND
flash controller
(which is in turn based on a cadence IP):

http://www.altera.com/literature/hb/arria-v/av_54010.pdf

This really looks like the sunxi NAND flash controller (except for the
registers positions
and contents) ;-)

boris brezillon

unread,
Jan 13, 2014, 4:10:02 AM1/13/14
to
Hi Henrik,

On 11/01/2014 22:11, Henrik Nordström wrote:
> <bbrezillon> thanks for pointing out your documents
> <bbrezillon> I'm trying to get the NAND driver with HW ECC (and HW RND)
> without using DMA at all
>
> I tried many things but did not quite get the ECC reading command to
> return meaningful resuts. But should work somehow.
>
> <bbrezillon> do you have any other information I could use to do this ?
>
> Not really. There is no known code to look at using the nand controller
> without DMA. All allwinner code uses DMA even the boot ROM (BROM).
>
> <bbrezillon> For example, I wonder why there are 2 RAM sectors (the
> driver I found only make use of RAM0)
>
> I think it's used during DMA to fetch next sector while the previous one
> is transferred by DMA. But not sure.

Some feedback on my tests:

- I managed to get HW ECC working without any DMA transfer (using CMD = 01):
* I only tested the sequential ECC => ECC are stored between 2 data
blocks (1024 byte)
* Non sequential ECC should work if I store ECC bytes in the OOB area
too (I'll just have
to send RANDOM_OUT commands to move to the OOB area before sending
the ECC
cmd and another RANDOM_OUT to go back to the DATA area)

- The HW RND (randomizer) works too, I'll just have to figure out how
this could be
mainlined:
* using a simple dt property to tell the controller it should enable
the randomizer
* provide an interface (like the nand_ecc_ctrl struct ) for other to
add their own
randomizer implementation (this was requested:
https://lkml.org/lkml/2013/12/13/154)


The most complicated part is the boot0 partition.

Tell me if I'm wrong, but here's what I understood from your work (and
yuq's work too):

boot 0 part properties:
- uses sequential ECC
- uses 1024 bytes ECC blocks
- boot0 code is stored only on the first ECC block of each page (1024
bytes + ecc bytes)
- boot0 code is stored on the first 64 pages of the first block
- boot0 uses HW randomizer with a specific rnd seed (0x4a80)

It's not that complicated to read/write from/to boot0, but it's a bit
more to mainline this
implementation:
- the nand chip must use the same ECC algorithm and ECC layout on the
whole flash
(no partition specific config available)
- you cannot mark some part of pages as unused => the nand driver will
write the
whole page, not just the first ECC block (1024 bytes)

I thought about manually creating an mtd device that fullfils these
needs (in case we
encounter the "allwinner,nandn-boot" property on a nand@X node), but I'm
not sure
this is the right approach.

Any ideas ?


Best Regards,

Boris
>
> Regards
> Henrik

Henrik Nordström

unread,
Jan 13, 2014, 5:10:02 AM1/13/14
to
mån 2014-01-13 klockan 10:02 +0100 skrev boris brezillon:

> The most complicated part is the boot0 partition.

Not really. It's only a little different (sequential ECC, static
randomizer seed on every page).

> Tell me if I'm wrong, but here's what I understood from your work (and
> yuq's work too):
>
> boot 0 part properties:
> - uses sequential ECC

Yes

> - uses 1024 bytes ECC blocks

Seems to support a couple modes.

> - boot0 code is stored only on the first ECC block of each page (1024
> bytes + ecc bytes)

No, it reads a whole page at a time in sequental mode
(data,ecc,data,ecc,data,ecc,data,ecc...).

> - boot0 code is stored on the first 64 pages of the first block

boot0 is restricted in size by available memory size (24KB max).

iirc multiple blocks is tried until a valid one is found.

also I am pretty sure boot1 is stored using the same flash format, but
have not looked in detail at how boot0 reads boot1 as it's a detail of
boot0 software implementation and not needed when using u-boot.

> - boot0 uses HW randomizer with a specific rnd seed (0x4a80)

Yes.

> It's not that complicated to read/write from/to boot0, but it's a bit
> more to mainline this
> implementation:

Ah, yes. It's different.

But isn't there other SoCs having similar issue with NAND boot blocks?
Maybe some guidance can be found there?

Qiang Yu (yuq) selected to implement the sunxi boot area driver using a
custom character device in his attempt in writing a sunxi mtd driver.
That approach works, but can't comment on if it's the right approach or
not.

https://github.com/yuq/sunxi-nfc-mtd

> - the nand chip must use the same ECC algorithm and ECC layout on the
> whole flash
> (no partition specific config available)

iirc there is an interface for dynamically selecting ECC mode and other
parameers. Or maybe that's only u-boot mtd?

> - you cannot mark some part of pages as unused => the nand driver will
> write the
> whole page, not just the first ECC block (1024 bytes)

Not sure what you mean. Why would something be marked as unused? The
boot area is not a filesystem, it is a single linear blob (or two if
boot1 is in the same format, but mostly irrelevant).

> I thought about manually creating an mtd device that fullfils these
> needs (in case we
> encounter the "allwinner,nandn-boot" property on a nand@X node), but I'm
> not sure
> this is the right approach.

Would work, if you also can make sure the two do not stomp on each
others. Should be divided by NAND block range.

boris brezillon

unread,
Jan 15, 2014, 10:10:03 AM1/15/14
to
Hello Jason,

On 09/01/2014 18:35, Jason Gunthorpe wrote:
> On Thu, Jan 09, 2014 at 09:36:18AM +0100, boris brezillon wrote:
>
>>> You might want to check if you can boil down the DT timings from the
>>> huge list to just an ONFI mode number..
>> Sure, but the sunxi driver needs at least 19 of them...
> So does mvebu's NAND driver..
>
> What I ment was you could have a
>
> onfi,nand-timing-mode = 0
>
> in the DT. Each of the modes defines all ~19 parameters, higher modes
> are faster.
>
> Pick a mode value that fits all the parameters of the connected
> non-ONFI flash.
>
> This would be instead of defining each parameter
> individually.. Provide some helpers to convert from a onfi mode number
> to all the onfi defined timing parameters so that drivers can
> configure the HW..

Are you suggesting we should provide a function that converts these
modes into a nand_timings struct, or just use the timing modes and
let the NAND controller drivers configure its IP accordingly ?

I found the ONFI timing tables in this document:

www.*onfi*.org/~/media/*ONFI*/specs/*onfi*_3_1_spec.pdf‎ (chapter 4.16).

I suppose my nand_timings struct should use the names described
page 110-111 (at least if we decide to use nand_timings and not
nand_timing_modes), right ?

Best Regards,

Boris

boris brezillon

unread,
Jan 15, 2014, 12:30:02 PM1/15/14
to
After taking a closer look at this document, the only parameter
available in my
nand_timings struct that is not defined in the standard is tR_max (data
transfer
from cell to register).

And the ONFI standard defines 4 more timings:
- tCEA_max
- tCEH_min
- tFEAT_max
- tITC_max

Henrik Nordström

unread,
Jan 21, 2014, 1:20:03 PM1/21/14
to
lör 2014-01-18 klockan 05:46 -0800 skrev Boris BREZILLON:

> Do you know which mode are used (X ECC strength / 512 or 1024 bytes ?)
> and
> when they are are selected (does it depend on the connected NAND
> chip ?) ?

It seems to blindly try some modes until something usable is found.
Varying both chip address size and ECC layout.

Sorry I do not have the exact details on the ECC modes used. Only
analyzed nand controller command traces of A13 BROM trying to load
boot0. The trace can be found at
https://github.com/hno/Allwinner-Info/blob/master/NAND/boot0/A13-brom

>
>
> > - boot0 code is stored only on the first ECC block of each
> page (1024
> > bytes + ecc bytes)
>
> No, it reads a whole page at a time in sequental mode
> (data,ecc,data,ecc,data,ecc,data,ecc...).
>
>
> Are you sure ?
> This thread says that only the first 1024 bytes of data (+ 96 bytes of
> ECC) of each page are used:

yes I am sure. There was no page access commands between the sectors,
only linear read of data,ecc,data,ecc.

> I'm not a big fan of this approach, because the real media is an MTD
> (NAND) device,
> not a block device.

Hit implementation acks this by not providing a block device.


> iirc there is an interface for dynamically selecting ECC mode
> and other
> parameers. Or maybe that's only u-boot mtd?
>
>
> Haven't found anything authorizing per partiton ECC config, though
> this could be an
> enhancement of the MTD framework.

u-boot have user selectable ecc scheme for some boards. I.e. omap3 based
ones.

other boards select based on NAND size etc.

Henrik Nordström

unread,
Jan 21, 2014, 4:00:03 PM1/21/14
to
tis 2014-01-21 klockan 19:13 +0100 skrev Henrik Nordström:
> > Are you sure ?
> > This thread says that only the first 1024 bytes of data (+ 96 bytes of
> > ECC) of each page are used:
>
> yes I am sure. There was no page access commands between the sectors,
> only linear read of data,ecc,data,ecc.

Hmm.. but thinking back I am no longer so sure. Need to test this again
while verifying with logic probe what is going on.

Jason Gunthorpe

unread,
Jan 21, 2014, 6:00:03 PM1/21/14
to
On Wed, Jan 15, 2014 at 06:03:01PM +0100, boris brezillon wrote:

> >>Pick a mode value that fits all the parameters of the connected
> >>non-ONFI flash.
> >>
> >>This would be instead of defining each parameter
> >>individually.. Provide some helpers to convert from a onfi mode number
> >>to all the onfi defined timing parameters so that drivers can
> >>configure the HW..
> >
> >Are you suggesting we should provide a function that converts these
> >modes into a nand_timings struct, or just use the timing modes and
> >let the NAND controller drivers configure its IP accordingly ?

Either seems reasonable to me, but passing the ONFI mode directly from
the NAND core to the driver seems a little safer..

The NAND core can provide a helper function to xlate the mode number
to the timing struct to help drivers that need broken out timing.

> >I found the ONFI timing tables in this document:
> >
> >www.*onfi*.org/~/media/*ONFI*/specs/*onfi*_3_1_spec.pdf‎ (chapter 4.16).
> >
> >I suppose my nand_timings struct should use the names described
> >page 110-111 (at least if we decide to use nand_timings and not
> >nand_timing_modes), right ?

Yah, I think follow the standard. The standard has timing diagrams
that show what all these parameters actually are.

> After taking a closer look at this document, the only parameter
> available in my nand_timings struct that is not defined in the
> standard is tR_max (data transfer from cell to register).

Maybe it can be derived from the other parameters? The ONFI values
seemed pretty comprehensive to me.

I think the mvebu driver was similar, not all of the ONFI values were
used and some translation was needed.

Brian Norris

unread,
Jan 22, 2014, 8:50:01 PM1/22/14
to
+ Huang

Hi Boris,

On Wed, Jan 08, 2014 at 03:21:56PM +0100, Boris BREZILLON wrote:
> The Hynix nand flashes store their ECC requirements in byte 4 of its id
> (returned on READ ID command).
>
> Signed-off-by: Boris BREZILLON <b.bre...@overkiz.com>

I haven't verified yet (perhaps Huang can confirm?), but this may be
similar to a patch Huang submitted recently. In his case, we found that
this table is actually quite unreliable and is likely hard to maintain.

Why do you need this ECC information, for my reference?

Brian

Rob Herring

unread,
Jan 23, 2014, 10:30:01 AM1/23/14
to
On Sat, Jan 11, 2014 at 7:38 AM, boris brezillon
<b.bre...@overkiz.com> wrote:
> On 08/01/2014 15:21, Boris BREZILLON wrote:
>>
>> Hello,
>>
>> This series add the sunxi NFC support with up to 8 NAND chip connected.
>> I'm still in the early stages drivers development and some key features
>> are
>> missing, but it's usable (I tested it on the cubietruck board).
>>
>> Here's what's missing:
>> - HW ECC support
>> - DMA support
>> - HW randomization support
>> - many more improvements
>>
>> This series depends on Emilio's patch series implementing mod0 clks
>>
>> (http://lists.infradead.org/pipermail/linux-arm-kernel/2013-July/185478.html)
>> + an other patch not yet posted
>>
>> (http://git.elopez.com.ar/linux/commits/5b4eb3ac406b9c98965714d40e8dd6da943d1ab0)
>
>
> During my reasearch regarding the HW ECC and HW randomizer of the Allwinner
> NAND flash controller I found this document describing the Altera NAND flash
> controller
> (which is in turn based on a cadence IP):

Which may be similar to drivers/mtd/nand/denali.c as Cadence bought Denali?

Rob

boris brezillon

unread,
Jan 29, 2014, 5:30:02 AM1/29/14
to
Hello Rob,

On 23/01/2014 16:22, Rob Herring wrote:
> On Sat, Jan 11, 2014 at 7:38 AM, boris brezillon
> <b.bre...@overkiz.com> wrote:
>> On 08/01/2014 15:21, Boris BREZILLON wrote:
>>> Hello,
>>>
>>> This series add the sunxi NFC support with up to 8 NAND chip connected.
>>> I'm still in the early stages drivers development and some key features
>>> are
>>> missing, but it's usable (I tested it on the cubietruck board).
>>>
>>> Here's what's missing:
>>> - HW ECC support
>>> - DMA support
>>> - HW randomization support
>>> - many more improvements
>>>
>>> This series depends on Emilio's patch series implementing mod0 clks
>>>
>>> (http://lists.infradead.org/pipermail/linux-arm-kernel/2013-July/185478.html)
>>> + an other patch not yet posted
>>>
>>> (http://git.elopez.com.ar/linux/commits/5b4eb3ac406b9c98965714d40e8dd6da943d1ab0)
>>
>> During my reasearch regarding the HW ECC and HW randomizer of the Allwinner
>> NAND flash controller I found this document describing the Altera NAND flash
>> controller
>> (which is in turn based on a cadence IP):
> Which may be similar to drivers/mtd/nand/denali.c as Cadence bought Denali?
Actually I was wrong, the sunxi and the cadence IP have nothing in common.
This was pointed out by Henrik (see this thread :
https://groups.google.com/forum/#!searchin/linux-sunxi/nand/linux-sunxi/x69tFBi95Zk/bNyJlWWOV8oJ
<https://groups.google.com/forum/#%21searchin/linux-sunxi/nand/linux-sunxi/x69tFBi95Zk/bNyJlWWOV8oJ>).


Sorry for the false hopes.

Best Regards,

Boris

boris brezillon

unread,
Jan 29, 2014, 5:50:02 AM1/29/14
to
Hello Brian,

On 23/01/2014 02:49, Brian Norris wrote:
> + Huang
>
> Hi Boris,
>
> On Wed, Jan 08, 2014 at 03:21:56PM +0100, Boris BREZILLON wrote:
>> The Hynix nand flashes store their ECC requirements in byte 4 of its id
>> (returned on READ ID command).
>>
>> Signed-off-by: Boris BREZILLON <b.bre...@overkiz.com>
> I haven't verified yet (perhaps Huang can confirm?), but this may be
> similar to a patch Huang submitted recently. In his case, we found that
> this table is actually quite unreliable and is likely hard to maintain.

You mean these bytes are not reliable within the whole Hynix LP (Large Page)
NAND product line ?

>
> Why do you need this ECC information, for my reference?

Because the NAND flash available on the cubietruck board does not
support the
ONFI standard, and I thought this could be a option to retrieve the ECC
strength
requirements.

Anyway, I added a new helper function to retrieve ecc informations from
device
tree (I'll post it in the 2nd version of this series). We'll see if this
approach is
accepted...

Boris BREZILLON

unread,
Jan 29, 2014, 9:40:03 AM1/29/14
to
Hello,

This series adds support for the sunxi NAND Flash Controller (NFC).
This controller supports up to 8 NAND chip connected.

I'm still in the early stages drivers development and some key features are
missing, but it's usable (I tested it on the cubietruck board).

Here's what's missing:
- DMA support
- HW randomization support
- other improvements ?

This series depends on Emilio's patch series implementing mod0 clks
(http://lists.infradead.org/pipermail/linux-arm-kernel/2013-July/185478.html)
+ an other patch not yet posted
(http://git.elopez.com.ar/linux/commits/5b4eb3ac406b9c98965714d40e8dd6da943d1ab0)


Best Regards,

Boris

Changes since v1:
- add HW ECC support
- rework NAND timings retrieval (use ONFI timing mode instead of raw timings)
- add nand-ecc-level property to specify NAND ECC requirements from DT

Boris BREZILLON (14):
mtd: nand: retrieve ECC requirements from Hynix READ ID byte 4
of: mtd: add NAND ECC level requirements retrieval
of: mtd: add documentation for nand-ecc-level property
mtd: nand: define struct nand_timings
mtd: nand: add ONFI timing mode to nand_timings converter
of: mtd: add NAND timing mode retrieval support
of: mtd: add documentation for the ONFI NAND timing mode property
mtd: nand: add sunxi NAND flash controller support
mtd: nand: add sunxi NFC dt bindings doc
ARM: dt/sunxi: add NFC node to Allwinner A20 SoC
ARM: dt/sunxi: add NFC pinctrl pin definitions
ARM: sunxi/dt: enable NAND on cubietruck board
mtd: nand: add sunxi HW ECC support
ARM: sunxi/dt: enable HW ECC on cubietruck board

Documentation/devicetree/bindings/mtd/nand.txt | 8 +
.../devicetree/bindings/mtd/sunxi-nand.txt | 46 +
arch/arm/boot/dts/sun7i-a20-cubietruck.dts | 31 +
arch/arm/boot/dts/sun7i-a20.dtsi | 35 +
drivers/mtd/nand/Kconfig | 6 +
drivers/mtd/nand/Makefile | 3 +-
drivers/mtd/nand/nand_base.c | 37 +
drivers/mtd/nand/nand_timings.c | 248 +++++
drivers/mtd/nand/sunxi_nand.c | 997 ++++++++++++++++++++
drivers/of/of_mtd.c | 44 +
include/linux/mtd/nand.h | 53 ++
include/linux/of_mtd.h | 15 +
12 files changed, 1522 insertions(+), 1 deletion(-)
create mode 100644 Documentation/devicetree/bindings/mtd/sunxi-nand.txt
create mode 100644 drivers/mtd/nand/nand_timings.c
create mode 100644 drivers/mtd/nand/sunxi_nand.c

--
1.7.9.5

Boris BREZILLON

unread,
Jan 29, 2014, 9:40:03 AM1/29/14
to
Add a function to retrieve NAND timing mode (ONFI timing mode) from a given
DT node.

Signed-off-by: Boris BREZILLON <b.brezi...@gmail.com>
---
drivers/of/of_mtd.c | 19 +++++++++++++++++++
include/linux/of_mtd.h | 8 ++++++++
2 files changed, 27 insertions(+)

diff --git a/drivers/of/of_mtd.c b/drivers/of/of_mtd.c
index e8ced61..63155d4 100644
--- a/drivers/of/of_mtd.c
+++ b/drivers/of/of_mtd.c
@@ -108,3 +108,22 @@ bool of_get_nand_on_flash_bbt(struct device_node *np)
return of_property_read_bool(np, "nand-on-flash-bbt");
}
EXPORT_SYMBOL_GPL(of_get_nand_on_flash_bbt);
+
+/**
+ * of_get_nand_timings - Get nand timings for the given device_node
+ * @np: Pointer to the given device_node
+ *
+ * return 0 on success errno other wise
+ */
+int of_get_nand_onfi_timing_mode(struct device_node *np)
+{
+ int err;
+ u32 mode;
+
+ err = of_property_read_u32(np, "onfi,nand-timing-mode", &mode);
+ if (err)
+ return err;
+
+ return mode;
+}
+EXPORT_SYMBOL_GPL(of_get_nand_onfi_timing_mode);
diff --git a/include/linux/of_mtd.h b/include/linux/of_mtd.h
index 3bd8c3b..eb9fda6 100644
--- a/include/linux/of_mtd.h
+++ b/include/linux/of_mtd.h
@@ -9,6 +9,8 @@
#ifndef __LINUX_OF_MTD_H
#define __LINUX_OF_NET_H

+#include <linux/mtd/nand.h>
+
#ifdef CONFIG_OF_MTD

#include <linux/of.h>
@@ -16,6 +18,7 @@ int of_get_nand_ecc_mode(struct device_node *np);
int of_get_nand_ecc_level(struct device_node *np, u32 *strengh, u32 *blk_size);
int of_get_nand_bus_width(struct device_node *np);
bool of_get_nand_on_flash_bbt(struct device_node *np);
+int of_get_nand_onfi_timing_mode(struct device_node *np);

#else /* CONFIG_OF_MTD */

@@ -40,6 +43,11 @@ static inline bool of_get_nand_on_flash_bbt(struct device_node *np)
return false;
}

+static inline int of_get_nand_onfi_timing_mode(struct device_node *np)
+{
+ return -ENOSYS;
+}
+
#endif /* CONFIG_OF_MTD */

#endif /* __LINUX_OF_MTD_H */

Boris BREZILLON

unread,
Jan 29, 2014, 9:40:03 AM1/29/14
to
Define the NAND pinctrl configs.

Signed-off-by: Boris BREZILLON <b.brezi...@gmail.com>
---
arch/arm/boot/dts/sun7i-a20.dtsi | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)

diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi
index 3b47253..0f6e002 100644
--- a/arch/arm/boot/dts/sun7i-a20.dtsi
+++ b/arch/arm/boot/dts/sun7i-a20.dtsi
@@ -389,6 +389,30 @@
allwinner,drive = <0>;
allwinner,pull = <0>;
};
+
+ 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_rb0_pins_a: nand_rb@0 {
+ allwinner,pins = "PC6";
+ allwinner,function = "nand0";
+ allwinner,drive = <0>;
+ allwinner,pull = <0>;
+ };
};

timer@01c20c00 {

Boris BREZILLON

unread,
Jan 29, 2014, 9:50:02 AM1/29/14
to
Add documentation for the ONFI NAND timing mode property.

Signed-off-by: Boris BREZILLON <b.brezi...@gmail.com>
---
Documentation/devicetree/bindings/mtd/nand.txt | 5 +++++
1 file changed, 5 insertions(+)

diff --git a/Documentation/devicetree/bindings/mtd/nand.txt b/Documentation/devicetree/bindings/mtd/nand.txt
index 0c962296..75e46f3 100644
--- a/Documentation/devicetree/bindings/mtd/nand.txt
+++ b/Documentation/devicetree/bindings/mtd/nand.txt
@@ -8,3 +8,8 @@
E.g. : nand-ecc-level = <4 512>; /* 4 bits / 512 bytes */
- nand-bus-width : 8 or 16 bus width if not present 8
- nand-on-flash-bbt: boolean to enable on flash bbt option if not present false
+- onfi,nand-timing-mode: an integer encoding the ONFI timing mode of the NAND
+ chip. This is only used when the chip does not support the ONFI standard.
+ Choose the closest mode fulfilling the NAND chip timings.
+ For a full description of the different timing modes see this document:
+ www.onfi.org/~/media/ONFI/specs/onfi_3_1_spec.pdf

Boris BREZILLON

unread,
Jan 29, 2014, 10:00:01 AM1/29/14
to
nand-ecc-level property statically defines NAND chip's ECC requirements.

Signed-off-by: Boris BREZILLON <b.brezi...@gmail.com>
---
Documentation/devicetree/bindings/mtd/nand.txt | 3 +++
1 file changed, 3 insertions(+)

diff --git a/Documentation/devicetree/bindings/mtd/nand.txt b/Documentation/devicetree/bindings/mtd/nand.txt
index 03855c8..0c962296 100644
--- a/Documentation/devicetree/bindings/mtd/nand.txt
+++ b/Documentation/devicetree/bindings/mtd/nand.txt
@@ -3,5 +3,8 @@
- nand-ecc-mode : String, operation mode of the NAND ecc mode.
Supported values are: "none", "soft", "hw", "hw_syndrome", "hw_oob_first",
"soft_bch".
+- nand-ecc-level : Two cells property defining the ECC level requirements.
+ The first cell represent the strength and the second cell the ECC block size.
+ E.g. : nand-ecc-level = <4 512>; /* 4 bits / 512 bytes */
- nand-bus-width : 8 or 16 bus width if not present 8
- nand-on-flash-bbt: boolean to enable on flash bbt option if not present false

Michal Suchanek

unread,
Jan 29, 2014, 10:20:02 AM1/29/14
to
Maybe if varying parameters on one MTD device is not acceptable you
could export parts of the flash as different MTD devices each with its
own parameters. Since the boot0 part is fixed size this should not
really be an issue. Existing MTD drivers that share hardware with
other devices exist - eg. the MTD driver which exports part of RAM as
MDT device.

I wonder if it would be good idea to make it possible to use the NAND
only for storage without a boot0 area. If this is selected by a DT
parameter as suggested changing the parameter will probably make the
NAND unreadable.

Thanks

Michal

>
>
> Best Regards,
>
> Boris
>
>>
>> Regards
>> Henrik
>>
>
> --
> 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...@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.

boris brezillon dev

unread,
Jan 29, 2014, 10:50:01 AM1/29/14
to
Hello Michal,
I considered this option (exposing 2 mtd devices which use the
same nand chip: one for the boot partition and the other one
for the remaining space).
I might give it a try.

For the moment I'm trying to use standard partitions and then
attach one of these partitions as a sunxi-nand-boot-interface.
Something similar to what UBI is doing when attaching to an MTD
device.

This way we can use the NAND as a standard MTD dev and when one
partition is attached as a sunxi-nand-boot-interface you can access
the boot0 partition using a char dev (/dev/snbi0 ?).
The sunxi-nand-boot-interface will provide the appropriate abstraction
to hide the specific boot0 layout...

What do you think ?

>
> I wonder if it would be good idea to make it possible to use the NAND
> only for storage without a boot0 area. If this is selected by a DT
> parameter as suggested changing the parameter will probably make the
> NAND unreadable.
Actually the NAND controller supports up to 8 chips. I guess only the
first one can be used as a boot device.
Reserving space for the boot partition on all of these chips is kind of
useless.
Moreover, we can't tell if the user wants to boot from the NAND or
from another storage (MMC for example), in this case we don't need
to expose the boot0 partition.


Best Regards,

Boris

Michal Suchanek

unread,
Jan 29, 2014, 11:10:01 AM1/29/14
to
If it works with MTD, sure.

The problem the two devices avoid is that with uniform parameters
across MTD device the boot0 partition is invalid.

>
>
>>
>> I wonder if it would be good idea to make it possible to use the NAND
>> only for storage without a boot0 area. If this is selected by a DT
>> parameter as suggested changing the parameter will probably make the
>> NAND unreadable.
>
> Actually the NAND controller supports up to 8 chips. I guess only the
> first one can be used as a boot device.
> Reserving space for the boot partition on all of these chips is kind of
> useless.

This actually depends on the BROM.

I did not read the BROM code so I don't know what it does.

> Moreover, we can't tell if the user wants to boot from the NAND or
> from another storage (MMC for example), in this case we don't need
> to expose the boot0 partition.

It's possible to use the NAND only for storage, sure.

However, a NAND on which the boo0 area is reserved would be unreadable
without reserving boot0 area in the driver, right?

The best we can tell is if user specified to reserve the area in the
DT. It might be possible to verify the boot0 area the same way BROM
does when booting from it. This might be nice option when you don't
know what you have on the chip and want to read it but most of the
time you will want to enforce bootable or non-bootable format when
writing the NAND.

Thanks

Michal

boris brezillon dev

unread,
Jan 29, 2014, 12:00:02 PM1/29/14
to
Using partitions we would still have X char devices (X = number of MTD
partitions). But indeed, we need to provide a way to configure ECC and
randomizer specifically on each partition.

>
>>
>>> I wonder if it would be good idea to make it possible to use the NAND
>>> only for storage without a boot0 area. If this is selected by a DT
>>> parameter as suggested changing the parameter will probably make the
>>> NAND unreadable.
>> Actually the NAND controller supports up to 8 chips. I guess only the
>> first one can be used as a boot device.
>> Reserving space for the boot partition on all of these chips is kind of
>> useless.
> This actually depends on the BROM.
>
> I did not read the BROM code so I don't know what it does.
>
>> Moreover, we can't tell if the user wants to boot from the NAND or
>> from another storage (MMC for example), in this case we don't need
>> to expose the boot0 partition.
> It's possible to use the NAND only for storage, sure.
>
> However, a NAND on which the boo0 area is reserved would be unreadable
> without reserving boot0 area in the driver, right?

Not exactly: the NAND would still be readable but the blocks reserved for
boot0 (boot0 partition) won't be read correctly (ECC layout differs).
If you define a partition boot0 with the appropriate size and never access
it, the other partitions defined on the same NAND chip will work perfectly.

To solve this ECC config issue we might need to provide a way to configure
the ECC engine per partition and not on the whole NAND chip.

Moreover, IRC, BROM only uses 1K on each page of a given block to store
boot0 code and data.

And eventually the randomizer config (random seed) is specific for this
partition too.
Note that the current driver does not support randomization at all.
I'm still working on providing a generic framework to support HW and SW
randomization (in the same way HW and SW ECC are supported).

For all the reasons exposed above we need a specific handling for the boot0
partition. But I'd like to keep the NAND Flash controller driver as
simple and
as generic as possible.
I'd prefer adding a new driver for the sunxi boot0 partition handling than
adding this code in the sunxi_nand driver.
Atfer all, this boot partition has nothing to do with NAND, this is just
a specific
format for the sunxi BROM to load code from NAND to RAM, right ?

Ezequiel Garcia

unread,
Jan 29, 2014, 1:00:03 PM1/29/14
to
On Wed, Jan 29, 2014 at 03:34:13PM +0100, Boris BREZILLON wrote:
> nand-ecc-level property statically defines NAND chip's ECC requirements.
>
> Signed-off-by: Boris BREZILLON <b.brezi...@gmail.com>
> ---
> Documentation/devicetree/bindings/mtd/nand.txt | 3 +++
> 1 file changed, 3 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/mtd/nand.txt b/Documentation/devicetree/bindings/mtd/nand.txt
> index 03855c8..0c962296 100644
> --- a/Documentation/devicetree/bindings/mtd/nand.txt
> +++ b/Documentation/devicetree/bindings/mtd/nand.txt
> @@ -3,5 +3,8 @@
> - nand-ecc-mode : String, operation mode of the NAND ecc mode.
> Supported values are: "none", "soft", "hw", "hw_syndrome", "hw_oob_first",
> "soft_bch".
> +- nand-ecc-level : Two cells property defining the ECC level requirements.
> + The first cell represent the strength and the second cell the ECC block size.
> + E.g. : nand-ecc-level = <4 512>; /* 4 bits / 512 bytes */
> - nand-bus-width : 8 or 16 bus width if not present 8
> - nand-on-flash-bbt: boolean to enable on flash bbt option if not present false

Hm.. when was this proposal agreed? It seems I've missed the
discussion...

FWIW, we've already proposed an equivalent one, but it received no
feedback from the devicetree maintainers:

http://comments.gmane.org/gmane.linux.drivers.devicetree/58764

Maybe we can discuss about it now?

nand-ecc-strength : integer ECC required strength.
nand-ecc-size : integer step size associated to the ECC strength.

vs.

nand-ecc-level : Two cells property defining the ECC level requirements.
The first cell represent the strength and the second cell the ECC block size.
E.g. : nand-ecc-level = <4 512>; /* 4 bits / 512 bytes */

It's really the same proposal but with a different format, right?
IMHO, the former is more human-readable, but other than that I see no
difference.

Brian? DT-guys?
--
Ezequiel García, Free Electrons
Embedded Linux, Kernel and Android Engineering
http://free-electrons.com

Boris BREZILLON

unread,
Jan 29, 2014, 1:40:03 PM1/29/14
to
Hello Ezequiel

Le 29/01/2014 18:53, Ezequiel Garcia a écrit :
> On Wed, Jan 29, 2014 at 03:34:13PM +0100, Boris BREZILLON wrote:
>> nand-ecc-level property statically defines NAND chip's ECC requirements.
>>
>> Signed-off-by: Boris BREZILLON <b.brezi...@gmail.com>
>> ---
>> Documentation/devicetree/bindings/mtd/nand.txt | 3 +++
>> 1 file changed, 3 insertions(+)
>>
>> diff --git a/Documentation/devicetree/bindings/mtd/nand.txt b/Documentation/devicetree/bindings/mtd/nand.txt
>> index 03855c8..0c962296 100644
>> --- a/Documentation/devicetree/bindings/mtd/nand.txt
>> +++ b/Documentation/devicetree/bindings/mtd/nand.txt
>> @@ -3,5 +3,8 @@
>> - nand-ecc-mode : String, operation mode of the NAND ecc mode.
>> Supported values are: "none", "soft", "hw", "hw_syndrome", "hw_oob_first",
>> "soft_bch".
>> +- nand-ecc-level : Two cells property defining the ECC level requirements.
>> + The first cell represent the strength and the second cell the ECC block size.
>> + E.g. : nand-ecc-level = <4 512>; /* 4 bits / 512 bytes */
>> - nand-bus-width : 8 or 16 bus width if not present 8
>> - nand-on-flash-bbt: boolean to enable on flash bbt option if not present false
> Hm.. when was this proposal agreed?
Never, this is a proposal based on my needs, and this was not present in the
1st version of this series :-).
> It seems I've missed the
> discussion...
>
> FWIW, we've already proposed an equivalent one, but it received no
> feedback from the devicetree maintainers:
>
> http://comments.gmane.org/gmane.linux.drivers.devicetree/58764
>
> Maybe we can discuss about it now?
>
> nand-ecc-strength : integer ECC required strength.
> nand-ecc-size : integer step size associated to the ECC strength.
>
> vs.
>
> nand-ecc-level : Two cells property defining the ECC level requirements.
> The first cell represent the strength and the second cell the ECC block size.
> E.g. : nand-ecc-level = <4 512>; /* 4 bits / 512 bytes */
>
> It's really the same proposal but with a different format, right?

Yes it is.

> IMHO, the former is more human-readable, but other than that I see no
> difference.

As I already said to Pekon, I won't complain if my proposal is not chosen,
as long as there is a proper way to define these ECC requirements ;-).

Best Regards,

Boris

>
> Brian? DT-guys?

Boris BREZILLON

unread,
Jan 30, 2014, 8:50:02 AM1/30/14
to
Add documentation for the ONFI NAND timing mode property.

Signed-off-by: Boris BREZILLON <b.brezi...@gmail.com>
---
Changes since v2:
- fix description of the nand-timing-mode property: the mode property is
a mask containing all supported modes, each mode is encoded as a bit
position

Documentation/devicetree/bindings/mtd/nand.txt | 7 +++++++
1 file changed, 7 insertions(+)

diff --git a/Documentation/devicetree/bindings/mtd/nand.txt b/Documentation/devicetree/bindings/mtd/nand.txt
index 0c962296..60c7112 100644
--- a/Documentation/devicetree/bindings/mtd/nand.txt
+++ b/Documentation/devicetree/bindings/mtd/nand.txt
@@ -8,3 +8,10 @@
E.g. : nand-ecc-level = <4 512>; /* 4 bits / 512 bytes */
- nand-bus-width : 8 or 16 bus width if not present 8
- nand-on-flash-bbt: boolean to enable on flash bbt option if not present false
+- onfi,nand-timing-mode: an integer encoding the supported ONFI timing modes of
+ the NAND chip. Each supported mode is represented as a bit position (i.e. :
+ mode 0 and 1 => (1 << 0) | (1 << 1) = 0x3).
+ This is only used when the chip does not support the ONFI standard.
+ The last bit set represent the closest mode fulfilling the NAND chip timings.
+ For a full description of the different timing modes see this document:
+ www.onfi.org/~/media/ONFI/specs/onfi_3_1_spec.pdf

Boris BREZILLON

unread,
Jan 30, 2014, 8:50:02 AM1/30/14
to
Add support for the sunxi NAND Flash Controller (NFC).

Signed-off-by: Boris BREZILLON <b.brezi...@gmail.com>
---
Hello,

This version fixes a bug in the R/B GPIO config block.
The timing config order is now respected, but I'll wait for Jason work
regarding timing config in NAND core code before posting the 3rd version
of this series.

Best Regards,

Boris

Changes since v2:
- fix R/B GPIO retrieval/config bug
- fix timings configuration order (set mode 0 -> scan -> set best supported
mode)

drivers/mtd/nand/Kconfig | 6 +
drivers/mtd/nand/Makefile | 1 +
drivers/mtd/nand/sunxi_nand.c | 758 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 765 insertions(+)
create mode 100644 drivers/mtd/nand/sunxi_nand.c

diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 93ae6a6..784dd42 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -510,4 +510,10 @@ config MTD_NAND_XWAY
Enables support for NAND Flash chips on Lantiq XWAY SoCs. NAND is attached
to the External Bus Unit (EBU).

+config MTD_NAND_SUNXI
+ tristate "Support for NAND on Allwinner SoCs"
+ depends on ARCH_SUNXI
+ help
+ Enables support for NAND Flash chips on Allwinner SoCs.
+
endif # MTD_NAND
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index bbea7a6..e3b4a34 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -49,5 +49,6 @@ obj-$(CONFIG_MTD_NAND_JZ4740) += jz4740_nand.o
obj-$(CONFIG_MTD_NAND_GPMI_NAND) += gpmi-nand/
obj-$(CONFIG_MTD_NAND_XWAY) += xway_nand.o
obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH) += bcm47xxnflash/
+obj-$(CONFIG_MTD_NAND_SUNXI) += sunxi_nand.o

nand-objs := nand_base.o nand_bbt.o
diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c
new file mode 100644
index 0000000..1014b2a
--- /dev/null
+++ b/drivers/mtd/nand/sunxi_nand.c
@@ -0,0 +1,758 @@
+/*
+ * Copyright (C) 2013 Boris BREZILLON <b.brezi...@gmail.com>
+ *
+ * Derived from:
+ * https://github.com/yuq/sunxi-nfc-mtd
+ * Copyright (C) 2013 Qiang Yu <yuq...@gmail.com>
+ *
+ * https://github.com/hno/Allwinner-Info
+ * Copyright (C) 2013 Henrik Nordström <Henrik Nordström>
+ *
+ * Copyright (C) 2013 Dmitriy B. <rzk...@gmail.com>
+ * Copyright (C) 2013 Sergey Lapin <sla...@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.
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/of_mtd.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+
+#define NFC_REG_CTL 0x0000
+#define NFC_REG_ST 0x0004
+#define NFC_REG_INT 0x0008
+#define NFC_REG_TIMING_CTL 0x000C
+#define NFC_REG_TIMING_CFG 0x0010
+#define NFC_REG_ADDR_LOW 0x0014
+#define NFC_REG_ADDR_HIGH 0x0018
+#define NFC_REG_SECTOR_NUM 0x001C
+#define NFC_REG_CNT 0x0020
+#define NFC_REG_CMD 0x0024
+#define NFC_REG_RCMD_SET 0x0028
+#define NFC_REG_WCMD_SET 0x002C
+#define NFC_REG_IO_DATA 0x0030
+#define NFC_REG_ECC_CTL 0x0034
+#define NFC_REG_ECC_ST 0x0038
+#define NFC_REG_DEBUG 0x003C
+#define NFC_REG_ECC_CNT0 0x0040
+#define NFC_REG_ECC_CNT1 0x0044
+#define NFC_REG_ECC_CNT2 0x0048
+#define NFC_REG_ECC_CNT3 0x004c
+#define NFC_REG_USER_DATA_BASE 0x0050
+#define NFC_REG_SPARE_AREA 0x00A0
+#define NFC_RAM0_BASE 0x0400
+#define NFC_RAM1_BASE 0x0800
+
+/*define bit use in NFC_CTL*/
+#define NFC_EN (1 << 0)
+#define NFC_RESET (1 << 1)
+#define NFC_BUS_WIDYH (1 << 2)
+#define NFC_RB_SEL (1 << 3)
+#define NFC_CE_SEL (7 << 24)
+#define NFC_CE_CTL (1 << 6)
+#define NFC_CE_CTL1 (1 << 7)
+#define NFC_PAGE_SIZE (0xf << 8)
+#define NFC_SAM (1 << 12)
+#define NFC_RAM_METHOD (1 << 14)
+#define NFC_DEBUG_CTL (1 << 31)
+
+/*define bit use in NFC_ST*/
+#define NFC_RB_B2R (1 << 0)
+#define NFC_CMD_INT_FLAG (1 << 1)
+#define NFC_DMA_INT_FLAG (1 << 2)
+#define NFC_CMD_FIFO_STATUS (1 << 3)
+#define NFC_STA (1 << 4)
+#define NFC_NATCH_INT_FLAG (1 << 5)
+#define NFC_RB_STATE0 (1 << 8)
+#define NFC_RB_STATE1 (1 << 9)
+#define NFC_RB_STATE2 (1 << 10)
+#define NFC_RB_STATE3 (1 << 11)
+
+/*define bit use in NFC_INT*/
+#define NFC_B2R_INT_ENABLE (1 << 0)
+#define NFC_CMD_INT_ENABLE (1 << 1)
+#define NFC_DMA_INT_ENABLE (1 << 2)
+#define NFC_INT_MASK (NFC_B2R_INT_ENABLE | \
+ NFC_CMD_INT_ENABLE | \
+ NFC_DMA_INT_ENABLE)
+
+
+/*define bit use in NFC_CMD*/
+#define NFC_CMD_LOW_BYTE (0xff << 0)
+#define NFC_CMD_HIGH_BYTE (0xff << 8)
+#define NFC_ADR_NUM (0x7 << 16)
+#define NFC_SEND_ADR (1 << 19)
+#define NFC_ACCESS_DIR (1 << 20)
+#define NFC_DATA_TRANS (1 << 21)
+#define NFC_SEND_CMD1 (1 << 22)
+#define NFC_WAIT_FLAG (1 << 23)
+#define NFC_SEND_CMD2 (1 << 24)
+#define NFC_SEQ (1 << 25)
+#define NFC_DATA_SWAP_METHOD (1 << 26)
+#define NFC_ROW_AUTO_INC (1 << 27)
+#define NFC_SEND_CMD3 (1 << 28)
+#define NFC_SEND_CMD4 (1 << 29)
+#define NFC_CMD_TYPE (3 << 30)
+
+/* define bit use in NFC_RCMD_SET*/
+#define NFC_READ_CMD (0xff << 0)
+#define NFC_RANDOM_READ_CMD0 (0xff << 8)
+#define NFC_RANDOM_READ_CMD1 (0xff << 16)
+
+/*define bit use in NFC_WCMD_SET*/
+#define NFC_PROGRAM_CMD (0xff << 0)
+#define NFC_RANDOM_WRITE_CMD (0xff << 8)
+#define NFC_READ_CMD0 (0xff << 16)
+#define NFC_READ_CMD1 (0xff << 24)
+
+/*define bit use in NFC_ECC_CTL*/
+#define NFC_ECC_EN (1 << 0)
+#define NFC_ECC_PIPELINE (1 << 3)
+#define NFC_ECC_EXCEPTION (1 << 4)
+#define NFC_ECC_BLOCK_SIZE (1 << 5)
+#define NFC_RANDOM_EN (1 << 9)
+#define NFC_RANDOM_DIRECTION (1 << 10)
+#define NFC_ECC_MODE_SHIFT 12
+#define NFC_ECC_MODE (0xf << NFC_ECC_MODE_SHIFT)
+#define NFC_RANDOM_SEED (0x7fff << 16)
+
+
+
+enum sunxi_nand_rb_type {
+ RB_NONE,
+ RB_NATIVE,
+ RB_GPIO,
+};
+
+struct sunxi_nand_rb {
+ enum sunxi_nand_rb_type type;
+ union {
+ int gpio;
+ int nativeid;
+ } info;
+};
+
+struct sunxi_nand_chip_sel {
+ u8 cs;
+ struct sunxi_nand_rb rb;
+};
+
+#define DEFAULT_NAME_FORMAT "nand@%d"
+#define MAX_NAME_SIZE (sizeof("nand@") + 2)
+
+struct sunxi_nand_chip {
+ struct list_head node;
+ struct nand_chip nand;
+ struct mtd_info mtd;
+ char default_name[MAX_NAME_SIZE];
+ 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 mtd_info *mtd)
+{
+ return container_of(mtd, struct sunxi_nand_chip, mtd);
+}
+
+struct sunxi_nfc {
+ struct nand_hw_control controller;
+ void __iomem *regs;
+ int irq;
+ struct clk *ahb_clk;
+ struct clk *sclk;
+ unsigned long assigned_cs;
+ unsigned long clk_rate;
+ struct list_head chips;
+ struct completion complete;
+};
+
+static inline struct sunxi_nfc *to_sunxi_nfc(struct nand_hw_control *ctrl)
+{
+ return container_of(ctrl, struct sunxi_nfc, controller);
+}
+
+static irqreturn_t sunxi_nfc_interrupt(int irq, void *dev_id)
+{
+ struct sunxi_nfc *nfc = dev_id;
+ u32 st = readl(nfc->regs + NFC_REG_ST);
+ u32 ien = readl(nfc->regs + NFC_REG_INT);
+
+ if (!(ien & st))
+ return IRQ_NONE;
+
+ if ((ien & st) == ien)
+ complete(&nfc->complete);
+
+ writel(st & NFC_INT_MASK, nfc->regs + NFC_REG_ST);
+ writel(~st & ien & NFC_INT_MASK, nfc->regs + NFC_REG_INT);
+
+ return IRQ_HANDLED;
+}
+
+static int sunxi_nfc_wait_int(struct sunxi_nfc *nfc, u32 flags,
+ unsigned int timeout_ms)
+{
+ init_completion(&nfc->complete);
+
+ writel(flags, nfc->regs + NFC_REG_INT);
+ if (!timeout_ms)
+ wait_for_completion(&nfc->complete);
+ else if (!wait_for_completion_timeout(&nfc->complete,
+ msecs_to_jiffies(timeout_ms)))
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static int sunxi_nfc_dev_ready(struct mtd_info *mtd)
+{
+ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(mtd);
+ 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 + NFC_REG_ST) &
+ (NFC_RB_STATE0 << rb->info.nativeid));
+ if (ret)
+ break;
+
+ sunxi_nfc_wait_int(nfc, NFC_RB_B2R, timeo);
+ ret = !!(readl(nfc->regs + NFC_REG_ST) &
+ (NFC_RB_STATE0 << rb->info.nativeid));
+ break;
+ case RB_GPIO:
+ ret = gpio_get_value(rb->info.gpio);
+ break;
+ case RB_NONE:
+ default:
+ ret = 0;
+ dev_err(&mtd->dev, "cannot check R/B NAND status!");
+ break;
+ }
+
+ return ret;
+}
+
+static void sunxi_nfc_select_chip(struct mtd_info *mtd, int chip)
+{
+ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(mtd);
+ struct nand_chip *nand = &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 + NFC_REG_CTL) &
+ ~(NFC_CE_SEL | NFC_RB_SEL | NFC_EN);
+
+ if (chip >= 0) {
+ sel = &sunxi_nand->sels[chip];
+
+ ctl |= (sel->cs << 24) | NFC_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 |= (sel->rb.info.nativeid << 3);
+ }
+
+ writel(mtd->writesize, nfc->regs + NFC_REG_SPARE_AREA);
+
+ if (nfc->clk_rate != sunxi_nand->clk_rate) {
+ clk_set_rate(nfc->sclk, sunxi_nand->clk_rate);
+ nfc->clk_rate = sunxi_nand->clk_rate;
+ }
+ }
+
+ writel(ctl, nfc->regs + NFC_REG_CTL);
+
+ sunxi_nand->selected = chip;
+}
+
+static void sunxi_nfc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(mtd);
+ 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;
+
+ while ((readl(nfc->regs + NFC_REG_ST) & NFC_CMD_FIFO_STATUS))
+ ;
+ writel(cnt, nfc->regs + NFC_REG_CNT);
+ tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD;
+ writel(tmp, nfc->regs + NFC_REG_CMD);
+ sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
+ if (buf)
+ memcpy_fromio(buf + offs, nfc->regs + NFC_RAM0_BASE,
+ cnt);
+ offs += cnt;
+ }
+}
+
+static void sunxi_nfc_write_buf(struct mtd_info *mtd, const uint8_t *buf,
+ int len)
+{
+ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(mtd);
+ 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;
+
+ while ((readl(nfc->regs + NFC_REG_ST) & NFC_CMD_FIFO_STATUS))
+ ;
+ writel(cnt, nfc->regs + NFC_REG_CNT);
+ memcpy_toio(nfc->regs + NFC_RAM0_BASE, buf + offs, cnt);
+ tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD |
+ NFC_ACCESS_DIR;
+ writel(tmp, nfc->regs + NFC_REG_CMD);
+ sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
+ offs += cnt;
+ }
+}
+
+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 sunxi_nand_chip *sunxi_nand = to_sunxi_nand(mtd);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
+ u32 tmp;
+
+ while ((readl(nfc->regs + NFC_REG_ST) & NFC_CMD_FIFO_STATUS))
+ ;
+
+ if (ctrl & NAND_CTRL_CHANGE) {
+ tmp = readl(nfc->regs + NFC_REG_CTL);
+ if (ctrl & NAND_NCE)
+ tmp |= NFC_CE_CTL;
+ else
+ tmp &= ~NFC_CE_CTL;
+ writel(tmp, nfc->regs + NFC_REG_CTL);
+ }
+
+ if (dat == NAND_CMD_NONE)
+ return;
+
+ if (ctrl & NAND_CLE) {
+ writel(NFC_SEND_CMD1 | dat, nfc->regs + NFC_REG_CMD);
+ } else {
+ writel(dat, nfc->regs + NFC_REG_ADDR_LOW);
+ writel(NFC_SEND_ADR, nfc->regs + NFC_REG_CMD);
+ }
+
+ sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
+}
+
+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,
+ struct device_node *np)
+{
+ const struct nand_sdr_timings *timings;
+ int ret;
+
+ ret = onfi_get_async_timing_mode(&chip->nand);
+ if (ret == ONFI_TIMING_MODE_UNKNOWN) {
+ ret = of_get_nand_onfi_timing_mode(np);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = fls(ret);
+ if (!ret)
+ return -EINVAL;
+
+ timings = onfi_async_timing_mode_to_sdr_timings(ret - 1);
+ if (IS_ERR(timings))
+ return PTR_ERR(timings);
+
+ return sunxi_nand_chip_set_timings(chip, timings);
+}
+
+static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
+ struct device_node *np)
+{
+ const struct nand_sdr_timings *timings;
+ struct sunxi_nand_chip *chip;
+ struct mtd_part_parser_data ppdata;
+ struct mtd_info *mtd;
+ struct nand_chip *nand;
+ u32 strength;
+ u32 blk_size;
+ int nsels;
+ int ret;
+ int i;
+ u32 tmp;
+
+ if (!of_get_property(np, "reg", &nsels))
+ return -EINVAL;
+
+ nsels /= sizeof(u32);
+ if (!nsels)
+ return -EINVAL;
+
+ chip = devm_kzalloc(dev,
+ 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 = of_property_read_u32_index(np, "reg", i, &tmp);
+ if (ret)
+ return ret;
+
+ if (tmp > 7)
+ return -EINVAL;
+
+ if (test_and_set_bit(tmp, &nfc->assigned_cs))
+ return -EINVAL;
+
+ chip->sels[i].cs = tmp;
+
+ if (!of_property_read_u32_index(np, "allwinner,rb", i, &tmp) &&
+ tmp < 2) {
+ chip->sels[i].rb.type = RB_NATIVE;
+ chip->sels[i].rb.info.nativeid = tmp;
+ } else {
+ ret = of_get_named_gpio(np, "rb-gpios", i);
+ if (ret >= 0) {
+ tmp = ret;
+ chip->sels[i].rb.type = RB_GPIO;
+ chip->sels[i].rb.info.gpio = tmp;
+ ret = devm_gpio_request(dev, tmp, "nand-rb");
+ if (ret)
+ return ret;
+
+ ret = gpio_direction_input(tmp);
+ if (ret)
+ return ret;
+ } 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->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;
+
+ nand->ecc.mode = of_get_nand_ecc_mode(np);
+ if (of_get_nand_on_flash_bbt(np))
+ nand->bbt_options |= NAND_BBT_USE_FLASH;
+
+ mtd = &chip->mtd;
+ mtd->priv = nand;
+ mtd->owner = THIS_MODULE;
+
+ ret = nand_scan_ident(mtd, nsels, NULL);
+ if (ret)
+ return ret;
+
+ ret = sunxi_nand_chip_init_timings(chip, np);
+ if (ret)
+ return ret;
+
+ if (nand->ecc.mode == NAND_ECC_SOFT_BCH) {
+ if (!of_get_nand_ecc_level(np, &strength, &blk_size)) {
+ nand->ecc_step_ds = blk_size;
+ nand->ecc_strength_ds = strength;
+ }
+
+ nand->ecc.size = nand->ecc_step_ds;
+ nand->ecc.bytes = (((nand->ecc_strength_ds *
+ fls(8 * nand->ecc_step_ds)) + 7) / 8);
+ }
+
+ ret = nand_scan_tail(mtd);
+ if (ret)
+ return ret;
+
+ if (of_property_read_string(np, "nand-name", &mtd->name)) {
+ snprintf(chip->default_name, MAX_NAME_SIZE,
+ DEFAULT_NAME_FORMAT, chip->sels[i].cs);
+ mtd->name = chip->default_name;
+ }
+
+ ppdata.of_node = np;
+ ret = mtd_device_parse_register(mtd, NULL, &ppdata, NULL, 0);
+ if (!ret)
+ return ret;
+
+ list_add_tail(&chip->node, &nfc->chips);
+
+ return 0;
+}
+
+static int sunxi_nand_chips_init(struct device *dev, struct sunxi_nfc *nfc)
+{
+ struct device_node *np = dev->of_node;
+ struct device_node *nand_np;
+ int nchips = of_get_child_count(np);
+ int ret;
+
+ if (nchips > 8)
+ return -EINVAL;
+
+ for_each_child_of_node(np, nand_np) {
+ ret = sunxi_nand_chip_init(dev, nfc, nand_np);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int sunxi_nfc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct resource *r;
+ struct sunxi_nfc *nfc;
+ int ret;
+
+ nfc = devm_kzalloc(dev, sizeof(*nfc), GFP_KERNEL);
+ if (!nfc) {
+ dev_err(dev, "failed to allocate NFC struct\n");
+ return -ENOMEM;
+ }
+
+ spin_lock_init(&nfc->controller.lock);
+ init_waitqueue_head(&nfc->controller.wq);
+ INIT_LIST_HEAD(&nfc->chips);
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ nfc->regs = devm_ioremap_resource(dev, r);
+ if (IS_ERR(nfc->regs)) {
+ dev_err(dev, "failed to remap iomem\n");
+ return PTR_ERR(nfc->regs);
+ }
+
+ nfc->irq = platform_get_irq(pdev, 0);
+ if (nfc->irq < 0) {
+ dev_err(dev, "failed to retrieve irq\n");
+ return nfc->irq;
+ }
+
+ nfc->ahb_clk = devm_clk_get(dev, "ahb_clk");
+ if (IS_ERR(nfc->ahb_clk)) {
+ dev_err(dev, "failed to retrieve ahb_clk\n");
+ return PTR_ERR(nfc->ahb_clk);
+ }
+
+ ret = clk_prepare_enable(nfc->ahb_clk);
+ if (ret)
+ return ret;
+
+ nfc->sclk = devm_clk_get(dev, "sclk");
+ if (IS_ERR(nfc->sclk)) {
+ dev_err(dev, "failed to retrieve nand_clk\n");
+ ret = PTR_ERR(nfc->sclk);
+ goto out_ahb_clk_unprepare;
+ }
+
+ ret = clk_prepare_enable(nfc->sclk);
+ if (ret)
+ goto out_ahb_clk_unprepare;
+
+ /* Reset NFC */
+ writel(readl(nfc->regs + NFC_REG_CTL) | NFC_RESET,
+ nfc->regs + NFC_REG_CTL);
+ while (readl(nfc->regs + NFC_REG_CTL) & NFC_RESET)
+ ;
+
+ writel(0, nfc->regs + NFC_REG_INT);
+ ret = devm_request_irq(dev, nfc->irq, sunxi_nfc_interrupt,
+ 0, "sunxi-nand", nfc);
+ if (ret)
+ goto out_sclk_unprepare;
+
+ platform_set_drvdata(pdev, nfc);
+
+ writel(0x100, nfc->regs + NFC_REG_TIMING_CTL);
+ writel(0x7ff, nfc->regs + NFC_REG_TIMING_CFG);
+
+ ret = sunxi_nand_chips_init(dev, nfc);
+ if (ret) {
+ dev_err(dev, "failed to init nand chips\n");
+ goto out_sclk_unprepare;
+ }
+
+ return 0;
+
+out_sclk_unprepare:
+ clk_disable_unprepare(nfc->sclk);
+out_ahb_clk_unprepare:
+ clk_disable_unprepare(nfc->ahb_clk);
+
+ return ret;
+}
+
+static const struct of_device_id sunxi_nfc_ids[] = {
+ { .compatible = "allwinner,sun4i-nand" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, sunxi_nfc_ids);
+
+static struct platform_driver sunxi_nfc_driver = {
+ .driver = {
+ .name = "sunxi_nand",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(sunxi_nfc_ids),
+ },
+ .probe = sunxi_nfc_probe,
+};
+module_platform_driver(sunxi_nfc_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Boris BREZILLON");
+MODULE_DESCRIPTION("Allwinner NAND Flash Controller driver");
+MODULE_ALIAS("platform:sunxi_nfc");

Boris BREZILLON

unread,
Jan 30, 2014, 8:50:03 AM1/30/14
to
Add HW ECC support for the sunxi NAND Flash Controller.

Signed-off-by: Boris BREZILLON <b.brezi...@gmail.com>
---
drivers/mtd/nand/sunxi_nand.c | 279 +++++++++++++++++++++++++++++++++++++++--
1 file changed, 266 insertions(+), 13 deletions(-)

diff --git a/drivers/mtd/nand/sunxi_nand.c b/drivers/mtd/nand/sunxi_nand.c
index 1014b2a..b90268f 100644
--- a/drivers/mtd/nand/sunxi_nand.c
+++ b/drivers/mtd/nand/sunxi_nand.c
@@ -163,6 +163,11 @@ struct sunxi_nand_chip_sel {
#define DEFAULT_NAME_FORMAT "nand@%d"
#define MAX_NAME_SIZE (sizeof("nand@") + 2)

+struct sunxi_nand_hw_ecc {
+ int mode;
+ struct nand_ecclayout layout;
+};
+
struct sunxi_nand_chip {
struct list_head node;
struct nand_chip nand;
@@ -402,6 +407,126 @@ static void sunxi_nfc_cmd_ctrl(struct mtd_info *mtd, int dat,
sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
}

+static int sunxi_nfc_hwecc_read_page(struct mtd_info *mtd,
+ struct nand_chip *chip, uint8_t *buf,
+ int oob_required, int page)
+{
+ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(mtd);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
+ 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 offset;
+ u32 tmp;
+ int i;
+
+ tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
+ tmp &= ~(NFC_ECC_MODE | NFC_ECC_PIPELINE | NFC_ECC_BLOCK_SIZE |
+ NFC_ECC_BLOCK_SIZE);
+ tmp |= NFC_ECC_EN | (data->mode << NFC_ECC_MODE_SHIFT);
+ writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
+
+ for (i = 0; i < mtd->writesize / ecc->size; i++) {
+ if (i)
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, i * ecc->size, -1);
+ chip->read_buf(mtd, NULL, chip->ecc.size);
+ offset = mtd->writesize + layout->eccpos[i * ecc->bytes] - 4;
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, offset, -1);
+ while ((readl(nfc->regs + NFC_REG_ST) & NFC_CMD_FIFO_STATUS))
+ ;
+ tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | (1 << 30);
+ writel(tmp, nfc->regs + NFC_REG_CMD);
+ sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
+ memcpy_fromio(buf + (i * ecc->size), nfc->regs + NFC_RAM0_BASE,
+ chip->ecc.size);
+
+ if (readl(nfc->regs + NFC_REG_ECC_ST) & 0x1) {
+ mtd->ecc_stats.failed++;
+ } else {
+ tmp = readl(nfc->regs + NFC_REG_ECC_CNT0) & 0xff;
+ mtd->ecc_stats.corrected += tmp;
+ max_bitflips = max_t(unsigned int, max_bitflips, tmp);
+ }
+ }
+
+ if (oob_required) {
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1);
+ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ }
+
+ tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
+ tmp &= ~NFC_ECC_EN;
+
+ writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
+
+ return max_bitflips;
+}
+
+static int sunxi_nfc_hwecc_write_page(struct mtd_info *mtd,
+ struct nand_chip *chip,
+ const uint8_t *buf,
+ int oob_required)
+{
+ struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(mtd);
+ struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller);
+ struct nand_ecc_ctrl *ecc = &chip->ecc;
+ struct nand_ecclayout *layout = ecc->layout;
+ struct sunxi_nand_hw_ecc *data = ecc->priv;
+ int offset;
+ u32 tmp;
+ int i;
+ int j;
+
+ tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
+ tmp &= ~(NFC_ECC_MODE | NFC_ECC_PIPELINE | NFC_ECC_BLOCK_SIZE |
+ NFC_ECC_BLOCK_SIZE);
+ tmp |= NFC_ECC_EN | (data->mode << NFC_ECC_MODE_SHIFT);
+
+ writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
+
+ for (i = 0; i < mtd->writesize / ecc->size; i++) {
+ if (i)
+ chip->cmdfunc(mtd, NAND_CMD_RNDIN, i * ecc->size, -1);
+
+ chip->write_buf(mtd, buf + (i * ecc->size), ecc->size);
+ offset = mtd->writesize + layout->eccpos[i * ecc->bytes] - 4;
+ chip->cmdfunc(mtd, NAND_CMD_RNDIN, offset, -1);
+ while ((readl(nfc->regs + NFC_REG_ST) & NFC_CMD_FIFO_STATUS))
+ ;
+
+ /* Fill OOB data in */
+ for (j = 0; j < 4; j++) {
+ if (oob_required) {
+ offset = layout->eccpos[i * ecc->size] - 4;
+ writeb(chip->oob_poi[offset + j],
+ nfc->regs + NFC_REG_USER_DATA_BASE + j);
+ } else {
+ writeb(0xff,
+ nfc->regs + NFC_REG_USER_DATA_BASE + j);
+ }
+ }
+
+ tmp = NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD |
+ NFC_ACCESS_DIR | (1 << 30);
+ writel(tmp, nfc->regs + NFC_REG_CMD);
+ sunxi_nfc_wait_int(nfc, NFC_CMD_INT_FLAG, 0);
+ }
+
+ if (oob_required && chip->ecc.layout->oobfree[0].length > 2) {
+ chip->cmdfunc(mtd, NAND_CMD_RNDIN, mtd->writesize, -1);
+ chip->write_buf(mtd, chip->oob_poi,
+ chip->ecc.layout->oobfree[0].length - 2);
+ }
+
+ tmp = readl(nfc->regs + NFC_REG_ECC_CTL);
+ tmp &= ~(NFC_ECC_EN | NFC_ECC_PIPELINE);
+
+ writel(tmp, nfc->regs + NFC_REG_ECC_CTL);
+
+ return 0;
+}
+
static int sunxi_nand_chip_set_timings(struct sunxi_nand_chip *chip,
const struct nand_sdr_timings *timings)
{
@@ -504,6 +629,144 @@ static int sunxi_nand_chip_init_timings(struct sunxi_nand_chip *chip,
return sunxi_nand_chip_set_timings(chip, timings);
}

+static int sunxi_nand_chip_hwecc_init(struct device *dev,
+ struct sunxi_nand_chip *chip,
+ struct mtd_info *mtd,
+ struct device_node *np)
+{
+ struct nand_chip *nand = &chip->nand;
+ struct nand_ecc_ctrl *ecc = &nand->ecc;
+ struct sunxi_nand_hw_ecc *data;
+ struct nand_ecclayout *layout;
+ int nsectors;
+ int i;
+ int j;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ ecc->read_page = sunxi_nfc_hwecc_read_page;
+ ecc->write_page = sunxi_nfc_hwecc_write_page;
+
+ if (nand->ecc_strength_ds <= 16) {
+ nand->ecc_strength_ds = 16;
+ data->mode = 0;
+ } else if (nand->ecc_strength_ds <= 24) {
+ nand->ecc_strength_ds = 24;
+ data->mode = 1;
+ } else if (nand->ecc_strength_ds <= 28) {
+ nand->ecc_strength_ds = 28;
+ data->mode = 2;
+ } else if (nand->ecc_strength_ds <= 32) {
+ nand->ecc_strength_ds = 32;
+ data->mode = 3;
+ } else if (nand->ecc_strength_ds <= 40) {
+ nand->ecc_strength_ds = 40;
+ data->mode = 4;
+ } else if (nand->ecc_strength_ds <= 48) {
+ nand->ecc_strength_ds = 48;
+ data->mode = 5;
+ } else if (nand->ecc_strength_ds <= 56) {
+ nand->ecc_strength_ds = 56;
+ data->mode = 6;
+ } else if (nand->ecc_strength_ds <= 60) {
+ nand->ecc_strength_ds = 60;
+ data->mode = 7;
+ } else if (nand->ecc_strength_ds <= 64) {
+ nand->ecc_strength_ds = 64;
+ data->mode = 8;
+ } else {
+ dev_err(dev, "unsupported strength\n");
+ return -ENOTSUPP;
+ }
+
+ /* HW ECC always request ECC bytes for 1024 bytes blocks */
+ ecc->bytes = ((nand->ecc_strength_ds * fls(8 * 1024)) + 7) / 8;
+
+ /* HW ECC always work with even numbers of ECC bytes */
+ if (ecc->bytes % 2)
+ ecc->bytes++;
+ ecc->strength = nand->ecc_strength_ds;
+ ecc->size = nand->ecc_step_ds;
+
+ layout = &data->layout;
+ nsectors = mtd->writesize / ecc->size;
+
+ if (mtd->oobsize < ((ecc->bytes + 4) * nsectors))
+ return -EINVAL;
+
+ layout->eccbytes = (ecc->bytes * nsectors);
+
+ /*
+ * The first 2 bytes are used for BB markers.
+ * We merge the 4 user available bytes from HW ECC with this
+ * first section, hence why the + 2 operation (- 2 + 4).
+ */
+ layout->oobfree[0].length = mtd->oobsize + 2 -
+ ((ecc->bytes + 4) * nsectors);
+ layout->oobfree[0].offset = 2;
+ for (i = 0; i < nsectors; i++) {
+ /*
+ * The first 4 ECC block bytes are already counted in the first
+ * obbfree entry.
+ */
+ if (i) {
+ layout->oobfree[i].offset =
+ layout->oobfree[i - 1].offset +
+ layout->oobfree[i - 1].length +
+ ecc->bytes;
+ layout->oobfree[i].length = 4;
+ }
+
+ for (j = 0; j < ecc->bytes; j++)
+ layout->eccpos[(ecc->bytes * i) + j] =
+ layout->oobfree[i].offset +
+ layout->oobfree[i].length + j;
+ }
+
+ ecc->layout = layout;
+ ecc->priv = data;
+
+ return 0;
+}
+
+static int sunxi_nand_chip_ecc_init(struct device *dev,
+ struct sunxi_nand_chip *chip,
+ struct mtd_info *mtd,
+ struct device_node *np)
+{
+ struct nand_chip *nand = &chip->nand;
+ u32 strength;
+ u32 blk_size;
+ int ret;
+
+ nand->ecc.mode = of_get_nand_ecc_mode(np);
+
+ if (!of_get_nand_ecc_level(np, &strength, &blk_size)) {
+ nand->ecc_step_ds = blk_size;
+ nand->ecc_strength_ds = strength;
+ }
+
+ switch (nand->ecc.mode) {
+ case NAND_ECC_SOFT_BCH:
+ nand->ecc.size = nand->ecc_step_ds;
+ nand->ecc.bytes = ((nand->ecc_strength_ds *
+ fls(8 * nand->ecc_step_ds)) + 7) / 8;
+ break;
+ case NAND_ECC_HW:
+ ret = sunxi_nand_chip_hwecc_init(dev, chip, mtd, np);
+ if (ret)
+ return ret;
+ break;
+ case NAND_ECC_NONE:
+ default:
+ break;
+ }
+
+ return 0;
+}
+
static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
struct device_node *np)
{
@@ -512,8 +775,6 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
struct mtd_part_parser_data ppdata;
struct mtd_info *mtd;
struct nand_chip *nand;
- u32 strength;
- u32 blk_size;
int nsels;
int ret;
int i;
@@ -586,7 +847,6 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
nand->write_buf = sunxi_nfc_write_buf;
nand->read_byte = sunxi_nfc_read_byte;

- nand->ecc.mode = of_get_nand_ecc_mode(np);
if (of_get_nand_on_flash_bbt(np))
nand->bbt_options |= NAND_BBT_USE_FLASH;

@@ -602,16 +862,9 @@ static int sunxi_nand_chip_init(struct device *dev, struct sunxi_nfc *nfc,
if (ret)
return ret;

- if (nand->ecc.mode == NAND_ECC_SOFT_BCH) {
- if (!of_get_nand_ecc_level(np, &strength, &blk_size)) {
- nand->ecc_step_ds = blk_size;
- nand->ecc_strength_ds = strength;
- }
-
- nand->ecc.size = nand->ecc_step_ds;
- nand->ecc.bytes = (((nand->ecc_strength_ds *
- fls(8 * nand->ecc_step_ds)) + 7) / 8);
- }
+ ret = sunxi_nand_chip_ecc_init(dev, chip, mtd, np);
+ if (ret)
+ return ret;

ret = nand_scan_tail(mtd);
if (ret)

Russell King - ARM Linux

unread,
Jan 30, 2014, 9:40:02 AM1/30/14
to
Boris,

Can you please explain to me why you mail all your patches _To:_ me?
As in, why do I appear in the To: line of all the patches you seem to
mail out, whether or not they're relevant to me. I see this very
regularly from you - virtually all patches I see on the LAKML mailing
list from you are always sent To: me as well.

Take for instance this one. It doesn't match up with anything in
MAINTAINERS for me. It doesn't even touch a file that I've touched.
Yet somehow you think that I should be in the To: header.

Being in the To: header means that you expect the recipient to do
something with your email. The Cc: header is to circulate copies of
your email to people who may be interested.

I'm neither for this stuff. Please stop this.

Thanks.
FTTC broadband for 0.8mile line: 5.8Mbps down 500kbps up. Estimation
in database were 13.1 to 19Mbit for a good line, about 7.5+ for a bad.
Estimate before purchase was "up to 13.2Mbit".

Boris BREZILLON

unread,
Jan 30, 2014, 10:10:02 AM1/30/14
to
Hello Russel,

On 30/01/2014 15:36, Russell King - ARM Linux wrote:
> Boris,
>
> Can you please explain to me why you mail all your patches _To:_ me?
> As in, why do I appear in the To: line of all the patches you seem to
> mail out, whether or not they're relevant to me. I see this very
> regularly from you - virtually all patches I see on the LAKML mailing
> list from you are always sent To: me as well.
>
> Take for instance this one. It doesn't match up with anything in
> MAINTAINERS for me. It doesn't even touch a file that I've touched.
> Yet somehow you think that I should be in the To: header.
>
> Being in the To: header means that you expect the recipient to do
> something with your email. The Cc: header is to circulate copies of
> your email to people who may be interested.
>
> I'm neither for this stuff. Please stop this.

Sorry for the inconvenience.

I'm using get_maintainer.pl script to retrieve the list of concerned
people, and you appears in that list for patches 10,11,12 and 14 of this
series.
Moreover I was told to send the whole series (not just specific patches
to people who might be concerned).

I obviously misuse this script, so please tell me how I should know which
patch I should send to whom.

Thanks.

Best Regards,

Boris

Grant Likely

unread,
Feb 4, 2014, 12:10:02 PM2/4/14
to
On Tue, 21 Jan 2014 15:57:40 -0700, Jason Gunthorpe <jgunt...@obsidianresearch.com> wrote:
> On Wed, Jan 15, 2014 at 06:03:01PM +0100, boris brezillon wrote:
>
> > >>Pick a mode value that fits all the parameters of the connected
> > >>non-ONFI flash.
> > >>
> > >>This would be instead of defining each parameter
> > >>individually.. Provide some helpers to convert from a onfi mode number
> > >>to all the onfi defined timing parameters so that drivers can
> > >>configure the HW..
> > >
> > >Are you suggesting we should provide a function that converts these
> > >modes into a nand_timings struct, or just use the timing modes and
> > >let the NAND controller drivers configure its IP accordingly ?
>
> Either seems reasonable to me, but passing the ONFI mode directly from
> the NAND core to the driver seems a little safer..

I agree here. There are a lot of parameters being defined. If it can be
boiled down to an ONFI mode that will make for a much more robust
binding. Far fewer things to get wrong.

g.

Grant Likely

unread,
Feb 5, 2014, 6:20:02 AM2/5/14
to
On Wed, 29 Jan 2014 14:53:32 -0300, Ezequiel Garcia <ezequie...@free-electrons.com> wrote:
> On Wed, Jan 29, 2014 at 03:34:13PM +0100, Boris BREZILLON wrote:
> > nand-ecc-level property statically defines NAND chip's ECC requirements.
> >
> > Signed-off-by: Boris BREZILLON <b.brezi...@gmail.com>
> > ---
> > Documentation/devicetree/bindings/mtd/nand.txt | 3 +++
> > 1 file changed, 3 insertions(+)
> >
> > diff --git a/Documentation/devicetree/bindings/mtd/nand.txt b/Documentation/devicetree/bindings/mtd/nand.txt
> > index 03855c8..0c962296 100644
> > --- a/Documentation/devicetree/bindings/mtd/nand.txt
> > +++ b/Documentation/devicetree/bindings/mtd/nand.txt
> > @@ -3,5 +3,8 @@
> > - nand-ecc-mode : String, operation mode of the NAND ecc mode.
> > Supported values are: "none", "soft", "hw", "hw_syndrome", "hw_oob_first",
> > "soft_bch".
> > +- nand-ecc-level : Two cells property defining the ECC level requirements.
> > + The first cell represent the strength and the second cell the ECC block size.
> > + E.g. : nand-ecc-level = <4 512>; /* 4 bits / 512 bytes */
> > - nand-bus-width : 8 or 16 bus width if not present 8
> > - nand-on-flash-bbt: boolean to enable on flash bbt option if not present false
>
> Hm.. when was this proposal agreed? It seems I've missed the
> discussion...
>
> FWIW, we've already proposed an equivalent one, but it received no
> feedback from the devicetree maintainers:

Sorry, binding review has become a huge undertaking.

>
> http://comments.gmane.org/gmane.linux.drivers.devicetree/58764
>
> Maybe we can discuss about it now?
>
> nand-ecc-strength : integer ECC required strength.
> nand-ecc-size : integer step size associated to the ECC strength.

I'm okay with either, but the above binding is indeed more readable.

g.

Boris BREZILLON

unread,
Feb 5, 2014, 8:40:02 AM2/5/14
to
That's fine by me, if everybody agrees, let's go for the
nand-ecc-strength/nand-ecc-size couple then.

I'll rebase next version of my series on Ezequiel's patch providing
these OF helpers.

Best Regards,

Boris

Boris BREZILLON

unread,
Feb 5, 2014, 9:00:02 AM2/5/14
to
Hello Brian,

On 23/01/2014 02:49, Brian Norris wrote:
> + Huang
>
> Hi Boris,
>
> On Wed, Jan 08, 2014 at 03:21:56PM +0100, Boris BREZILLON wrote:
>> The Hynix nand flashes store their ECC requirements in byte 4 of its id
>> (returned on READ ID command).
>>
>> Signed-off-by: Boris BREZILLON <b.bre...@overkiz.com>
> I haven't verified yet (perhaps Huang can confirm?), but this may be
> similar to a patch Huang submitted recently. In his case, we found that
> this table is actually quite unreliable and is likely hard to maintain.

Indeed (as stated in this thread
http://comments.gmane.org/gmane.linux.drivers.mtd/50252).
I'll remove this patch from the next version of this series and make use
of the
nand-ecc-strength /nand-ecc-size DT properties instead.

Thanks.

Best Regards,

Boris

Ezequiel Garcia

unread,
Feb 5, 2014, 9:20:03 AM2/5/14
to
Hi Grant, Boris:

(BTW, dropped Russell, Rob Landley and some unrelated mailing lists from Cc,
and added Thomas, Gregory and Rob Herring).

On Wed, Feb 05, 2014 at 02:34:44PM +0100, Boris BREZILLON wrote:
> On 05/02/2014 12:15, Grant Likely wrote:
> > On Wed, 29 Jan 2014 14:53:32 -0300, Ezequiel Garcia <ezequie...@free-electrons.com> wrote:
[..]
> >>
> >> Maybe we can discuss about it now?
> >>
> >> nand-ecc-strength : integer ECC required strength.
> >> nand-ecc-size : integer step size associated to the ECC strength.
> > I'm okay with either, but the above binding is indeed more readable.
>
> That's fine by me, if everybody agrees, let's go for the
> nand-ecc-strength/nand-ecc-size couple then.
>

Great. So, if some DT dictator^C^Cmaintainers can Ack this binding,
I can send a new patchset, with pxa3xx-nand using it...

> I'll rebase next version of my series on Ezequiel's patch providing
> these OF helpers.
>

... and then you can base on it?

This is the original patchset:

http://permalink.gmane.org/gmane.linux.drivers.devicetree/58764
http://permalink.gmane.org/gmane.linux.drivers.devicetree/58763

--
Ezequiel García, Free Electrons
Embedded Linux, Kernel and Android Engineering
http://free-electrons.com

Grant Likely

unread,
Feb 17, 2014, 11:50:02 AM2/17/14
to
On Wed, Feb 5, 2014 at 2:19 PM, Ezequiel Garcia
<ezequie...@free-electrons.com> wrote:
> Hi Grant, Boris:
>
> (BTW, dropped Russell, Rob Landley and some unrelated mailing lists from Cc,
> and added Thomas, Gregory and Rob Herring).
>
> On Wed, Feb 05, 2014 at 02:34:44PM +0100, Boris BREZILLON wrote:
>> On 05/02/2014 12:15, Grant Likely wrote:
>> > On Wed, 29 Jan 2014 14:53:32 -0300, Ezequiel Garcia <ezequie...@free-electrons.com> wrote:
> [..]
>> >>
>> >> Maybe we can discuss about it now?
>> >>
>> >> nand-ecc-strength : integer ECC required strength.
>> >> nand-ecc-size : integer step size associated to the ECC strength.
>> > I'm okay with either, but the above binding is indeed more readable.
>>
>> That's fine by me, if everybody agrees, let's go for the
>> nand-ecc-strength/nand-ecc-size couple then.
>>
>
> Great. So, if some DT dictator^C^Cmaintainers can Ack this binding,
> I can send a new patchset, with pxa3xx-nand using it...
>
>> I'll rebase next version of my series on Ezequiel's patch providing
>> these OF helpers.
>>
>
> ... and then you can base on it?
>
> This is the original patchset:
>
> http://permalink.gmane.org/gmane.linux.drivers.devicetree/58764
> http://permalink.gmane.org/gmane.linux.drivers.devicetree/58763

I've looked at both. Go ahead and add my a-b line:

Acked-by: Grant Likely <grant....@linaro.org>

Ezequiel Garcia

unread,
Feb 17, 2014, 1:20:01 PM2/17/14
to
Cool. Thanks!

I'll push a patchset now.
--
Ezequiel García, Free Electrons
Embedded Linux, Kernel and Android Engineering
http://free-electrons.com
0 new messages