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
--
1.7.9.5
Add the sunxi NAND Flash Controller dt bindings documentation.
Signed-off-by: Boris BREZILLON <[email protected]>
---
.../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
Define the NAND pinctrl configs.
Signed-off-by: Boris BREZILLON <[email protected]>
---
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 {
--
1.7.9.5
Define a struct containing the standard NAND timings as described in NAND
datasheets.
Signed-off-by: Boris BREZILLON <[email protected]>
---
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 */
--
1.7.9.5
Add a function to retrieve NAND timings from a given DT node.
Signed-off-by: Boris BREZILLON <[email protected]>
---
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 */
--
1.7.9.5
The Hynix nand flashes store their ECC requirements in byte 4 of its id
(returned on READ ID command).
Signed-off-by: Boris BREZILLON <[email protected]>
---
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);
--
1.7.9.5
Add NAND Flash controller node definition to the A20 SoC.
Signed-off-by: Boris BREZILLON <[email protected]>
---
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>;
--
1.7.9.5
Add a NAND timing properties to NAND dt doc.
Signed-off-by: Boris BREZILLON <[email protected]>
---
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
--
1.7.9.5
Add the sunxi NAND Flash Controller driver.
Signed-off-by: Boris BREZILLON <[email protected]>
---
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 <[email protected]>
+ *
+ * Derived from Qiang Yu work:
+ * https://github.com/yuq/sunxi-nfc-mtd
+ * Copyright (C) 2013 Qiang Yu <[email protected]>
+ *
+ * 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");
--
1.7.9.5
Enable the NFC and describe the NAND flash connected to this controller.
Signed-off-by: Boris BREZILLON <[email protected]>
---
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-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>;
+ };
+ };
+
pinctrl@01c20800 {
led_pins_cubietruck: led_pins@0 {
allwinner,pins = "PH7", "PH11", "PH20", "PH21";
--
1.7.9.5
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 <[email protected]>
> ---
> 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.
> soc@01c00000 {
> + 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>;
> + };
> + };
> +
> pinctrl@01c20800 {
> led_pins_cubietruck: led_pins@0 {
> allwinner,pins = "PH7", "PH11", "PH20", "PH21";
On Wed, Jan 8, 2014 at 8:21 AM, Boris BREZILLON <[email protected]> wrote:
> Add a function to retrieve NAND timings from a given DT node.
>
> Signed-off-by: Boris BREZILLON <[email protected]>
> ---
> 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);
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
On 08/01/2014 17:30, Rob Herring wrote:
> On Wed, Jan 8, 2014 at 8:21 AM, Boris BREZILLON <[email protected]> wrote:
>> Add a function to retrieve NAND timings from a given DT node.
>>
>> Signed-off-by: Boris BREZILLON <[email protected]>
>> ---
>> 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);
> 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
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
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
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.
Jason
Le 08/01/2014 15:22, Boris BREZILLON a ?crit :
> Add the sunxi NAND Flash Controller driver.
>
> Signed-off-by: Boris BREZILLON <[email protected]>
> ---
> 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
>
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 ?
> 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 <[email protected]>
> + *
> + * Derived from Qiang Yu work:
> + * https://github.com/yuq/sunxi-nfc-mtd
> + * Copyright (C) 2013 Qiang Yu <[email protected]>
> + *
> + * 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");
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
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
On 08/01/2014 20:13, Jason Gunthorpe wrote:
> 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..
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
>
> Jason
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.
Arnd
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..
Jason
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) ;-)
>
> 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
>
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:
http://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
>
> Jason
On 15/01/2014 16:09, boris brezillon wrote:
> 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:
>
> http://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 ?
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
>
> Best Regards,
>
> Boris
>
>>
>> Jason
>
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:
> >
> >http://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.
Jason
+ 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 <[email protected]>
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
On Sat, Jan 11, 2014 at 7:38 AM, boris brezillon
<[email protected]> 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
Hello Rob,
On 23/01/2014 16:22, Rob Herring wrote:
> On Sat, Jan 11, 2014 at 7:38 AM, boris brezillon
> <[email protected]> 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
>
> Rob
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 <[email protected]>
> 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...
>
> Brian
On Tue, 21 Jan 2014 15:57:40 -0700, Jason Gunthorpe <[email protected]> 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.
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 <[email protected]>
> 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
>
> Why do you need this ECC information, for my reference?
>
> Brian