All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/8] mtd: nand: Support for new DT NAND driver
@ 2014-08-13  9:11 ` Lee Jones
  0 siblings, 0 replies; 51+ messages in thread
From: Lee Jones @ 2014-08-13  9:11 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: lee.jones, kernel, computersforpeace, pekon, linux-mtd

Hi Brian, Pekon,

I believe all of your queries have either been answered or addressed
and I am hoping this will be the last submission. :)

/me crosses fingers!

Kind regards,
Lee

Lee Jones (8):
  ARM: multi-v7: Enable ST BCH NAND
  ARM: sti: Add two new clock definitions for use with ST's NAND
    controllers
  ARM: sti: Add BCH (NAND Flash) Controller support for STiH41x (Orly)
    SoCs
  ARM: sti: Enable BCH NAND for STiH416 B2020-RevE
  mtd: nand: stm_nand_bch: provide Device Tree documentation
  mtd: nand: add shared register defines for ST's NAND Controller
    drivers
  mtd: nand: stm_nand_bch: add support for ST's BCH NAND controller
  mtd: nand: stm_nand_bch: provide ST's implementation of Back Block
    Table

 Documentation/devicetree/bindings/mtd/stm-nand.txt |   74 +
 arch/arm/boot/dts/stih416-b2020e.dts               |    8 +
 arch/arm/boot/dts/stih41x-b2020.dtsi               |   38 +
 arch/arm/configs/multi_v7_defconfig                |    2 +
 drivers/mtd/nand/Kconfig                           |   15 +
 drivers/mtd/nand/Makefile                          |    2 +
 drivers/mtd/nand/stm_nand_bbt.c                    |  601 ++++++++
 drivers/mtd/nand/stm_nand_bch.c                    | 1618 ++++++++++++++++++++
 drivers/mtd/nand/stm_nand_dt.c                     |  109 ++
 drivers/mtd/nand/stm_nand_dt.h                     |   24 +
 drivers/mtd/nand/stm_nand_regs.h                   |  304 ++++
 include/dt-bindings/clock/stih416-clks.h           |    2 +
 include/linux/mtd/stm_nand.h                       |  147 ++
 include/linux/mtd/stm_nand_bbt.h                   |   17 +
 14 files changed, 2961 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mtd/stm-nand.txt
 create mode 100644 drivers/mtd/nand/stm_nand_bbt.c
 create mode 100644 drivers/mtd/nand/stm_nand_bch.c
 create mode 100644 drivers/mtd/nand/stm_nand_dt.c
 create mode 100644 drivers/mtd/nand/stm_nand_dt.h
 create mode 100644 drivers/mtd/nand/stm_nand_regs.h
 create mode 100644 include/linux/mtd/stm_nand.h
 create mode 100644 include/linux/mtd/stm_nand_bbt.h

-- 
1.9.1


^ permalink raw reply	[flat|nested] 51+ messages in thread

* [PATCH 0/8] mtd: nand: Support for new DT NAND driver
@ 2014-08-13  9:11 ` Lee Jones
  0 siblings, 0 replies; 51+ messages in thread
From: Lee Jones @ 2014-08-13  9:11 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: pekon, computersforpeace, lee.jones, kernel, linux-mtd

Hi Brian, Pekon,

I believe all of your queries have either been answered or addressed
and I am hoping this will be the last submission. :)

/me crosses fingers!

Kind regards,
Lee

Lee Jones (8):
  ARM: multi-v7: Enable ST BCH NAND
  ARM: sti: Add two new clock definitions for use with ST's NAND
    controllers
  ARM: sti: Add BCH (NAND Flash) Controller support for STiH41x (Orly)
    SoCs
  ARM: sti: Enable BCH NAND for STiH416 B2020-RevE
  mtd: nand: stm_nand_bch: provide Device Tree documentation
  mtd: nand: add shared register defines for ST's NAND Controller
    drivers
  mtd: nand: stm_nand_bch: add support for ST's BCH NAND controller
  mtd: nand: stm_nand_bch: provide ST's implementation of Back Block
    Table

 Documentation/devicetree/bindings/mtd/stm-nand.txt |   74 +
 arch/arm/boot/dts/stih416-b2020e.dts               |    8 +
 arch/arm/boot/dts/stih41x-b2020.dtsi               |   38 +
 arch/arm/configs/multi_v7_defconfig                |    2 +
 drivers/mtd/nand/Kconfig                           |   15 +
 drivers/mtd/nand/Makefile                          |    2 +
 drivers/mtd/nand/stm_nand_bbt.c                    |  601 ++++++++
 drivers/mtd/nand/stm_nand_bch.c                    | 1618 ++++++++++++++++++++
 drivers/mtd/nand/stm_nand_dt.c                     |  109 ++
 drivers/mtd/nand/stm_nand_dt.h                     |   24 +
 drivers/mtd/nand/stm_nand_regs.h                   |  304 ++++
 include/dt-bindings/clock/stih416-clks.h           |    2 +
 include/linux/mtd/stm_nand.h                       |  147 ++
 include/linux/mtd/stm_nand_bbt.h                   |   17 +
 14 files changed, 2961 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mtd/stm-nand.txt
 create mode 100644 drivers/mtd/nand/stm_nand_bbt.c
 create mode 100644 drivers/mtd/nand/stm_nand_bch.c
 create mode 100644 drivers/mtd/nand/stm_nand_dt.c
 create mode 100644 drivers/mtd/nand/stm_nand_dt.h
 create mode 100644 drivers/mtd/nand/stm_nand_regs.h
 create mode 100644 include/linux/mtd/stm_nand.h
 create mode 100644 include/linux/mtd/stm_nand_bbt.h

-- 
1.9.1

^ permalink raw reply	[flat|nested] 51+ messages in thread

* [PATCH 0/8] mtd: nand: Support for new DT NAND driver
@ 2014-08-13  9:11 ` Lee Jones
  0 siblings, 0 replies; 51+ messages in thread
From: Lee Jones @ 2014-08-13  9:11 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Brian, Pekon,

I believe all of your queries have either been answered or addressed
and I am hoping this will be the last submission. :)

/me crosses fingers!

Kind regards,
Lee

Lee Jones (8):
  ARM: multi-v7: Enable ST BCH NAND
  ARM: sti: Add two new clock definitions for use with ST's NAND
    controllers
  ARM: sti: Add BCH (NAND Flash) Controller support for STiH41x (Orly)
    SoCs
  ARM: sti: Enable BCH NAND for STiH416 B2020-RevE
  mtd: nand: stm_nand_bch: provide Device Tree documentation
  mtd: nand: add shared register defines for ST's NAND Controller
    drivers
  mtd: nand: stm_nand_bch: add support for ST's BCH NAND controller
  mtd: nand: stm_nand_bch: provide ST's implementation of Back Block
    Table

 Documentation/devicetree/bindings/mtd/stm-nand.txt |   74 +
 arch/arm/boot/dts/stih416-b2020e.dts               |    8 +
 arch/arm/boot/dts/stih41x-b2020.dtsi               |   38 +
 arch/arm/configs/multi_v7_defconfig                |    2 +
 drivers/mtd/nand/Kconfig                           |   15 +
 drivers/mtd/nand/Makefile                          |    2 +
 drivers/mtd/nand/stm_nand_bbt.c                    |  601 ++++++++
 drivers/mtd/nand/stm_nand_bch.c                    | 1618 ++++++++++++++++++++
 drivers/mtd/nand/stm_nand_dt.c                     |  109 ++
 drivers/mtd/nand/stm_nand_dt.h                     |   24 +
 drivers/mtd/nand/stm_nand_regs.h                   |  304 ++++
 include/dt-bindings/clock/stih416-clks.h           |    2 +
 include/linux/mtd/stm_nand.h                       |  147 ++
 include/linux/mtd/stm_nand_bbt.h                   |   17 +
 14 files changed, 2961 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mtd/stm-nand.txt
 create mode 100644 drivers/mtd/nand/stm_nand_bbt.c
 create mode 100644 drivers/mtd/nand/stm_nand_bch.c
 create mode 100644 drivers/mtd/nand/stm_nand_dt.c
 create mode 100644 drivers/mtd/nand/stm_nand_dt.h
 create mode 100644 drivers/mtd/nand/stm_nand_regs.h
 create mode 100644 include/linux/mtd/stm_nand.h
 create mode 100644 include/linux/mtd/stm_nand_bbt.h

-- 
1.9.1

^ permalink raw reply	[flat|nested] 51+ messages in thread

* [PATCH 1/8] ARM: multi-v7: Enable ST BCH NAND
  2014-08-13  9:11 ` Lee Jones
  (?)
@ 2014-08-13  9:12   ` Lee Jones
  -1 siblings, 0 replies; 51+ messages in thread
From: Lee Jones @ 2014-08-13  9:12 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: lee.jones, kernel, computersforpeace, pekon, linux-mtd

Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
 arch/arm/configs/multi_v7_defconfig | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig
index 5348364..4670836 100644
--- a/arch/arm/configs/multi_v7_defconfig
+++ b/arch/arm/configs/multi_v7_defconfig
@@ -115,7 +115,9 @@ CONFIG_DMA_CMA=y
 CONFIG_CMA_SIZE_MBYTES=64
 CONFIG_OMAP_OCP2SCP=y
 CONFIG_MTD=y
+CONFIG_MTD_NAND=y
 CONFIG_MTD_M25P80=y
+CONFIG_MTD_NAND_STM_BCH=y
 CONFIG_BLK_DEV_LOOP=y
 CONFIG_ICS932S401=y
 CONFIG_APDS9802ALS=y
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH 1/8] ARM: multi-v7: Enable ST BCH NAND
@ 2014-08-13  9:12   ` Lee Jones
  0 siblings, 0 replies; 51+ messages in thread
From: Lee Jones @ 2014-08-13  9:12 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: pekon, computersforpeace, lee.jones, kernel, linux-mtd

Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
 arch/arm/configs/multi_v7_defconfig | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig
index 5348364..4670836 100644
--- a/arch/arm/configs/multi_v7_defconfig
+++ b/arch/arm/configs/multi_v7_defconfig
@@ -115,7 +115,9 @@ CONFIG_DMA_CMA=y
 CONFIG_CMA_SIZE_MBYTES=64
 CONFIG_OMAP_OCP2SCP=y
 CONFIG_MTD=y
+CONFIG_MTD_NAND=y
 CONFIG_MTD_M25P80=y
+CONFIG_MTD_NAND_STM_BCH=y
 CONFIG_BLK_DEV_LOOP=y
 CONFIG_ICS932S401=y
 CONFIG_APDS9802ALS=y
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH 1/8] ARM: multi-v7: Enable ST BCH NAND
@ 2014-08-13  9:12   ` Lee Jones
  0 siblings, 0 replies; 51+ messages in thread
From: Lee Jones @ 2014-08-13  9:12 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
 arch/arm/configs/multi_v7_defconfig | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig
index 5348364..4670836 100644
--- a/arch/arm/configs/multi_v7_defconfig
+++ b/arch/arm/configs/multi_v7_defconfig
@@ -115,7 +115,9 @@ CONFIG_DMA_CMA=y
 CONFIG_CMA_SIZE_MBYTES=64
 CONFIG_OMAP_OCP2SCP=y
 CONFIG_MTD=y
+CONFIG_MTD_NAND=y
 CONFIG_MTD_M25P80=y
+CONFIG_MTD_NAND_STM_BCH=y
 CONFIG_BLK_DEV_LOOP=y
 CONFIG_ICS932S401=y
 CONFIG_APDS9802ALS=y
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH 2/8] ARM: sti: Add two new clock definitions for use with ST's NAND controllers
  2014-08-13  9:11 ` Lee Jones
  (?)
@ 2014-08-13  9:12   ` Lee Jones
  -1 siblings, 0 replies; 51+ messages in thread
From: Lee Jones @ 2014-08-13  9:12 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: lee.jones, kernel, computersforpeace, pekon, linux-mtd

The EMISS and NAND Control clocks are used when conducting operations
on attached NAND Flash chips.

Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
 include/dt-bindings/clock/stih416-clks.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/include/dt-bindings/clock/stih416-clks.h b/include/dt-bindings/clock/stih416-clks.h
index f9bdbd1..3a8c979 100644
--- a/include/dt-bindings/clock/stih416-clks.h
+++ b/include/dt-bindings/clock/stih416-clks.h
@@ -7,10 +7,12 @@
 
 /* CLOCKGEN A0 */
 #define CLK_ICN_REG		0
+#define CLK_EMISS		3
 #define CLK_ETH1_PHY		4
 
 /* CLOCKGEN A1 */
 #define CLK_ICN_IF_2		0
 #define CLK_GMAC0_PHY		3
+#define CLK_NAND_CTRL		4
 
 #endif
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH 2/8] ARM: sti: Add two new clock definitions for use with ST's NAND controllers
@ 2014-08-13  9:12   ` Lee Jones
  0 siblings, 0 replies; 51+ messages in thread
From: Lee Jones @ 2014-08-13  9:12 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: pekon, computersforpeace, lee.jones, kernel, linux-mtd

The EMISS and NAND Control clocks are used when conducting operations
on attached NAND Flash chips.

Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
 include/dt-bindings/clock/stih416-clks.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/include/dt-bindings/clock/stih416-clks.h b/include/dt-bindings/clock/stih416-clks.h
index f9bdbd1..3a8c979 100644
--- a/include/dt-bindings/clock/stih416-clks.h
+++ b/include/dt-bindings/clock/stih416-clks.h
@@ -7,10 +7,12 @@
 
 /* CLOCKGEN A0 */
 #define CLK_ICN_REG		0
+#define CLK_EMISS		3
 #define CLK_ETH1_PHY		4
 
 /* CLOCKGEN A1 */
 #define CLK_ICN_IF_2		0
 #define CLK_GMAC0_PHY		3
+#define CLK_NAND_CTRL		4
 
 #endif
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH 2/8] ARM: sti: Add two new clock definitions for use with ST's NAND controllers
@ 2014-08-13  9:12   ` Lee Jones
  0 siblings, 0 replies; 51+ messages in thread
From: Lee Jones @ 2014-08-13  9:12 UTC (permalink / raw)
  To: linux-arm-kernel

The EMISS and NAND Control clocks are used when conducting operations
on attached NAND Flash chips.

Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
 include/dt-bindings/clock/stih416-clks.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/include/dt-bindings/clock/stih416-clks.h b/include/dt-bindings/clock/stih416-clks.h
index f9bdbd1..3a8c979 100644
--- a/include/dt-bindings/clock/stih416-clks.h
+++ b/include/dt-bindings/clock/stih416-clks.h
@@ -7,10 +7,12 @@
 
 /* CLOCKGEN A0 */
 #define CLK_ICN_REG		0
+#define CLK_EMISS		3
 #define CLK_ETH1_PHY		4
 
 /* CLOCKGEN A1 */
 #define CLK_ICN_IF_2		0
 #define CLK_GMAC0_PHY		3
+#define CLK_NAND_CTRL		4
 
 #endif
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH 3/8] ARM: sti: Add BCH (NAND Flash) Controller support for STiH41x (Orly) SoCs
  2014-08-13  9:11 ` Lee Jones
  (?)
@ 2014-08-13  9:12   ` Lee Jones
  -1 siblings, 0 replies; 51+ messages in thread
From: Lee Jones @ 2014-08-13  9:12 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: lee.jones, kernel, computersforpeace, pekon, linux-mtd

Provide device information and flash layout for the NAND Micron
MT29F8G08ABABAWP chip found on the STM B2020 Development Board.

Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
 arch/arm/boot/dts/stih41x-b2020.dtsi | 38 ++++++++++++++++++++++++++++++++++++
 1 file changed, 38 insertions(+)

diff --git a/arch/arm/boot/dts/stih41x-b2020.dtsi b/arch/arm/boot/dts/stih41x-b2020.dtsi
index d8a8429..553ee1f 100644
--- a/arch/arm/boot/dts/stih41x-b2020.dtsi
+++ b/arch/arm/boot/dts/stih41x-b2020.dtsi
@@ -74,5 +74,43 @@
 
 			pinctrl-0	= <&pinctrl_rgmii1>;
 		};
+
+		nand@fe901000 {
+			compatible = "st,nand-bch";
+			reg = <0xfe901000 0x1000>, <0xfef00000 0x107c>;
+			reg-names = "emi_nand", "emiss";
+			interrupts = <0 139 0x0>;
+			interrupt-names = "nand_irq";
+			status = "disabled";
+
+			/*
+			 * Micron MT29F8G08ABABAWP:
+			 *  - Size = 8Gib(1GiB); Page = 4096+224; Block = 512KiB
+			 *  - ECC = 4-bit/540B min
+			 *  - ONFI 2.1 (timing parameters retrieved during probe)
+			 */
+			bank0 {
+				nand-on-flash-bbt;
+				st,nand-csn		= <0>;
+				st,nand-timing-relax	= <0>;
+
+				partitions {
+					#address-cells	= <1>;
+					#size-cells	= <1>;
+
+					partition@0 {
+						/* 8MB */
+						label = "NANDFlash1";
+						reg = <0x00000000 0x00800000>;
+					};
+
+					partition@800000 {
+						/* 1GB - 8MB */
+						label = "NANDFlash2";
+						reg = <0x00800000 0x1f000000>;
+					};
+				};
+			};
+		};
 	};
 };
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH 3/8] ARM: sti: Add BCH (NAND Flash) Controller support for STiH41x (Orly) SoCs
@ 2014-08-13  9:12   ` Lee Jones
  0 siblings, 0 replies; 51+ messages in thread
From: Lee Jones @ 2014-08-13  9:12 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: pekon, computersforpeace, lee.jones, kernel, linux-mtd

Provide device information and flash layout for the NAND Micron
MT29F8G08ABABAWP chip found on the STM B2020 Development Board.

Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
 arch/arm/boot/dts/stih41x-b2020.dtsi | 38 ++++++++++++++++++++++++++++++++++++
 1 file changed, 38 insertions(+)

diff --git a/arch/arm/boot/dts/stih41x-b2020.dtsi b/arch/arm/boot/dts/stih41x-b2020.dtsi
index d8a8429..553ee1f 100644
--- a/arch/arm/boot/dts/stih41x-b2020.dtsi
+++ b/arch/arm/boot/dts/stih41x-b2020.dtsi
@@ -74,5 +74,43 @@
 
 			pinctrl-0	= <&pinctrl_rgmii1>;
 		};
+
+		nand@fe901000 {
+			compatible = "st,nand-bch";
+			reg = <0xfe901000 0x1000>, <0xfef00000 0x107c>;
+			reg-names = "emi_nand", "emiss";
+			interrupts = <0 139 0x0>;
+			interrupt-names = "nand_irq";
+			status = "disabled";
+
+			/*
+			 * Micron MT29F8G08ABABAWP:
+			 *  - Size = 8Gib(1GiB); Page = 4096+224; Block = 512KiB
+			 *  - ECC = 4-bit/540B min
+			 *  - ONFI 2.1 (timing parameters retrieved during probe)
+			 */
+			bank0 {
+				nand-on-flash-bbt;
+				st,nand-csn		= <0>;
+				st,nand-timing-relax	= <0>;
+
+				partitions {
+					#address-cells	= <1>;
+					#size-cells	= <1>;
+
+					partition@0 {
+						/* 8MB */
+						label = "NANDFlash1";
+						reg = <0x00000000 0x00800000>;
+					};
+
+					partition@800000 {
+						/* 1GB - 8MB */
+						label = "NANDFlash2";
+						reg = <0x00800000 0x1f000000>;
+					};
+				};
+			};
+		};
 	};
 };
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH 3/8] ARM: sti: Add BCH (NAND Flash) Controller support for STiH41x (Orly) SoCs
@ 2014-08-13  9:12   ` Lee Jones
  0 siblings, 0 replies; 51+ messages in thread
From: Lee Jones @ 2014-08-13  9:12 UTC (permalink / raw)
  To: linux-arm-kernel

Provide device information and flash layout for the NAND Micron
MT29F8G08ABABAWP chip found on the STM B2020 Development Board.

Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
 arch/arm/boot/dts/stih41x-b2020.dtsi | 38 ++++++++++++++++++++++++++++++++++++
 1 file changed, 38 insertions(+)

diff --git a/arch/arm/boot/dts/stih41x-b2020.dtsi b/arch/arm/boot/dts/stih41x-b2020.dtsi
index d8a8429..553ee1f 100644
--- a/arch/arm/boot/dts/stih41x-b2020.dtsi
+++ b/arch/arm/boot/dts/stih41x-b2020.dtsi
@@ -74,5 +74,43 @@
 
 			pinctrl-0	= <&pinctrl_rgmii1>;
 		};
+
+		nand at fe901000 {
+			compatible = "st,nand-bch";
+			reg = <0xfe901000 0x1000>, <0xfef00000 0x107c>;
+			reg-names = "emi_nand", "emiss";
+			interrupts = <0 139 0x0>;
+			interrupt-names = "nand_irq";
+			status = "disabled";
+
+			/*
+			 * Micron MT29F8G08ABABAWP:
+			 *  - Size = 8Gib(1GiB); Page = 4096+224; Block = 512KiB
+			 *  - ECC = 4-bit/540B min
+			 *  - ONFI 2.1 (timing parameters retrieved during probe)
+			 */
+			bank0 {
+				nand-on-flash-bbt;
+				st,nand-csn		= <0>;
+				st,nand-timing-relax	= <0>;
+
+				partitions {
+					#address-cells	= <1>;
+					#size-cells	= <1>;
+
+					partition at 0 {
+						/* 8MB */
+						label = "NANDFlash1";
+						reg = <0x00000000 0x00800000>;
+					};
+
+					partition at 800000 {
+						/* 1GB - 8MB */
+						label = "NANDFlash2";
+						reg = <0x00800000 0x1f000000>;
+					};
+				};
+			};
+		};
 	};
 };
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH 4/8] ARM: sti: Enable BCH NAND for STiH416 B2020-RevE
  2014-08-13  9:11 ` Lee Jones
  (?)
@ 2014-08-13  9:12   ` Lee Jones
  -1 siblings, 0 replies; 51+ messages in thread
From: Lee Jones @ 2014-08-13  9:12 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: lee.jones, kernel, computersforpeace, pekon, linux-mtd

Also supply the platform specific clocks required by the NAND driver.

Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
 arch/arm/boot/dts/stih416-b2020e.dts | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/arch/arm/boot/dts/stih416-b2020e.dts b/arch/arm/boot/dts/stih416-b2020e.dts
index ba0fa2c..f4b92d5 100644
--- a/arch/arm/boot/dts/stih416-b2020e.dts
+++ b/arch/arm/boot/dts/stih416-b2020e.dts
@@ -31,5 +31,13 @@
 		ethernet1: dwmac@fef08000 {
 			snps,reset-gpio = <&PIO0 7>;
 		};
+
+		nand@fe901000 {
+			clock-names = "emi", "bch";
+			clocks = <&clk_s_a0_ls CLK_EMISS>,
+				 <&clk_s_a1_ls CLK_NAND_CTRL>;
+
+			status = "okay";
+		};
 	};
 };
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH 4/8] ARM: sti: Enable BCH NAND for STiH416 B2020-RevE
@ 2014-08-13  9:12   ` Lee Jones
  0 siblings, 0 replies; 51+ messages in thread
From: Lee Jones @ 2014-08-13  9:12 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: pekon, computersforpeace, lee.jones, kernel, linux-mtd

Also supply the platform specific clocks required by the NAND driver.

Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
 arch/arm/boot/dts/stih416-b2020e.dts | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/arch/arm/boot/dts/stih416-b2020e.dts b/arch/arm/boot/dts/stih416-b2020e.dts
index ba0fa2c..f4b92d5 100644
--- a/arch/arm/boot/dts/stih416-b2020e.dts
+++ b/arch/arm/boot/dts/stih416-b2020e.dts
@@ -31,5 +31,13 @@
 		ethernet1: dwmac@fef08000 {
 			snps,reset-gpio = <&PIO0 7>;
 		};
+
+		nand@fe901000 {
+			clock-names = "emi", "bch";
+			clocks = <&clk_s_a0_ls CLK_EMISS>,
+				 <&clk_s_a1_ls CLK_NAND_CTRL>;
+
+			status = "okay";
+		};
 	};
 };
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH 4/8] ARM: sti: Enable BCH NAND for STiH416 B2020-RevE
@ 2014-08-13  9:12   ` Lee Jones
  0 siblings, 0 replies; 51+ messages in thread
From: Lee Jones @ 2014-08-13  9:12 UTC (permalink / raw)
  To: linux-arm-kernel

Also supply the platform specific clocks required by the NAND driver.

Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
 arch/arm/boot/dts/stih416-b2020e.dts | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/arch/arm/boot/dts/stih416-b2020e.dts b/arch/arm/boot/dts/stih416-b2020e.dts
index ba0fa2c..f4b92d5 100644
--- a/arch/arm/boot/dts/stih416-b2020e.dts
+++ b/arch/arm/boot/dts/stih416-b2020e.dts
@@ -31,5 +31,13 @@
 		ethernet1: dwmac at fef08000 {
 			snps,reset-gpio = <&PIO0 7>;
 		};
+
+		nand at fe901000 {
+			clock-names = "emi", "bch";
+			clocks = <&clk_s_a0_ls CLK_EMISS>,
+				 <&clk_s_a1_ls CLK_NAND_CTRL>;
+
+			status = "okay";
+		};
 	};
 };
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH 5/8] mtd: nand: stm_nand_bch: provide Device Tree documentation
  2014-08-13  9:11 ` Lee Jones
  (?)
@ 2014-08-13  9:12   ` Lee Jones
  -1 siblings, 0 replies; 51+ messages in thread
From: Lee Jones @ 2014-08-13  9:12 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: lee.jones, kernel, computersforpeace, pekon, linux-mtd, devicetree

This is where we describe the different new and generic options used by
the ST BCH driver.

Cc: devicetree@vger.kernel.org
Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
 Documentation/devicetree/bindings/mtd/stm-nand.txt | 74 ++++++++++++++++++++++
 1 file changed, 74 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mtd/stm-nand.txt

diff --git a/Documentation/devicetree/bindings/mtd/stm-nand.txt b/Documentation/devicetree/bindings/mtd/stm-nand.txt
new file mode 100644
index 0000000..3176252
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/stm-nand.txt
@@ -0,0 +1,74 @@
+STM BCH NAND Support
+--------------------
+
+Required properties:
+
+- compatible		: Should be "st,nand-bch"
+- reg			: Should contain register's location and length
+- reg-names		: "nand_mem" - NAND Controller register map
+			  "nand_dma" - BCH Controller DMA configuration map
+- interrupts		: Interrupt number
+- interrupt-names	: "nand_irq" - NAND Controller IRQ
+- st,nand-banks		: Subnode representing one or more "banks" of NAND
+			  Flash, connected to an STM NAND Controller (see
+			  description below).
+- nand-ecc-strength	: Generic NAND property (See mtd/nand.txt)
+			  Options are; 0, 18 or 30. If not present, the driver
+			  will choose the strongest scheme compatible if the
+			  OOB size.
+
+Properties describing Bank of NAND Flash ("st,nand-banks"):
+
+- st,nand-csn		: Chip select associated with the Bank.
+
+- st,nand-timing-relax	: [Optional] Number of IP clock cycles by which to
+			  "relax" timing configuration.  Required on some boards
+			  to accommodate board-level limitations. Applies to
+			  ONFI timing mode configuration.
+
+- nand-on-flash-bbt	: Generic NAND property (See mtd/nand.txt)
+
+- partitions		: [Optional] Subnode describing MTD partition map
+			  (see mtd/partition.txt)
+
+Note, during initialisation, the NAND Controller timing registers are configured
+according to one of the following methods, in order of precedence:
+
+	   1. Configuration based on ONFI timing mode, as advertised by the
+	      device during ONFI-probing (ONFI-compliant NAND only).
+
+	   2. Use reset/safe timing values
+
+Example:
+
+	nand@fe901000 {
+		compatible = "st,nand-bch";
+		reg = <0xfe901000 0x1000>, <0xfef00800 0x0800>;
+		reg-names = "nand_mem", "nand_dma";
+		interrupts = <0 139 0x0>;
+		interrupt-names = "nand_irq";
+		nand-ecc-strength = <30>;
+
+		status = "okay";
+
+		bank0 {
+			/* NAND_BBT_USE_FLASH */
+			nand-on-flash-bbt;
+			st,nand-csn		= <0>;
+
+			partitions {
+				#address-cells = <1>;
+				#size-cells = <1>;
+
+				partition@0{
+					label = "NANDFlash1";
+					reg = <0x00000000 0x00800000>;
+				};
+
+				partition@800000{
+					label = "NANDFlash2";
+					reg = <0x00800000 0x0f800000>;
+				};
+			};
+		};
+	};
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH 5/8] mtd: nand: stm_nand_bch: provide Device Tree documentation
@ 2014-08-13  9:12   ` Lee Jones
  0 siblings, 0 replies; 51+ messages in thread
From: Lee Jones @ 2014-08-13  9:12 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: devicetree, kernel, pekon, linux-mtd, computersforpeace, lee.jones

This is where we describe the different new and generic options used by
the ST BCH driver.

Cc: devicetree@vger.kernel.org
Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
 Documentation/devicetree/bindings/mtd/stm-nand.txt | 74 ++++++++++++++++++++++
 1 file changed, 74 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mtd/stm-nand.txt

diff --git a/Documentation/devicetree/bindings/mtd/stm-nand.txt b/Documentation/devicetree/bindings/mtd/stm-nand.txt
new file mode 100644
index 0000000..3176252
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/stm-nand.txt
@@ -0,0 +1,74 @@
+STM BCH NAND Support
+--------------------
+
+Required properties:
+
+- compatible		: Should be "st,nand-bch"
+- reg			: Should contain register's location and length
+- reg-names		: "nand_mem" - NAND Controller register map
+			  "nand_dma" - BCH Controller DMA configuration map
+- interrupts		: Interrupt number
+- interrupt-names	: "nand_irq" - NAND Controller IRQ
+- st,nand-banks		: Subnode representing one or more "banks" of NAND
+			  Flash, connected to an STM NAND Controller (see
+			  description below).
+- nand-ecc-strength	: Generic NAND property (See mtd/nand.txt)
+			  Options are; 0, 18 or 30. If not present, the driver
+			  will choose the strongest scheme compatible if the
+			  OOB size.
+
+Properties describing Bank of NAND Flash ("st,nand-banks"):
+
+- st,nand-csn		: Chip select associated with the Bank.
+
+- st,nand-timing-relax	: [Optional] Number of IP clock cycles by which to
+			  "relax" timing configuration.  Required on some boards
+			  to accommodate board-level limitations. Applies to
+			  ONFI timing mode configuration.
+
+- nand-on-flash-bbt	: Generic NAND property (See mtd/nand.txt)
+
+- partitions		: [Optional] Subnode describing MTD partition map
+			  (see mtd/partition.txt)
+
+Note, during initialisation, the NAND Controller timing registers are configured
+according to one of the following methods, in order of precedence:
+
+	   1. Configuration based on ONFI timing mode, as advertised by the
+	      device during ONFI-probing (ONFI-compliant NAND only).
+
+	   2. Use reset/safe timing values
+
+Example:
+
+	nand@fe901000 {
+		compatible = "st,nand-bch";
+		reg = <0xfe901000 0x1000>, <0xfef00800 0x0800>;
+		reg-names = "nand_mem", "nand_dma";
+		interrupts = <0 139 0x0>;
+		interrupt-names = "nand_irq";
+		nand-ecc-strength = <30>;
+
+		status = "okay";
+
+		bank0 {
+			/* NAND_BBT_USE_FLASH */
+			nand-on-flash-bbt;
+			st,nand-csn		= <0>;
+
+			partitions {
+				#address-cells = <1>;
+				#size-cells = <1>;
+
+				partition@0{
+					label = "NANDFlash1";
+					reg = <0x00000000 0x00800000>;
+				};
+
+				partition@800000{
+					label = "NANDFlash2";
+					reg = <0x00800000 0x0f800000>;
+				};
+			};
+		};
+	};
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH 5/8] mtd: nand: stm_nand_bch: provide Device Tree documentation
@ 2014-08-13  9:12   ` Lee Jones
  0 siblings, 0 replies; 51+ messages in thread
From: Lee Jones @ 2014-08-13  9:12 UTC (permalink / raw)
  To: linux-arm-kernel

This is where we describe the different new and generic options used by
the ST BCH driver.

Cc: devicetree at vger.kernel.org
Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
 Documentation/devicetree/bindings/mtd/stm-nand.txt | 74 ++++++++++++++++++++++
 1 file changed, 74 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mtd/stm-nand.txt

diff --git a/Documentation/devicetree/bindings/mtd/stm-nand.txt b/Documentation/devicetree/bindings/mtd/stm-nand.txt
new file mode 100644
index 0000000..3176252
--- /dev/null
+++ b/Documentation/devicetree/bindings/mtd/stm-nand.txt
@@ -0,0 +1,74 @@
+STM BCH NAND Support
+--------------------
+
+Required properties:
+
+- compatible		: Should be "st,nand-bch"
+- reg			: Should contain register's location and length
+- reg-names		: "nand_mem" - NAND Controller register map
+			  "nand_dma" - BCH Controller DMA configuration map
+- interrupts		: Interrupt number
+- interrupt-names	: "nand_irq" - NAND Controller IRQ
+- st,nand-banks		: Subnode representing one or more "banks" of NAND
+			  Flash, connected to an STM NAND Controller (see
+			  description below).
+- nand-ecc-strength	: Generic NAND property (See mtd/nand.txt)
+			  Options are; 0, 18 or 30. If not present, the driver
+			  will choose the strongest scheme compatible if the
+			  OOB size.
+
+Properties describing Bank of NAND Flash ("st,nand-banks"):
+
+- st,nand-csn		: Chip select associated with the Bank.
+
+- st,nand-timing-relax	: [Optional] Number of IP clock cycles by which to
+			  "relax" timing configuration.  Required on some boards
+			  to accommodate board-level limitations. Applies to
+			  ONFI timing mode configuration.
+
+- nand-on-flash-bbt	: Generic NAND property (See mtd/nand.txt)
+
+- partitions		: [Optional] Subnode describing MTD partition map
+			  (see mtd/partition.txt)
+
+Note, during initialisation, the NAND Controller timing registers are configured
+according to one of the following methods, in order of precedence:
+
+	   1. Configuration based on ONFI timing mode, as advertised by the
+	      device during ONFI-probing (ONFI-compliant NAND only).
+
+	   2. Use reset/safe timing values
+
+Example:
+
+	nand at fe901000 {
+		compatible = "st,nand-bch";
+		reg = <0xfe901000 0x1000>, <0xfef00800 0x0800>;
+		reg-names = "nand_mem", "nand_dma";
+		interrupts = <0 139 0x0>;
+		interrupt-names = "nand_irq";
+		nand-ecc-strength = <30>;
+
+		status = "okay";
+
+		bank0 {
+			/* NAND_BBT_USE_FLASH */
+			nand-on-flash-bbt;
+			st,nand-csn		= <0>;
+
+			partitions {
+				#address-cells = <1>;
+				#size-cells = <1>;
+
+				partition at 0{
+					label = "NANDFlash1";
+					reg = <0x00000000 0x00800000>;
+				};
+
+				partition at 800000{
+					label = "NANDFlash2";
+					reg = <0x00800000 0x0f800000>;
+				};
+			};
+		};
+	};
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH 6/8] mtd: nand: add shared register defines for ST's NAND Controller drivers
  2014-08-13  9:11 ` Lee Jones
  (?)
@ 2014-08-13  9:12   ` Lee Jones
  -1 siblings, 0 replies; 51+ messages in thread
From: Lee Jones @ 2014-08-13  9:12 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: lee.jones, kernel, computersforpeace, pekon, linux-mtd

Provide register and bit definitions used by STM's BCH Controller driver.
We place these into a shared location as they will be used by other STM
NAND Controllers as they appear.

Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
 drivers/mtd/nand/stm_nand_regs.h | 304 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 304 insertions(+)
 create mode 100644 drivers/mtd/nand/stm_nand_regs.h

diff --git a/drivers/mtd/nand/stm_nand_regs.h b/drivers/mtd/nand/stm_nand_regs.h
new file mode 100644
index 0000000..fe28364
--- /dev/null
+++ b/drivers/mtd/nand/stm_nand_regs.h
@@ -0,0 +1,304 @@
+/*
+ * drivers/mtd/nand/stm_nand_regs.h
+ *
+ * STMicroelectronics NAND Controller register definitions
+ *
+ * Copyright (c) 2008-2014 STMicroelectronics Limited
+ * Author: Angus Clark <Angus.Clark@st.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef STM_NANDC_REGS_H
+#define STM_NANDC_REGS_H
+
+#define EMISS_CFG					0x1000
+
+/* Hamming Controller Registers (Offsets from EMINAND_BASE) */
+#define NANDHAM_BOOTBANK_CFG				0x000
+#define NANDHAM_RBN_STA					0x004
+#define NANDHAM_INT_EN					0x010
+#define NANDHAM_INT_STA					0x014
+#define NANDHAM_INT_CLR					0x018
+#define NANDHAM_INT_EDGE_CFG				0x01C
+#define NANDHAM_CTL_TIMING				0x040
+#define NANDHAM_WEN_TIMING				0x044
+#define NANDHAM_REN_TIMING				0x048
+#define NANDHAM_BLOCK_ZERO_REMAP_REG			0x04C
+#define NANDHAM_FLEXMODE_CFG				0x100
+#define NANDHAM_FLEX_MUXCTRL				0x104
+#define NANDHAM_FLEX_DATAWRITE_CONFIG			0x10C
+#define NANDHAM_FLEX_DATAREAD_CONFIG			0x110
+#define NANDHAM_FLEX_CMD				0x114
+#define NANDHAM_FLEX_ADD				0x118
+#define NANDHAM_FLEX_DATA				0x120
+#define NANDHAM_VERSION_REG				0x144
+#define NANDHAM_MULTI_CS_CONFIG_REG			0x1EC
+#define NANDHAM_AFM_SEQ_REG_1				0x200
+#define NANDHAM_AFM_SEQ_REG_2				0x204
+#define NANDHAM_AFM_SEQ_REG_3				0x208
+#define NANDHAM_AFM_SEQ_REG_4				0x20C
+#define NANDHAM_AFM_ADD					0x210
+#define NANDHAM_AFM_EXTRA				0x214
+#define NANDHAM_AFM_CMD					0x218
+#define NANDHAM_AFM_SEQ_CFG				0x21C
+#define NANDHAM_AFM_GEN_CFG				0x220
+#define NANDHAM_AFM_SEQ_STA				0x240
+#define NANDHAM_AFM_ECC_REG_0				0x280
+#define NANDHAM_AFM_ECC_REG_1				0x284
+#define NANDHAM_AFM_ECC_REG_2				0x288
+#define NANDHAM_AFM_ECC_REG_3				0x28C
+#define NANDHAM_AFM_DATA_FIFO				0x300
+
+/* BCH Controller Registers (Offsets from EMI_NAND) */
+#define NANDBCH_BOOTBANK_CFG				0x000
+#define NANDBCH_RBN_STA					0x004
+#define NANDBCH_INT_EN					0x010
+#define NANDBCH_INT_STA					0x014
+#define NANDBCH_INT_CLR					0x018
+#define NANDBCH_INT_EDGE_CFG				0x01C
+#define NANDBCH_CTL_TIMING				0x040
+#define NANDBCH_WEN_TIMING				0x044
+#define NANDBCH_REN_TIMING				0x048
+#define NANDBCH_BLOCK_ZERO_REMAP_REG			0x04C
+#define NANDBCH_BOOT_STATUS				0x050
+#define NANDBCH_FALSE_BOOT_REG				0x054
+#define NANDBCH_FALSE_BOOT_STATUS			0x058
+#define NANDBCH_CONTROLLER_CFG				0x100
+#define NANDBCH_FLEX_MUXCTRL				0x104
+#define NANDBCH_FLEX_DATAWRITE_CONFIG			0x10C
+#define NANDBCH_FLEX_DATAREAD_CONFIG			0x110
+#define NANDBCH_VERSION_REG				0x144
+#define NANDBCH_ADDRESS_REG_1				0x1F0
+#define NANDBCH_ADDRESS_REG_2				0x1F4
+#define NANDBCH_ADDRESS_REG_3				0x1F8
+#define NANDBCH_MULTI_CS_CONFIG_REG			0x1FC
+#define NANDBCH_SEQ_REG_1				0x200
+#define NANDBCH_SEQ_REG_2				0x204
+#define NANDBCH_SEQ_REG_3				0x208
+#define NANDBCH_SEQ_REG_4				0x20C
+#define NANDBCH_ADD					0x210
+#define NANDBCH_EXTRA_REG				0x214
+#define NANDBCH_CMD					0x218
+#define NANDBCH_GEN_CFG					0x220
+#define NANDBCH_DELAY_REG				0x224
+#define NANDBCH_SEQ_CFG					0x22C
+#define NANDBCH_SEQ_STA					0x270
+#define NANDBCH_DATA_BUFFER_ENTRY_0			0x280
+#define NANDBCH_DATA_BUFFER_ENTRY_1			0x284
+#define NANDBCH_DATA_BUFFER_ENTRY_2			0x288
+#define NANDBCH_DATA_BUFFER_ENTRY_3			0x28C
+#define NANDBCH_DATA_BUFFER_ENTRY_4			0x290
+#define NANDBCH_DATA_BUFFER_ENTRY_5			0x294
+#define NANDBCH_DATA_BUFFER_ENTRY_6			0x298
+#define NANDBCH_DATA_BUFFER_ENTRY_7			0x29C
+#define NANDBCH_ECC_SCORE_REG_A				0x2A0
+#define NANDBCH_ECC_SCORE_REG_B				0x2A4
+#define NANDBCH_CHECK_STATUS_REG_A			0x2A8
+#define NANDBCH_CHECK_STATUS_REG_B			0x2AC
+#define NANDBCH_BUFFER_LIST_PTR				0x300
+#define NANDBCH_SEQ_PTR_REG				0x304
+#define NANDBCH_ERROR_THRESHOLD_REG			0x308
+
+#define EMISS_NAND_DMA					0x0800
+/* EMISS NAND BCH STPLUG Registers (Offsets from EMISS_NAND_DMA) */
+#define EMISS_NAND_RD_DMA_PAGE_SIZE			0x000
+#define EMISS_NAND_RD_DMA_MAX_OPCODE_SIZE		0x004
+#define EMISS_NAND_RD_DMA_MIN_OPCODE_SIZE		0x008
+#define EMISS_NAND_RD_DMA_MAX_CHUNK_SIZE		0x00C
+#define EMISS_NAND_RD_DMA_MAX_MESSAGE_SIZE		0x010
+
+#define EMISS_NAND_WR_DMA_PAGE_SIZE			0x100
+#define EMISS_NAND_WR_DMA_MAX_OPCODE_SIZE		0x104
+#define EMISS_NAND_WR_DMA_MIN_OPCODE_SIZE		0x108
+#define EMISS_NAND_WR_DMA_MAX_CHUNK_SIZE		0x10C
+#define EMISS_NAND_WR_DMA_MAX_MESSAGE_SIZE		0x110
+
+/*
+ * Hamming/BCH controller interrupts
+ */
+
+/* NANDxxx_INT_EN/NANDxxx_INT_STA */
+/*      Common */
+#define NAND_INT_ENABLE				(0x1 << 0)
+#define NAND_INT_RBN				(0x1 << 2)
+#define NAND_INT_SEQCHECK			(0x1 << 5)
+/*      Hamming only */
+#define NANDHAM_INT_DATA_DREQ			(0x1 << 3)
+#define NANDHAM_INT_SEQ_DREQ			(0x1 << 4)
+#define NANDHAM_INT_ECC_FIX_REQ			(0x1 << 6)
+/*      BCH only */
+#define NANDBCH_INT_SEQNODESOVER		(0x1 << 7)
+#define NANDBCH_INT_ECCTHRESHOLD		(0x1 << 8)
+
+/* NANDxxx_INT_CLR */
+/*      Common */
+#define NAND_INT_CLR_RBN			(0x1 << 2)
+#define NAND_INT_CLR_SEQCHECK			(0x1 << 3)
+/*      Hamming only */
+#define NANDHAM_INT_CLR_ECC_FIX_REQ		(0x1 << 4)
+#define NANDHAM_INT_CLR_DATA_DREQ		(0x1 << 5)
+#define NANDHAM_INT_CLR_SEQ_DREQ		(0x1 << 6)
+/*      BCH only */
+#define NANDBCH_INT_CLR_SEQNODESOVER		(0x1 << 5)
+#define NANDBCH_INT_CLR_ECCTHRESHOLD		(0x1 << 6)
+
+/* NANDxxx_INT_EDGE_CFG */
+#define NAND_EDGE_CFG_RBN_RISING		0x1
+#define NAND_EDGE_CFG_RBN_FALLING		0x2
+#define NAND_EDGE_CFG_RBN_ANY			0x3
+
+/* NANDBCH_CONTROLLER_CFG/NANDHAM_FLEXMODE_CFG */
+#define CFG_ENABLE_FLEX				0x1
+#define CFG_ENABLE_AFM				0x2
+#define CFG_RESET				(0x1 << 3)
+#define CFG_RESET_ECC(x)			(0x1 << (7 + (x)))
+#define CFG_RESET_ECC_ALL			(0xff << 7)
+
+
+/*
+ * BCH Controller
+ */
+
+/* NANDBCH_BOOTBANK_CFG */
+#define BOOT_CFG_RESET				(0x1 << 3)
+
+/* NANDBCH_CTL_TIMING */
+#define NANDBCH_CTL_SETUP(x)			((x) & 0xff)
+#define NANDBCH_CTL_HOLD(x)			(((x) & 0xff) << 8)
+#define NANDBCH_CTL_WERBN(x)			(((x) & 0xff) << 24)
+
+/* NANDBCH_WEN_TIMING */
+#define NANDBCH_WEN_ONTIME(x)			((x) & 0xff)
+#define NANDBCH_WEN_OFFTIME(x)			(((x) & 0xff) << 8)
+#define NANDBCH_WEN_ONHALFCYCLE			(0x1 << 16)
+#define NANDBCH_WEN_OFFHALFCYCLE		(0x1 << 17)
+
+/* NANDBCH_REN_TIMING */
+#define NANDBCH_REN_ONTIME(x)			((x) & 0xff)
+#define NANDBCH_REN_OFFTIME(x)			(((x) & 0xff) << 8)
+#define NANDBCH_REN_ONHALFCYCLE			(0x1 << 16)
+#define NANDBCH_REN_OFFHALFCYCLE		(0x1 << 17)
+#define NANDBCH_REN_TELQV(x)			(((x) & 0xff) << 24)
+
+/* NANDBCH_BLOCK_ZERO_REMAP_REG */
+#define NANDBCH_BACKUP_COPY_FOUND		(0x1 << 0)
+#define NANDBCH_ORIG_CODE_CORRUPTED		(0x1 << 1)
+#define NANDBCH_BLK_ZERO_REMAP(x)		((x) >> 14)
+
+/* NANDBCH_BOOT_STATUS */
+#define NANDBCH_BOOT_MAX_ERRORS(x)		((x) & 0x1f)
+
+/* NANDBCH_GEN_CFG */
+#define GEN_CFG_DATA_8_NOT_16			(0x1 << 16)
+#define GEN_CFG_EXTRA_ADD_CYCLE			(0x1 << 18)
+#define GEN_CFG_2X8_MODE			(0x1 << 19)
+#define GEN_CFG_ECC_SHIFT			20
+#define GEN_CFG_18BIT_ECC			(BCH_18BIT_ECC << \
+						GEN_CFG_ECC_SHIFT)
+#define GEN_CFG_30BIT_ECC			(BCH_30BIT_ECC << \
+						GEN_CFG_ECC_SHIFT)
+#define GEN_CFG_NO_ECC				(BCH_NO_ECC    << \
+						GEN_CFG_ECC_SHIFT)
+#define GEN_CFG_LAST_SEQ_NODE			(0x1 << 22)
+
+/* NANDBCH_SEQ_CFG */
+#define SEQ_CFG_REPEAT_COUNTER(x)		((x) & 0xffff)
+#define SEQ_CFG_SEQ_IDENT(x)			(((x) & 0xff) << 16)
+#define SEQ_CFG_DATA_WRITE			(0x1 << 24)
+#define SEQ_CFG_ERASE				(0x1 << 25)
+#define SEQ_CFG_GO_STOP				(0x1 << 26)
+
+/* NANDBCH_SEQ_STA */
+#define SEQ_STA_RUN				(0x1 << 4)
+
+/*
+ * BCH Commands
+ */
+#define BCH_OPC_STOP			0x0
+#define BCH_OPC_CMD			0x1
+#define BCH_OPC_INC			0x2
+#define BCH_OPC_DEC_JUMP		0x3
+#define BCH_OPC_DATA			0x4
+#define BCH_OPC_DELAY			0x5
+#define BCH_OPC_CHECK			0x6
+#define BCH_OPC_ADDR			0x7
+#define BCH_OPC_NEXT_CHIP_ON		0x8
+#define BCH_OPC_DEC_JMP_MCS		0x9
+#define BCH_OPC_ECC_SCORE		0xA
+
+#define BCH_INSTR(opc, opr)		((opc) | ((opr) << 4))
+
+#define BCH_CMD_ADDR			BCH_INSTR(BCH_OPC_CMD, 0)
+#define BCH_CL_CMD_1			BCH_INSTR(BCH_OPC_CMD, 1)
+#define BCH_CL_CMD_2			BCH_INSTR(BCH_OPC_CMD, 2)
+#define BCH_CL_CMD_3			BCH_INSTR(BCH_OPC_CMD, 3)
+#define BCH_CL_EX_0			BCH_INSTR(BCH_OPC_CMD, 4)
+#define BCH_CL_EX_1			BCH_INSTR(BCH_OPC_CMD, 5)
+#define BCH_CL_EX_2			BCH_INSTR(BCH_OPC_CMD, 6)
+#define BCH_CL_EX_3			BCH_INSTR(BCH_OPC_CMD, 7)
+#define BCH_INC(x)			BCH_INSTR(BCH_OPC_INC, (x))
+#define BCH_DEC_JUMP(x)			BCH_INSTR(BCH_OPC_DEC_JUMP, (x))
+#define BCH_STOP			BCH_INSTR(BCH_OPC_STOP, 0)
+#define BCH_DATA_1_SECTOR		BCH_INSTR(BCH_OPC_DATA, 0)
+#define BCH_DATA_2_SECTOR		BCH_INSTR(BCH_OPC_DATA, 1)
+#define BCH_DATA_4_SECTOR		BCH_INSTR(BCH_OPC_DATA, 2)
+#define BCH_DATA_8_SECTOR		BCH_INSTR(BCH_OPC_DATA, 3)
+#define BCH_DATA_16_SECTOR		BCH_INSTR(BCH_OPC_DATA, 4)
+#define BCH_DATA_32_SECTOR		BCH_INSTR(BCH_OPC_DATA, 5)
+#define BCH_DELAY_0			BCH_INSTR(BCH_OPC_DELAY, 0)
+#define BCH_DELAY_1			BCH_INSTR(BCH_OPC_DELAY, 1)
+#define BCH_OP_ERR			BCH_INSTR(BCH_OPC_CHECK, 0)
+#define BCH_CACHE_ERR			BCH_INSTR(BCH_OPC_CHECK, 1)
+#define BCH_ERROR			BCH_INSTR(BCH_OPC_CHECK, 2)
+#define BCH_AL_EX_0			BCH_INSTR(BCH_OPC_ADDR, 0)
+#define BCH_AL_EX_1			BCH_INSTR(BCH_OPC_ADDR, 1)
+#define BCH_AL_EX_2			BCH_INSTR(BCH_OPC_ADDR, 2)
+#define BCH_AL_EX_3			BCH_INSTR(BCH_OPC_ADDR, 3)
+#define BCH_AL_AD_0			BCH_INSTR(BCH_OPC_ADDR, 4)
+#define BCH_AL_AD_1			BCH_INSTR(BCH_OPC_ADDR, 5)
+#define BCH_AL_AD_2			BCH_INSTR(BCH_OPC_ADDR, 6)
+#define BCH_AL_AD_3			BCH_INSTR(BCH_OPC_ADDR, 7)
+#define BCH_NEXT_CHIP_ON		BCH_INSTR(BCH_OPC_NEXT_CHIP_ON, 0)
+#define BCH_DEC_JMP_MCS(x)		BCH_INSTR(BCH_OPC_DEC_JMP_MCS, (x))
+#define BCH_ECC_SCORE(x)		BCH_INSTR(BCH_OPC_ECC_SCORE, (x))
+
+
+/*
+ * Hamming-FLEX register fields
+ */
+
+/* NANDHAM_FLEX_DATAREAD/WRITE_CONFIG */
+#define FLEX_DATA_CFG_WAIT_RBN			(0x1 << 27)
+#define FLEX_DATA_CFG_BEATS_1			(0x1 << 28)
+#define FLEX_DATA_CFG_BEATS_2			(0x2 << 28)
+#define FLEX_DATA_CFG_BEATS_3			(0x3 << 28)
+#define FLEX_DATA_CFG_BEATS_4			(0x0 << 28)
+#define FLEX_DATA_CFG_BYTES_1			(0x0 << 30)
+#define FLEX_DATA_CFG_BYTES_2			(0x1 << 30)
+#define FLEX_DATA_CFG_CSN			(0x1 << 31)
+
+/* NANDHAM_FLEX_CMD */
+#define FLEX_CMD_RBN				(0x1 << 27)
+#define FLEX_CMD_BEATS_1			(0x1 << 28)
+#define FLEX_CMD_BEATS_2			(0x2 << 28)
+#define FLEX_CMD_BEATS_3			(0x3 << 28)
+#define FLEX_CMD_BEATS_4			(0x0 << 28)
+#define FLEX_CMD_CSN				(0x1 << 31)
+#define FLEX_CMD(x)				(((x) & 0xff) |	\
+						 FLEX_CMD_RBN |	\
+						 FLEX_CMD_BEATS_1 |	\
+						 FLEX_CMD_CSN)
+/* NANDHAM_FLEX_ADD */
+#define FLEX_ADDR_RBN				(0x1 << 27)
+#define FLEX_ADDR_BEATS_1			(0x1 << 28)
+#define FLEX_ADDR_BEATS_2			(0x2 << 28)
+#define FLEX_ADDR_BEATS_3			(0x3 << 28)
+#define FLEX_ADDR_BEATS_4			(0x0 << 28)
+#define FLEX_ADDR_ADD8_VALID			(0x1 << 30)
+#define FLEX_ADDR_CSN				(0x1 << 31)
+
+#endif /* STM_NANDC_REGS_H */
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH 6/8] mtd: nand: add shared register defines for ST's NAND Controller drivers
@ 2014-08-13  9:12   ` Lee Jones
  0 siblings, 0 replies; 51+ messages in thread
From: Lee Jones @ 2014-08-13  9:12 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: pekon, computersforpeace, lee.jones, kernel, linux-mtd

Provide register and bit definitions used by STM's BCH Controller driver.
We place these into a shared location as they will be used by other STM
NAND Controllers as they appear.

Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
 drivers/mtd/nand/stm_nand_regs.h | 304 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 304 insertions(+)
 create mode 100644 drivers/mtd/nand/stm_nand_regs.h

diff --git a/drivers/mtd/nand/stm_nand_regs.h b/drivers/mtd/nand/stm_nand_regs.h
new file mode 100644
index 0000000..fe28364
--- /dev/null
+++ b/drivers/mtd/nand/stm_nand_regs.h
@@ -0,0 +1,304 @@
+/*
+ * drivers/mtd/nand/stm_nand_regs.h
+ *
+ * STMicroelectronics NAND Controller register definitions
+ *
+ * Copyright (c) 2008-2014 STMicroelectronics Limited
+ * Author: Angus Clark <Angus.Clark@st.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef STM_NANDC_REGS_H
+#define STM_NANDC_REGS_H
+
+#define EMISS_CFG					0x1000
+
+/* Hamming Controller Registers (Offsets from EMINAND_BASE) */
+#define NANDHAM_BOOTBANK_CFG				0x000
+#define NANDHAM_RBN_STA					0x004
+#define NANDHAM_INT_EN					0x010
+#define NANDHAM_INT_STA					0x014
+#define NANDHAM_INT_CLR					0x018
+#define NANDHAM_INT_EDGE_CFG				0x01C
+#define NANDHAM_CTL_TIMING				0x040
+#define NANDHAM_WEN_TIMING				0x044
+#define NANDHAM_REN_TIMING				0x048
+#define NANDHAM_BLOCK_ZERO_REMAP_REG			0x04C
+#define NANDHAM_FLEXMODE_CFG				0x100
+#define NANDHAM_FLEX_MUXCTRL				0x104
+#define NANDHAM_FLEX_DATAWRITE_CONFIG			0x10C
+#define NANDHAM_FLEX_DATAREAD_CONFIG			0x110
+#define NANDHAM_FLEX_CMD				0x114
+#define NANDHAM_FLEX_ADD				0x118
+#define NANDHAM_FLEX_DATA				0x120
+#define NANDHAM_VERSION_REG				0x144
+#define NANDHAM_MULTI_CS_CONFIG_REG			0x1EC
+#define NANDHAM_AFM_SEQ_REG_1				0x200
+#define NANDHAM_AFM_SEQ_REG_2				0x204
+#define NANDHAM_AFM_SEQ_REG_3				0x208
+#define NANDHAM_AFM_SEQ_REG_4				0x20C
+#define NANDHAM_AFM_ADD					0x210
+#define NANDHAM_AFM_EXTRA				0x214
+#define NANDHAM_AFM_CMD					0x218
+#define NANDHAM_AFM_SEQ_CFG				0x21C
+#define NANDHAM_AFM_GEN_CFG				0x220
+#define NANDHAM_AFM_SEQ_STA				0x240
+#define NANDHAM_AFM_ECC_REG_0				0x280
+#define NANDHAM_AFM_ECC_REG_1				0x284
+#define NANDHAM_AFM_ECC_REG_2				0x288
+#define NANDHAM_AFM_ECC_REG_3				0x28C
+#define NANDHAM_AFM_DATA_FIFO				0x300
+
+/* BCH Controller Registers (Offsets from EMI_NAND) */
+#define NANDBCH_BOOTBANK_CFG				0x000
+#define NANDBCH_RBN_STA					0x004
+#define NANDBCH_INT_EN					0x010
+#define NANDBCH_INT_STA					0x014
+#define NANDBCH_INT_CLR					0x018
+#define NANDBCH_INT_EDGE_CFG				0x01C
+#define NANDBCH_CTL_TIMING				0x040
+#define NANDBCH_WEN_TIMING				0x044
+#define NANDBCH_REN_TIMING				0x048
+#define NANDBCH_BLOCK_ZERO_REMAP_REG			0x04C
+#define NANDBCH_BOOT_STATUS				0x050
+#define NANDBCH_FALSE_BOOT_REG				0x054
+#define NANDBCH_FALSE_BOOT_STATUS			0x058
+#define NANDBCH_CONTROLLER_CFG				0x100
+#define NANDBCH_FLEX_MUXCTRL				0x104
+#define NANDBCH_FLEX_DATAWRITE_CONFIG			0x10C
+#define NANDBCH_FLEX_DATAREAD_CONFIG			0x110
+#define NANDBCH_VERSION_REG				0x144
+#define NANDBCH_ADDRESS_REG_1				0x1F0
+#define NANDBCH_ADDRESS_REG_2				0x1F4
+#define NANDBCH_ADDRESS_REG_3				0x1F8
+#define NANDBCH_MULTI_CS_CONFIG_REG			0x1FC
+#define NANDBCH_SEQ_REG_1				0x200
+#define NANDBCH_SEQ_REG_2				0x204
+#define NANDBCH_SEQ_REG_3				0x208
+#define NANDBCH_SEQ_REG_4				0x20C
+#define NANDBCH_ADD					0x210
+#define NANDBCH_EXTRA_REG				0x214
+#define NANDBCH_CMD					0x218
+#define NANDBCH_GEN_CFG					0x220
+#define NANDBCH_DELAY_REG				0x224
+#define NANDBCH_SEQ_CFG					0x22C
+#define NANDBCH_SEQ_STA					0x270
+#define NANDBCH_DATA_BUFFER_ENTRY_0			0x280
+#define NANDBCH_DATA_BUFFER_ENTRY_1			0x284
+#define NANDBCH_DATA_BUFFER_ENTRY_2			0x288
+#define NANDBCH_DATA_BUFFER_ENTRY_3			0x28C
+#define NANDBCH_DATA_BUFFER_ENTRY_4			0x290
+#define NANDBCH_DATA_BUFFER_ENTRY_5			0x294
+#define NANDBCH_DATA_BUFFER_ENTRY_6			0x298
+#define NANDBCH_DATA_BUFFER_ENTRY_7			0x29C
+#define NANDBCH_ECC_SCORE_REG_A				0x2A0
+#define NANDBCH_ECC_SCORE_REG_B				0x2A4
+#define NANDBCH_CHECK_STATUS_REG_A			0x2A8
+#define NANDBCH_CHECK_STATUS_REG_B			0x2AC
+#define NANDBCH_BUFFER_LIST_PTR				0x300
+#define NANDBCH_SEQ_PTR_REG				0x304
+#define NANDBCH_ERROR_THRESHOLD_REG			0x308
+
+#define EMISS_NAND_DMA					0x0800
+/* EMISS NAND BCH STPLUG Registers (Offsets from EMISS_NAND_DMA) */
+#define EMISS_NAND_RD_DMA_PAGE_SIZE			0x000
+#define EMISS_NAND_RD_DMA_MAX_OPCODE_SIZE		0x004
+#define EMISS_NAND_RD_DMA_MIN_OPCODE_SIZE		0x008
+#define EMISS_NAND_RD_DMA_MAX_CHUNK_SIZE		0x00C
+#define EMISS_NAND_RD_DMA_MAX_MESSAGE_SIZE		0x010
+
+#define EMISS_NAND_WR_DMA_PAGE_SIZE			0x100
+#define EMISS_NAND_WR_DMA_MAX_OPCODE_SIZE		0x104
+#define EMISS_NAND_WR_DMA_MIN_OPCODE_SIZE		0x108
+#define EMISS_NAND_WR_DMA_MAX_CHUNK_SIZE		0x10C
+#define EMISS_NAND_WR_DMA_MAX_MESSAGE_SIZE		0x110
+
+/*
+ * Hamming/BCH controller interrupts
+ */
+
+/* NANDxxx_INT_EN/NANDxxx_INT_STA */
+/*      Common */
+#define NAND_INT_ENABLE				(0x1 << 0)
+#define NAND_INT_RBN				(0x1 << 2)
+#define NAND_INT_SEQCHECK			(0x1 << 5)
+/*      Hamming only */
+#define NANDHAM_INT_DATA_DREQ			(0x1 << 3)
+#define NANDHAM_INT_SEQ_DREQ			(0x1 << 4)
+#define NANDHAM_INT_ECC_FIX_REQ			(0x1 << 6)
+/*      BCH only */
+#define NANDBCH_INT_SEQNODESOVER		(0x1 << 7)
+#define NANDBCH_INT_ECCTHRESHOLD		(0x1 << 8)
+
+/* NANDxxx_INT_CLR */
+/*      Common */
+#define NAND_INT_CLR_RBN			(0x1 << 2)
+#define NAND_INT_CLR_SEQCHECK			(0x1 << 3)
+/*      Hamming only */
+#define NANDHAM_INT_CLR_ECC_FIX_REQ		(0x1 << 4)
+#define NANDHAM_INT_CLR_DATA_DREQ		(0x1 << 5)
+#define NANDHAM_INT_CLR_SEQ_DREQ		(0x1 << 6)
+/*      BCH only */
+#define NANDBCH_INT_CLR_SEQNODESOVER		(0x1 << 5)
+#define NANDBCH_INT_CLR_ECCTHRESHOLD		(0x1 << 6)
+
+/* NANDxxx_INT_EDGE_CFG */
+#define NAND_EDGE_CFG_RBN_RISING		0x1
+#define NAND_EDGE_CFG_RBN_FALLING		0x2
+#define NAND_EDGE_CFG_RBN_ANY			0x3
+
+/* NANDBCH_CONTROLLER_CFG/NANDHAM_FLEXMODE_CFG */
+#define CFG_ENABLE_FLEX				0x1
+#define CFG_ENABLE_AFM				0x2
+#define CFG_RESET				(0x1 << 3)
+#define CFG_RESET_ECC(x)			(0x1 << (7 + (x)))
+#define CFG_RESET_ECC_ALL			(0xff << 7)
+
+
+/*
+ * BCH Controller
+ */
+
+/* NANDBCH_BOOTBANK_CFG */
+#define BOOT_CFG_RESET				(0x1 << 3)
+
+/* NANDBCH_CTL_TIMING */
+#define NANDBCH_CTL_SETUP(x)			((x) & 0xff)
+#define NANDBCH_CTL_HOLD(x)			(((x) & 0xff) << 8)
+#define NANDBCH_CTL_WERBN(x)			(((x) & 0xff) << 24)
+
+/* NANDBCH_WEN_TIMING */
+#define NANDBCH_WEN_ONTIME(x)			((x) & 0xff)
+#define NANDBCH_WEN_OFFTIME(x)			(((x) & 0xff) << 8)
+#define NANDBCH_WEN_ONHALFCYCLE			(0x1 << 16)
+#define NANDBCH_WEN_OFFHALFCYCLE		(0x1 << 17)
+
+/* NANDBCH_REN_TIMING */
+#define NANDBCH_REN_ONTIME(x)			((x) & 0xff)
+#define NANDBCH_REN_OFFTIME(x)			(((x) & 0xff) << 8)
+#define NANDBCH_REN_ONHALFCYCLE			(0x1 << 16)
+#define NANDBCH_REN_OFFHALFCYCLE		(0x1 << 17)
+#define NANDBCH_REN_TELQV(x)			(((x) & 0xff) << 24)
+
+/* NANDBCH_BLOCK_ZERO_REMAP_REG */
+#define NANDBCH_BACKUP_COPY_FOUND		(0x1 << 0)
+#define NANDBCH_ORIG_CODE_CORRUPTED		(0x1 << 1)
+#define NANDBCH_BLK_ZERO_REMAP(x)		((x) >> 14)
+
+/* NANDBCH_BOOT_STATUS */
+#define NANDBCH_BOOT_MAX_ERRORS(x)		((x) & 0x1f)
+
+/* NANDBCH_GEN_CFG */
+#define GEN_CFG_DATA_8_NOT_16			(0x1 << 16)
+#define GEN_CFG_EXTRA_ADD_CYCLE			(0x1 << 18)
+#define GEN_CFG_2X8_MODE			(0x1 << 19)
+#define GEN_CFG_ECC_SHIFT			20
+#define GEN_CFG_18BIT_ECC			(BCH_18BIT_ECC << \
+						GEN_CFG_ECC_SHIFT)
+#define GEN_CFG_30BIT_ECC			(BCH_30BIT_ECC << \
+						GEN_CFG_ECC_SHIFT)
+#define GEN_CFG_NO_ECC				(BCH_NO_ECC    << \
+						GEN_CFG_ECC_SHIFT)
+#define GEN_CFG_LAST_SEQ_NODE			(0x1 << 22)
+
+/* NANDBCH_SEQ_CFG */
+#define SEQ_CFG_REPEAT_COUNTER(x)		((x) & 0xffff)
+#define SEQ_CFG_SEQ_IDENT(x)			(((x) & 0xff) << 16)
+#define SEQ_CFG_DATA_WRITE			(0x1 << 24)
+#define SEQ_CFG_ERASE				(0x1 << 25)
+#define SEQ_CFG_GO_STOP				(0x1 << 26)
+
+/* NANDBCH_SEQ_STA */
+#define SEQ_STA_RUN				(0x1 << 4)
+
+/*
+ * BCH Commands
+ */
+#define BCH_OPC_STOP			0x0
+#define BCH_OPC_CMD			0x1
+#define BCH_OPC_INC			0x2
+#define BCH_OPC_DEC_JUMP		0x3
+#define BCH_OPC_DATA			0x4
+#define BCH_OPC_DELAY			0x5
+#define BCH_OPC_CHECK			0x6
+#define BCH_OPC_ADDR			0x7
+#define BCH_OPC_NEXT_CHIP_ON		0x8
+#define BCH_OPC_DEC_JMP_MCS		0x9
+#define BCH_OPC_ECC_SCORE		0xA
+
+#define BCH_INSTR(opc, opr)		((opc) | ((opr) << 4))
+
+#define BCH_CMD_ADDR			BCH_INSTR(BCH_OPC_CMD, 0)
+#define BCH_CL_CMD_1			BCH_INSTR(BCH_OPC_CMD, 1)
+#define BCH_CL_CMD_2			BCH_INSTR(BCH_OPC_CMD, 2)
+#define BCH_CL_CMD_3			BCH_INSTR(BCH_OPC_CMD, 3)
+#define BCH_CL_EX_0			BCH_INSTR(BCH_OPC_CMD, 4)
+#define BCH_CL_EX_1			BCH_INSTR(BCH_OPC_CMD, 5)
+#define BCH_CL_EX_2			BCH_INSTR(BCH_OPC_CMD, 6)
+#define BCH_CL_EX_3			BCH_INSTR(BCH_OPC_CMD, 7)
+#define BCH_INC(x)			BCH_INSTR(BCH_OPC_INC, (x))
+#define BCH_DEC_JUMP(x)			BCH_INSTR(BCH_OPC_DEC_JUMP, (x))
+#define BCH_STOP			BCH_INSTR(BCH_OPC_STOP, 0)
+#define BCH_DATA_1_SECTOR		BCH_INSTR(BCH_OPC_DATA, 0)
+#define BCH_DATA_2_SECTOR		BCH_INSTR(BCH_OPC_DATA, 1)
+#define BCH_DATA_4_SECTOR		BCH_INSTR(BCH_OPC_DATA, 2)
+#define BCH_DATA_8_SECTOR		BCH_INSTR(BCH_OPC_DATA, 3)
+#define BCH_DATA_16_SECTOR		BCH_INSTR(BCH_OPC_DATA, 4)
+#define BCH_DATA_32_SECTOR		BCH_INSTR(BCH_OPC_DATA, 5)
+#define BCH_DELAY_0			BCH_INSTR(BCH_OPC_DELAY, 0)
+#define BCH_DELAY_1			BCH_INSTR(BCH_OPC_DELAY, 1)
+#define BCH_OP_ERR			BCH_INSTR(BCH_OPC_CHECK, 0)
+#define BCH_CACHE_ERR			BCH_INSTR(BCH_OPC_CHECK, 1)
+#define BCH_ERROR			BCH_INSTR(BCH_OPC_CHECK, 2)
+#define BCH_AL_EX_0			BCH_INSTR(BCH_OPC_ADDR, 0)
+#define BCH_AL_EX_1			BCH_INSTR(BCH_OPC_ADDR, 1)
+#define BCH_AL_EX_2			BCH_INSTR(BCH_OPC_ADDR, 2)
+#define BCH_AL_EX_3			BCH_INSTR(BCH_OPC_ADDR, 3)
+#define BCH_AL_AD_0			BCH_INSTR(BCH_OPC_ADDR, 4)
+#define BCH_AL_AD_1			BCH_INSTR(BCH_OPC_ADDR, 5)
+#define BCH_AL_AD_2			BCH_INSTR(BCH_OPC_ADDR, 6)
+#define BCH_AL_AD_3			BCH_INSTR(BCH_OPC_ADDR, 7)
+#define BCH_NEXT_CHIP_ON		BCH_INSTR(BCH_OPC_NEXT_CHIP_ON, 0)
+#define BCH_DEC_JMP_MCS(x)		BCH_INSTR(BCH_OPC_DEC_JMP_MCS, (x))
+#define BCH_ECC_SCORE(x)		BCH_INSTR(BCH_OPC_ECC_SCORE, (x))
+
+
+/*
+ * Hamming-FLEX register fields
+ */
+
+/* NANDHAM_FLEX_DATAREAD/WRITE_CONFIG */
+#define FLEX_DATA_CFG_WAIT_RBN			(0x1 << 27)
+#define FLEX_DATA_CFG_BEATS_1			(0x1 << 28)
+#define FLEX_DATA_CFG_BEATS_2			(0x2 << 28)
+#define FLEX_DATA_CFG_BEATS_3			(0x3 << 28)
+#define FLEX_DATA_CFG_BEATS_4			(0x0 << 28)
+#define FLEX_DATA_CFG_BYTES_1			(0x0 << 30)
+#define FLEX_DATA_CFG_BYTES_2			(0x1 << 30)
+#define FLEX_DATA_CFG_CSN			(0x1 << 31)
+
+/* NANDHAM_FLEX_CMD */
+#define FLEX_CMD_RBN				(0x1 << 27)
+#define FLEX_CMD_BEATS_1			(0x1 << 28)
+#define FLEX_CMD_BEATS_2			(0x2 << 28)
+#define FLEX_CMD_BEATS_3			(0x3 << 28)
+#define FLEX_CMD_BEATS_4			(0x0 << 28)
+#define FLEX_CMD_CSN				(0x1 << 31)
+#define FLEX_CMD(x)				(((x) & 0xff) |	\
+						 FLEX_CMD_RBN |	\
+						 FLEX_CMD_BEATS_1 |	\
+						 FLEX_CMD_CSN)
+/* NANDHAM_FLEX_ADD */
+#define FLEX_ADDR_RBN				(0x1 << 27)
+#define FLEX_ADDR_BEATS_1			(0x1 << 28)
+#define FLEX_ADDR_BEATS_2			(0x2 << 28)
+#define FLEX_ADDR_BEATS_3			(0x3 << 28)
+#define FLEX_ADDR_BEATS_4			(0x0 << 28)
+#define FLEX_ADDR_ADD8_VALID			(0x1 << 30)
+#define FLEX_ADDR_CSN				(0x1 << 31)
+
+#endif /* STM_NANDC_REGS_H */
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH 6/8] mtd: nand: add shared register defines for ST's NAND Controller drivers
@ 2014-08-13  9:12   ` Lee Jones
  0 siblings, 0 replies; 51+ messages in thread
From: Lee Jones @ 2014-08-13  9:12 UTC (permalink / raw)
  To: linux-arm-kernel

Provide register and bit definitions used by STM's BCH Controller driver.
We place these into a shared location as they will be used by other STM
NAND Controllers as they appear.

Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
 drivers/mtd/nand/stm_nand_regs.h | 304 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 304 insertions(+)
 create mode 100644 drivers/mtd/nand/stm_nand_regs.h

diff --git a/drivers/mtd/nand/stm_nand_regs.h b/drivers/mtd/nand/stm_nand_regs.h
new file mode 100644
index 0000000..fe28364
--- /dev/null
+++ b/drivers/mtd/nand/stm_nand_regs.h
@@ -0,0 +1,304 @@
+/*
+ * drivers/mtd/nand/stm_nand_regs.h
+ *
+ * STMicroelectronics NAND Controller register definitions
+ *
+ * Copyright (c) 2008-2014 STMicroelectronics Limited
+ * Author: Angus Clark <Angus.Clark@st.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef STM_NANDC_REGS_H
+#define STM_NANDC_REGS_H
+
+#define EMISS_CFG					0x1000
+
+/* Hamming Controller Registers (Offsets from EMINAND_BASE) */
+#define NANDHAM_BOOTBANK_CFG				0x000
+#define NANDHAM_RBN_STA					0x004
+#define NANDHAM_INT_EN					0x010
+#define NANDHAM_INT_STA					0x014
+#define NANDHAM_INT_CLR					0x018
+#define NANDHAM_INT_EDGE_CFG				0x01C
+#define NANDHAM_CTL_TIMING				0x040
+#define NANDHAM_WEN_TIMING				0x044
+#define NANDHAM_REN_TIMING				0x048
+#define NANDHAM_BLOCK_ZERO_REMAP_REG			0x04C
+#define NANDHAM_FLEXMODE_CFG				0x100
+#define NANDHAM_FLEX_MUXCTRL				0x104
+#define NANDHAM_FLEX_DATAWRITE_CONFIG			0x10C
+#define NANDHAM_FLEX_DATAREAD_CONFIG			0x110
+#define NANDHAM_FLEX_CMD				0x114
+#define NANDHAM_FLEX_ADD				0x118
+#define NANDHAM_FLEX_DATA				0x120
+#define NANDHAM_VERSION_REG				0x144
+#define NANDHAM_MULTI_CS_CONFIG_REG			0x1EC
+#define NANDHAM_AFM_SEQ_REG_1				0x200
+#define NANDHAM_AFM_SEQ_REG_2				0x204
+#define NANDHAM_AFM_SEQ_REG_3				0x208
+#define NANDHAM_AFM_SEQ_REG_4				0x20C
+#define NANDHAM_AFM_ADD					0x210
+#define NANDHAM_AFM_EXTRA				0x214
+#define NANDHAM_AFM_CMD					0x218
+#define NANDHAM_AFM_SEQ_CFG				0x21C
+#define NANDHAM_AFM_GEN_CFG				0x220
+#define NANDHAM_AFM_SEQ_STA				0x240
+#define NANDHAM_AFM_ECC_REG_0				0x280
+#define NANDHAM_AFM_ECC_REG_1				0x284
+#define NANDHAM_AFM_ECC_REG_2				0x288
+#define NANDHAM_AFM_ECC_REG_3				0x28C
+#define NANDHAM_AFM_DATA_FIFO				0x300
+
+/* BCH Controller Registers (Offsets from EMI_NAND) */
+#define NANDBCH_BOOTBANK_CFG				0x000
+#define NANDBCH_RBN_STA					0x004
+#define NANDBCH_INT_EN					0x010
+#define NANDBCH_INT_STA					0x014
+#define NANDBCH_INT_CLR					0x018
+#define NANDBCH_INT_EDGE_CFG				0x01C
+#define NANDBCH_CTL_TIMING				0x040
+#define NANDBCH_WEN_TIMING				0x044
+#define NANDBCH_REN_TIMING				0x048
+#define NANDBCH_BLOCK_ZERO_REMAP_REG			0x04C
+#define NANDBCH_BOOT_STATUS				0x050
+#define NANDBCH_FALSE_BOOT_REG				0x054
+#define NANDBCH_FALSE_BOOT_STATUS			0x058
+#define NANDBCH_CONTROLLER_CFG				0x100
+#define NANDBCH_FLEX_MUXCTRL				0x104
+#define NANDBCH_FLEX_DATAWRITE_CONFIG			0x10C
+#define NANDBCH_FLEX_DATAREAD_CONFIG			0x110
+#define NANDBCH_VERSION_REG				0x144
+#define NANDBCH_ADDRESS_REG_1				0x1F0
+#define NANDBCH_ADDRESS_REG_2				0x1F4
+#define NANDBCH_ADDRESS_REG_3				0x1F8
+#define NANDBCH_MULTI_CS_CONFIG_REG			0x1FC
+#define NANDBCH_SEQ_REG_1				0x200
+#define NANDBCH_SEQ_REG_2				0x204
+#define NANDBCH_SEQ_REG_3				0x208
+#define NANDBCH_SEQ_REG_4				0x20C
+#define NANDBCH_ADD					0x210
+#define NANDBCH_EXTRA_REG				0x214
+#define NANDBCH_CMD					0x218
+#define NANDBCH_GEN_CFG					0x220
+#define NANDBCH_DELAY_REG				0x224
+#define NANDBCH_SEQ_CFG					0x22C
+#define NANDBCH_SEQ_STA					0x270
+#define NANDBCH_DATA_BUFFER_ENTRY_0			0x280
+#define NANDBCH_DATA_BUFFER_ENTRY_1			0x284
+#define NANDBCH_DATA_BUFFER_ENTRY_2			0x288
+#define NANDBCH_DATA_BUFFER_ENTRY_3			0x28C
+#define NANDBCH_DATA_BUFFER_ENTRY_4			0x290
+#define NANDBCH_DATA_BUFFER_ENTRY_5			0x294
+#define NANDBCH_DATA_BUFFER_ENTRY_6			0x298
+#define NANDBCH_DATA_BUFFER_ENTRY_7			0x29C
+#define NANDBCH_ECC_SCORE_REG_A				0x2A0
+#define NANDBCH_ECC_SCORE_REG_B				0x2A4
+#define NANDBCH_CHECK_STATUS_REG_A			0x2A8
+#define NANDBCH_CHECK_STATUS_REG_B			0x2AC
+#define NANDBCH_BUFFER_LIST_PTR				0x300
+#define NANDBCH_SEQ_PTR_REG				0x304
+#define NANDBCH_ERROR_THRESHOLD_REG			0x308
+
+#define EMISS_NAND_DMA					0x0800
+/* EMISS NAND BCH STPLUG Registers (Offsets from EMISS_NAND_DMA) */
+#define EMISS_NAND_RD_DMA_PAGE_SIZE			0x000
+#define EMISS_NAND_RD_DMA_MAX_OPCODE_SIZE		0x004
+#define EMISS_NAND_RD_DMA_MIN_OPCODE_SIZE		0x008
+#define EMISS_NAND_RD_DMA_MAX_CHUNK_SIZE		0x00C
+#define EMISS_NAND_RD_DMA_MAX_MESSAGE_SIZE		0x010
+
+#define EMISS_NAND_WR_DMA_PAGE_SIZE			0x100
+#define EMISS_NAND_WR_DMA_MAX_OPCODE_SIZE		0x104
+#define EMISS_NAND_WR_DMA_MIN_OPCODE_SIZE		0x108
+#define EMISS_NAND_WR_DMA_MAX_CHUNK_SIZE		0x10C
+#define EMISS_NAND_WR_DMA_MAX_MESSAGE_SIZE		0x110
+
+/*
+ * Hamming/BCH controller interrupts
+ */
+
+/* NANDxxx_INT_EN/NANDxxx_INT_STA */
+/*      Common */
+#define NAND_INT_ENABLE				(0x1 << 0)
+#define NAND_INT_RBN				(0x1 << 2)
+#define NAND_INT_SEQCHECK			(0x1 << 5)
+/*      Hamming only */
+#define NANDHAM_INT_DATA_DREQ			(0x1 << 3)
+#define NANDHAM_INT_SEQ_DREQ			(0x1 << 4)
+#define NANDHAM_INT_ECC_FIX_REQ			(0x1 << 6)
+/*      BCH only */
+#define NANDBCH_INT_SEQNODESOVER		(0x1 << 7)
+#define NANDBCH_INT_ECCTHRESHOLD		(0x1 << 8)
+
+/* NANDxxx_INT_CLR */
+/*      Common */
+#define NAND_INT_CLR_RBN			(0x1 << 2)
+#define NAND_INT_CLR_SEQCHECK			(0x1 << 3)
+/*      Hamming only */
+#define NANDHAM_INT_CLR_ECC_FIX_REQ		(0x1 << 4)
+#define NANDHAM_INT_CLR_DATA_DREQ		(0x1 << 5)
+#define NANDHAM_INT_CLR_SEQ_DREQ		(0x1 << 6)
+/*      BCH only */
+#define NANDBCH_INT_CLR_SEQNODESOVER		(0x1 << 5)
+#define NANDBCH_INT_CLR_ECCTHRESHOLD		(0x1 << 6)
+
+/* NANDxxx_INT_EDGE_CFG */
+#define NAND_EDGE_CFG_RBN_RISING		0x1
+#define NAND_EDGE_CFG_RBN_FALLING		0x2
+#define NAND_EDGE_CFG_RBN_ANY			0x3
+
+/* NANDBCH_CONTROLLER_CFG/NANDHAM_FLEXMODE_CFG */
+#define CFG_ENABLE_FLEX				0x1
+#define CFG_ENABLE_AFM				0x2
+#define CFG_RESET				(0x1 << 3)
+#define CFG_RESET_ECC(x)			(0x1 << (7 + (x)))
+#define CFG_RESET_ECC_ALL			(0xff << 7)
+
+
+/*
+ * BCH Controller
+ */
+
+/* NANDBCH_BOOTBANK_CFG */
+#define BOOT_CFG_RESET				(0x1 << 3)
+
+/* NANDBCH_CTL_TIMING */
+#define NANDBCH_CTL_SETUP(x)			((x) & 0xff)
+#define NANDBCH_CTL_HOLD(x)			(((x) & 0xff) << 8)
+#define NANDBCH_CTL_WERBN(x)			(((x) & 0xff) << 24)
+
+/* NANDBCH_WEN_TIMING */
+#define NANDBCH_WEN_ONTIME(x)			((x) & 0xff)
+#define NANDBCH_WEN_OFFTIME(x)			(((x) & 0xff) << 8)
+#define NANDBCH_WEN_ONHALFCYCLE			(0x1 << 16)
+#define NANDBCH_WEN_OFFHALFCYCLE		(0x1 << 17)
+
+/* NANDBCH_REN_TIMING */
+#define NANDBCH_REN_ONTIME(x)			((x) & 0xff)
+#define NANDBCH_REN_OFFTIME(x)			(((x) & 0xff) << 8)
+#define NANDBCH_REN_ONHALFCYCLE			(0x1 << 16)
+#define NANDBCH_REN_OFFHALFCYCLE		(0x1 << 17)
+#define NANDBCH_REN_TELQV(x)			(((x) & 0xff) << 24)
+
+/* NANDBCH_BLOCK_ZERO_REMAP_REG */
+#define NANDBCH_BACKUP_COPY_FOUND		(0x1 << 0)
+#define NANDBCH_ORIG_CODE_CORRUPTED		(0x1 << 1)
+#define NANDBCH_BLK_ZERO_REMAP(x)		((x) >> 14)
+
+/* NANDBCH_BOOT_STATUS */
+#define NANDBCH_BOOT_MAX_ERRORS(x)		((x) & 0x1f)
+
+/* NANDBCH_GEN_CFG */
+#define GEN_CFG_DATA_8_NOT_16			(0x1 << 16)
+#define GEN_CFG_EXTRA_ADD_CYCLE			(0x1 << 18)
+#define GEN_CFG_2X8_MODE			(0x1 << 19)
+#define GEN_CFG_ECC_SHIFT			20
+#define GEN_CFG_18BIT_ECC			(BCH_18BIT_ECC << \
+						GEN_CFG_ECC_SHIFT)
+#define GEN_CFG_30BIT_ECC			(BCH_30BIT_ECC << \
+						GEN_CFG_ECC_SHIFT)
+#define GEN_CFG_NO_ECC				(BCH_NO_ECC    << \
+						GEN_CFG_ECC_SHIFT)
+#define GEN_CFG_LAST_SEQ_NODE			(0x1 << 22)
+
+/* NANDBCH_SEQ_CFG */
+#define SEQ_CFG_REPEAT_COUNTER(x)		((x) & 0xffff)
+#define SEQ_CFG_SEQ_IDENT(x)			(((x) & 0xff) << 16)
+#define SEQ_CFG_DATA_WRITE			(0x1 << 24)
+#define SEQ_CFG_ERASE				(0x1 << 25)
+#define SEQ_CFG_GO_STOP				(0x1 << 26)
+
+/* NANDBCH_SEQ_STA */
+#define SEQ_STA_RUN				(0x1 << 4)
+
+/*
+ * BCH Commands
+ */
+#define BCH_OPC_STOP			0x0
+#define BCH_OPC_CMD			0x1
+#define BCH_OPC_INC			0x2
+#define BCH_OPC_DEC_JUMP		0x3
+#define BCH_OPC_DATA			0x4
+#define BCH_OPC_DELAY			0x5
+#define BCH_OPC_CHECK			0x6
+#define BCH_OPC_ADDR			0x7
+#define BCH_OPC_NEXT_CHIP_ON		0x8
+#define BCH_OPC_DEC_JMP_MCS		0x9
+#define BCH_OPC_ECC_SCORE		0xA
+
+#define BCH_INSTR(opc, opr)		((opc) | ((opr) << 4))
+
+#define BCH_CMD_ADDR			BCH_INSTR(BCH_OPC_CMD, 0)
+#define BCH_CL_CMD_1			BCH_INSTR(BCH_OPC_CMD, 1)
+#define BCH_CL_CMD_2			BCH_INSTR(BCH_OPC_CMD, 2)
+#define BCH_CL_CMD_3			BCH_INSTR(BCH_OPC_CMD, 3)
+#define BCH_CL_EX_0			BCH_INSTR(BCH_OPC_CMD, 4)
+#define BCH_CL_EX_1			BCH_INSTR(BCH_OPC_CMD, 5)
+#define BCH_CL_EX_2			BCH_INSTR(BCH_OPC_CMD, 6)
+#define BCH_CL_EX_3			BCH_INSTR(BCH_OPC_CMD, 7)
+#define BCH_INC(x)			BCH_INSTR(BCH_OPC_INC, (x))
+#define BCH_DEC_JUMP(x)			BCH_INSTR(BCH_OPC_DEC_JUMP, (x))
+#define BCH_STOP			BCH_INSTR(BCH_OPC_STOP, 0)
+#define BCH_DATA_1_SECTOR		BCH_INSTR(BCH_OPC_DATA, 0)
+#define BCH_DATA_2_SECTOR		BCH_INSTR(BCH_OPC_DATA, 1)
+#define BCH_DATA_4_SECTOR		BCH_INSTR(BCH_OPC_DATA, 2)
+#define BCH_DATA_8_SECTOR		BCH_INSTR(BCH_OPC_DATA, 3)
+#define BCH_DATA_16_SECTOR		BCH_INSTR(BCH_OPC_DATA, 4)
+#define BCH_DATA_32_SECTOR		BCH_INSTR(BCH_OPC_DATA, 5)
+#define BCH_DELAY_0			BCH_INSTR(BCH_OPC_DELAY, 0)
+#define BCH_DELAY_1			BCH_INSTR(BCH_OPC_DELAY, 1)
+#define BCH_OP_ERR			BCH_INSTR(BCH_OPC_CHECK, 0)
+#define BCH_CACHE_ERR			BCH_INSTR(BCH_OPC_CHECK, 1)
+#define BCH_ERROR			BCH_INSTR(BCH_OPC_CHECK, 2)
+#define BCH_AL_EX_0			BCH_INSTR(BCH_OPC_ADDR, 0)
+#define BCH_AL_EX_1			BCH_INSTR(BCH_OPC_ADDR, 1)
+#define BCH_AL_EX_2			BCH_INSTR(BCH_OPC_ADDR, 2)
+#define BCH_AL_EX_3			BCH_INSTR(BCH_OPC_ADDR, 3)
+#define BCH_AL_AD_0			BCH_INSTR(BCH_OPC_ADDR, 4)
+#define BCH_AL_AD_1			BCH_INSTR(BCH_OPC_ADDR, 5)
+#define BCH_AL_AD_2			BCH_INSTR(BCH_OPC_ADDR, 6)
+#define BCH_AL_AD_3			BCH_INSTR(BCH_OPC_ADDR, 7)
+#define BCH_NEXT_CHIP_ON		BCH_INSTR(BCH_OPC_NEXT_CHIP_ON, 0)
+#define BCH_DEC_JMP_MCS(x)		BCH_INSTR(BCH_OPC_DEC_JMP_MCS, (x))
+#define BCH_ECC_SCORE(x)		BCH_INSTR(BCH_OPC_ECC_SCORE, (x))
+
+
+/*
+ * Hamming-FLEX register fields
+ */
+
+/* NANDHAM_FLEX_DATAREAD/WRITE_CONFIG */
+#define FLEX_DATA_CFG_WAIT_RBN			(0x1 << 27)
+#define FLEX_DATA_CFG_BEATS_1			(0x1 << 28)
+#define FLEX_DATA_CFG_BEATS_2			(0x2 << 28)
+#define FLEX_DATA_CFG_BEATS_3			(0x3 << 28)
+#define FLEX_DATA_CFG_BEATS_4			(0x0 << 28)
+#define FLEX_DATA_CFG_BYTES_1			(0x0 << 30)
+#define FLEX_DATA_CFG_BYTES_2			(0x1 << 30)
+#define FLEX_DATA_CFG_CSN			(0x1 << 31)
+
+/* NANDHAM_FLEX_CMD */
+#define FLEX_CMD_RBN				(0x1 << 27)
+#define FLEX_CMD_BEATS_1			(0x1 << 28)
+#define FLEX_CMD_BEATS_2			(0x2 << 28)
+#define FLEX_CMD_BEATS_3			(0x3 << 28)
+#define FLEX_CMD_BEATS_4			(0x0 << 28)
+#define FLEX_CMD_CSN				(0x1 << 31)
+#define FLEX_CMD(x)				(((x) & 0xff) |	\
+						 FLEX_CMD_RBN |	\
+						 FLEX_CMD_BEATS_1 |	\
+						 FLEX_CMD_CSN)
+/* NANDHAM_FLEX_ADD */
+#define FLEX_ADDR_RBN				(0x1 << 27)
+#define FLEX_ADDR_BEATS_1			(0x1 << 28)
+#define FLEX_ADDR_BEATS_2			(0x2 << 28)
+#define FLEX_ADDR_BEATS_3			(0x3 << 28)
+#define FLEX_ADDR_BEATS_4			(0x0 << 28)
+#define FLEX_ADDR_ADD8_VALID			(0x1 << 30)
+#define FLEX_ADDR_CSN				(0x1 << 31)
+
+#endif /* STM_NANDC_REGS_H */
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH 7/8] mtd: nand: stm_nand_bch: add support for ST's BCH NAND controller
  2014-08-13  9:11 ` Lee Jones
  (?)
@ 2014-08-13  9:12   ` Lee Jones
  -1 siblings, 0 replies; 51+ messages in thread
From: Lee Jones @ 2014-08-13  9:12 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: lee.jones, kernel, computersforpeace, pekon, linux-mtd

Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
 drivers/mtd/nand/Kconfig        |    7 +
 drivers/mtd/nand/Makefile       |    1 +
 drivers/mtd/nand/stm_nand_bch.c | 1618 +++++++++++++++++++++++++++++++++++++++
 drivers/mtd/nand/stm_nand_dt.c  |  109 +++
 drivers/mtd/nand/stm_nand_dt.h  |   24 +
 include/linux/mtd/stm_nand.h    |  147 ++++
 6 files changed, 1906 insertions(+)
 create mode 100644 drivers/mtd/nand/stm_nand_bch.c
 create mode 100644 drivers/mtd/nand/stm_nand_dt.c
 create mode 100644 drivers/mtd/nand/stm_nand_dt.h
 create mode 100644 include/linux/mtd/stm_nand.h

diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index f1cf503..2738eec 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -513,4 +513,11 @@ 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_STM_BCH
+	tristate "STMicroelectronics: NANDi BCH Controller"
+	depends on ARCH_STI
+	depends on OF
+	help
+	  Adds support for the STMicroelectronics NANDi BCH Controller.
+
 endif # MTD_NAND
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index a035e7c..60f374b 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -46,6 +46,7 @@ obj-$(CONFIG_MTD_NAND_NUC900)		+= nuc900_nand.o
 obj-$(CONFIG_MTD_NAND_MPC5121_NFC)	+= mpc5121_nfc.o
 obj-$(CONFIG_MTD_NAND_RICOH)		+= r852.o
 obj-$(CONFIG_MTD_NAND_JZ4740)		+= jz4740_nand.o
+obj-$(CONFIG_MTD_NAND_STM_BCH)		+= stm_nand_bch.o stm_nand_dt.o
 obj-$(CONFIG_MTD_NAND_GPMI_NAND)	+= gpmi-nand/
 obj-$(CONFIG_MTD_NAND_XWAY)		+= xway_nand.o
 obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH)	+= bcm47xxnflash/
diff --git a/drivers/mtd/nand/stm_nand_bch.c b/drivers/mtd/nand/stm_nand_bch.c
new file mode 100644
index 0000000..a104343
--- /dev/null
+++ b/drivers/mtd/nand/stm_nand_bch.c
@@ -0,0 +1,1618 @@
+/*
+ * Support for STMicroelectronics NANDi BCH Controller
+ *
+ * Copyright (c) 2014 STMicroelectronics Limited
+ *
+ * Authors: Angus Clark <Angus.Clark@st.com>
+ *	    Lee Jones <lee.jones@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/completion.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/stm_nand_bbt.h>
+#include <linux/mtd/stm_nand.h>
+#include <linux/mtd/partitions.h>
+
+#include "stm_nand_dt.h"
+#include "stm_nand_regs.h"
+
+/* NANDi BCH Controller properties */
+#define NANDI_BCH_SECTOR_SIZE			1024
+#define NANDI_BCH_DMA_ALIGNMENT			64
+#define NANDI_BCH_MAX_BUF_LIST			8
+#define NANDI_BCH_BUF_LIST_SIZE			(4 * NANDI_BCH_MAX_BUF_LIST)
+
+/* ONFI define 6 timing modes */
+#define ST_NAND_ONFI_TIMING_MODES		6
+
+#define PICO_TO_MILI(pico)			(pico / 1000)
+
+static int bch_ecc_strength[] = {
+	[BCH_18BIT_ECC] = 18,
+	[BCH_30BIT_ECC] = 30,
+	[BCH_NO_ECC] = 0,
+};
+
+/* BCH 'program' structure */
+struct bch_prog {
+	u32	multi_cs_addr[3];
+	u32	multi_cs_config;
+	u8	seq[16];
+	u32	addr;
+	u32	extra;
+	u8	cmd[4];
+	u32	reserved1;
+	u32	gen_cfg;
+	u32	delay;
+	u32	reserved2;
+	u32	seq_cfg;
+};
+
+/* BCH template programs (modified on-the-fly) */
+static struct bch_prog bch_prog_read_page = {
+	.cmd = {
+		NAND_CMD_READ0,
+		NAND_CMD_READSTART,
+	},
+	.seq = {
+		BCH_ECC_SCORE(0),
+		BCH_CMD_ADDR,
+		BCH_CL_CMD_1,
+		BCH_DATA_2_SECTOR,
+		BCH_STOP,
+	},
+	.gen_cfg = (GEN_CFG_DATA_8_NOT_16 |
+		    GEN_CFG_EXTRA_ADD_CYCLE |
+		    GEN_CFG_LAST_SEQ_NODE),
+	.seq_cfg = SEQ_CFG_GO_STOP,
+};
+
+static struct bch_prog bch_prog_write_page = {
+	.cmd = {
+		NAND_CMD_SEQIN,
+		NAND_CMD_PAGEPROG,
+		NAND_CMD_STATUS,
+	},
+	.seq = {
+		BCH_CMD_ADDR,
+		BCH_DATA_4_SECTOR,
+		BCH_CL_CMD_1,
+		BCH_CL_CMD_2,
+		BCH_OP_ERR,
+		BCH_STOP,
+	},
+	.gen_cfg = (GEN_CFG_DATA_8_NOT_16 |
+		    GEN_CFG_EXTRA_ADD_CYCLE |
+		    GEN_CFG_LAST_SEQ_NODE),
+	.seq_cfg = (SEQ_CFG_GO_STOP |
+		    SEQ_CFG_DATA_WRITE),
+};
+
+static struct bch_prog bch_prog_erase_block = {
+	.seq = {
+		BCH_CL_CMD_1,
+		BCH_AL_EX_0,
+		BCH_AL_EX_1,
+		BCH_AL_EX_2,
+		BCH_CL_CMD_2,
+		BCH_CL_CMD_3,
+		BCH_OP_ERR,
+		BCH_STOP,
+	},
+	.cmd = {
+		NAND_CMD_ERASE1,
+		NAND_CMD_ERASE1,
+		NAND_CMD_ERASE2,
+		NAND_CMD_STATUS,
+	},
+	.gen_cfg = (GEN_CFG_DATA_8_NOT_16 |
+		    GEN_CFG_EXTRA_ADD_CYCLE |
+		    GEN_CFG_LAST_SEQ_NODE),
+	.seq_cfg = (SEQ_CFG_GO_STOP |
+		    SEQ_CFG_ERASE),
+};
+
+/* Configure BCH read/write/erase programs */
+static void bch_configure_progs(struct nandi_controller *nandi)
+{
+	uint8_t data_opa = ffs(nandi->sectors_per_page) - 1;
+	uint8_t data_instr = BCH_INSTR(BCH_OPC_DATA, data_opa);
+	uint32_t gen_cfg_ecc = nandi->bch_ecc_mode << GEN_CFG_ECC_SHIFT;
+
+	/* Set 'DATA' instruction */
+	bch_prog_read_page.seq[3] = data_instr;
+	bch_prog_write_page.seq[1] = data_instr;
+
+	/* Set ECC mode */
+	bch_prog_read_page.gen_cfg |= gen_cfg_ecc;
+	bch_prog_write_page.gen_cfg |= gen_cfg_ecc;
+	bch_prog_erase_block.gen_cfg |= gen_cfg_ecc;
+
+	/*
+	 * Template sequences above are defined for devices that use 5 address
+	 * cycles for page Read/Write operations (and 3 for Erase operations).
+	 * Update sequences for devices that use 4 address cycles.
+	 */
+	if (!nandi->extra_addr) {
+		/* Clear 'GEN_CFG_EXTRA_ADD_CYCLE' flag */
+		bch_prog_read_page.gen_cfg &= ~GEN_CFG_EXTRA_ADD_CYCLE;
+		bch_prog_write_page.gen_cfg &= ~GEN_CFG_EXTRA_ADD_CYCLE;
+		bch_prog_erase_block.gen_cfg &= ~GEN_CFG_EXTRA_ADD_CYCLE;
+
+		/* Configure Erase sequence for 2 address cycles */
+		/* (page address) */
+		bch_prog_erase_block.seq[0] = BCH_CL_CMD_1;
+		bch_prog_erase_block.seq[1] = BCH_AL_EX_0;
+		bch_prog_erase_block.seq[2] = BCH_AL_EX_1;
+		bch_prog_erase_block.seq[3] = BCH_CL_CMD_2;
+		bch_prog_erase_block.seq[4] = BCH_CL_CMD_3;
+		bch_prog_erase_block.seq[5] = BCH_OP_ERR;
+		bch_prog_erase_block.seq[6] = BCH_STOP;
+	}
+}
+
+/*
+ * NANDi Interrupts (shared by Hamming and BCH controllers)
+ */
+static irqreturn_t nandi_irq_handler(int irq, void *dev)
+{
+	struct nandi_controller *nandi = dev;
+	unsigned int status;
+
+	status = readl(nandi->base + NANDBCH_INT_STA);
+
+	if (nandi->bch_ecc_mode && (status & NANDBCH_INT_SEQNODESOVER)) {
+		/* BCH */
+		writel(NANDBCH_INT_CLR_SEQNODESOVER,
+		       nandi->base + NANDBCH_INT_CLR);
+		complete(&nandi->seq_completed);
+		return IRQ_HANDLED;
+	}
+	if (status & NAND_INT_RBN) {
+		/* Hamming */
+		writel(NAND_INT_CLR_RBN, nandi->base + NANDHAM_INT_CLR);
+		complete(&nandi->rbn_completed);
+		return IRQ_HANDLED;
+	}
+
+	return IRQ_NONE;
+}
+
+static void nandi_enable_interrupts(struct nandi_controller *nandi,
+				    uint32_t irqs)
+{
+	uint32_t val;
+
+	val = readl(nandi->base + NANDBCH_INT_EN);
+	val |= irqs;
+	writel(val, nandi->base + NANDBCH_INT_EN);
+}
+
+static void nandi_disable_interrupts(struct nandi_controller *nandi,
+				     uint32_t irqs)
+{
+	uint32_t val;
+
+	val = readl(nandi->base + NANDBCH_INT_EN);
+	val &= ~irqs;
+	writel(val, nandi->base + NANDBCH_INT_EN);
+}
+
+/*
+ * BCH Operations
+ */
+static inline void bch_load_prog_cpu(struct nandi_controller *nandi,
+				     struct bch_prog *prog)
+{
+	uint32_t *src = (uint32_t *)prog;
+	uint32_t *dst = (uint32_t *)(nandi->base + NANDBCH_ADDRESS_REG_1);
+	int i;
+
+	for (i = 0; i < 16; i++) {
+		/* Skip registers marked as "reserved" */
+		if (i != 11 && i != 14)
+			writel(*src, dst);
+		dst++;
+		src++;
+	}
+}
+
+static void bch_wait_seq(struct nandi_controller *nandi)
+{
+	int ret;
+
+	ret = wait_for_completion_timeout(&nandi->seq_completed, HZ/2);
+	if (!ret)
+		dev_err(nandi->dev, "BCH Seq timeout\n");
+}
+
+uint8_t bch_erase_block(struct nandi_controller *nandi,
+			loff_t offs)
+{
+	struct nand_chip *chip = &nandi->info.chip;
+	struct bch_prog *prog = &bch_prog_erase_block;
+	uint8_t status;
+
+	dev_dbg(nandi->dev, "%s: offs = 0x%012llx\n", __func__, offs);
+
+	prog->extra = (uint32_t)(offs >> chip->page_shift);
+
+	emiss_nandi_select(nandi, STM_NANDI_BCH);
+
+	nandi_enable_interrupts(nandi, NANDBCH_INT_SEQNODESOVER);
+	reinit_completion(&nandi->seq_completed);
+
+	bch_load_prog_cpu(nandi, prog);
+
+	bch_wait_seq(nandi);
+
+	nandi_disable_interrupts(nandi, NANDBCH_INT_SEQNODESOVER);
+
+	status = (uint8_t)(readl(nandi->base +
+				 NANDBCH_CHECK_STATUS_REG_A) & 0xff);
+
+	return status;
+}
+EXPORT_SYMBOL(bch_erase_block);
+
+static int bch_erase(struct mtd_info *mtd, int page)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct nandi_controller *nandi = chip->priv;
+	uint32_t page_size = mtd->writesize;
+	loff_t offs = (loff_t)page * page_size;
+
+	return bch_erase_block(nandi, offs);
+}
+
+/*
+ * Detect an erased page, tolerating and correcting up to a specified number of
+ * bits at '0'.  (For many devices, it is now deemed within spec for an erased
+ * page to include a number of bits at '0', either as a result of read-disturb
+ * behaviour or 'stuck-at-zero' failures.)  Returns the number of corrected
+ * bits, or a '-1' if we have exceeded the maximum number of bits at '0' (likely
+ * to be a genuine uncorrectable ECC error).  In the latter case, the data must
+ * be returned unmodified, in accordance with the MTD API.
+ */
+static int check_erased_page(uint8_t *data, uint32_t page_size, int max_zeros)
+{
+	uint8_t *b = data;
+	int zeros = 0;
+	int i;
+
+	for (i = 0; i < page_size; i++) {
+		zeros += hweight8(~*b++);
+		if (zeros > max_zeros)
+			return -1;
+	}
+
+	if (zeros)
+		memset(data, 0xff, page_size);
+
+	return zeros;
+}
+
+/* Returns the number of ECC errors, or '-1' for uncorrectable error */
+int bch_read_page(struct nandi_controller *nandi,
+		  loff_t offs,
+		  uint8_t *buf)
+{
+	struct nand_chip *chip = &nandi->info.chip;
+	struct bch_prog *prog = &bch_prog_read_page;
+	uint32_t page_size = nandi->info.mtd.writesize;
+	unsigned long list_phys;
+	unsigned long buf_phys;
+	uint32_t ecc_err;
+	int ret = 0;
+
+	dev_dbg(nandi->dev, "%s: offs = 0x%012llx\n", __func__, offs);
+
+	BUG_ON(offs & (NANDI_BCH_DMA_ALIGNMENT - 1));
+
+	emiss_nandi_select(nandi, STM_NANDI_BCH);
+
+	nandi_enable_interrupts(nandi, NANDBCH_INT_SEQNODESOVER);
+	reinit_completion(&nandi->seq_completed);
+
+	/* Reset ECC stats */
+	writel(CFG_RESET_ECC_ALL | CFG_ENABLE_AFM,
+	       nandi->base + NANDBCH_CONTROLLER_CFG);
+	writel(CFG_ENABLE_AFM, nandi->base + NANDBCH_CONTROLLER_CFG);
+
+	prog->addr = (uint32_t)((offs >> (chip->page_shift - 8)) & 0xffffff00);
+
+	buf_phys = dma_map_single(NULL, buf, page_size, DMA_FROM_DEVICE);
+
+	memset(nandi->buf_list, 0x00, NANDI_BCH_BUF_LIST_SIZE);
+	nandi->buf_list[0] = buf_phys | (nandi->sectors_per_page - 1);
+
+	list_phys = dma_map_single(NULL, nandi->buf_list,
+				   NANDI_BCH_BUF_LIST_SIZE, DMA_TO_DEVICE);
+
+	writel(list_phys, nandi->base + NANDBCH_BUFFER_LIST_PTR);
+
+	bch_load_prog_cpu(nandi, prog);
+
+	bch_wait_seq(nandi);
+
+	nandi_disable_interrupts(nandi, NANDBCH_INT_SEQNODESOVER);
+
+	dma_unmap_single(NULL, list_phys, NANDI_BCH_BUF_LIST_SIZE,
+			 DMA_TO_DEVICE);
+	dma_unmap_single(NULL, buf_phys, page_size, DMA_FROM_DEVICE);
+
+	/* Use the maximum per-sector ECC count! */
+	ecc_err = readl(nandi->base + NANDBCH_ECC_SCORE_REG_A) & 0xff;
+	if (ecc_err == 0xff) {
+		/*
+		 * Downgrade uncorrectable ECC error for an erased page,
+		 * tolerating 'bch_ecc_strength' bits at zero.
+		 */
+		ret = check_erased_page(buf, page_size, chip->ecc.strength);
+		if (ret >= 0)
+			dev_dbg(nandi->dev,
+				"%s: erased page detected: \n"
+				"  downgrading uncorrectable ECC error.\n",
+				__func__);
+	} else {
+		ret = (int)ecc_err;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(bch_read_page);
+
+static int bch_read(struct mtd_info *mtd, struct nand_chip *chip,
+			 uint8_t *buf, int oob_required, int page)
+{
+	struct nandi_controller *nandi = chip->priv;
+	uint32_t page_size = mtd->writesize;
+	loff_t offs = (loff_t)page * page_size;
+	bool bounce = false;
+	uint8_t *p;
+	int ret;
+
+	if (((unsigned int)buf & (NANDI_BCH_DMA_ALIGNMENT - 1)) ||
+	    (!virt_addr_valid(buf))) /* vmalloc'd buffer! */
+		bounce = true;
+
+	p = bounce ? nandi->page_buf : buf;
+
+	ret = bch_read_page(nandi, offs, p);
+	if (ret < 0)
+		mtd->ecc_stats.failed++;
+	else
+		mtd->ecc_stats.corrected += ret;
+
+	if (bounce)
+		memcpy(buf, p, page_size);
+
+	return ret;
+}
+
+/* Returns the status of the NAND device following the write operation */
+uint8_t bch_write_page(struct nandi_controller *nandi,
+		       loff_t offs, const uint8_t *buf)
+{
+	struct nand_chip *chip = &nandi->info.chip;
+	struct bch_prog *prog = &bch_prog_write_page;
+	uint32_t page_size = nandi->info.mtd.writesize;
+	uint8_t *p = (uint8_t *)buf;
+	unsigned long list_phys;
+	unsigned long buf_phys;
+	uint8_t status;
+	bool bounce = false;
+
+	dev_dbg(nandi->dev, "%s: offs = 0x%012llx\n", __func__, offs);
+
+	BUG_ON(offs & (page_size - 1));
+
+	if (((unsigned long)buf & (NANDI_BCH_DMA_ALIGNMENT - 1)) ||
+	    !virt_addr_valid(buf)) { /* vmalloc'd buffer! */
+		bounce = true;
+	}
+
+	if (bounce) {
+		memcpy(nandi->page_buf, buf, page_size);
+		p = nandi->page_buf;
+		nandi->cached_page = -1;
+	}
+
+	emiss_nandi_select(nandi, STM_NANDI_BCH);
+
+	nandi_enable_interrupts(nandi, NANDBCH_INT_SEQNODESOVER);
+	reinit_completion(&nandi->seq_completed);
+
+	prog->addr = (uint32_t)((offs >> (chip->page_shift - 8)) & 0xffffff00);
+
+	buf_phys = dma_map_single(NULL, p, page_size, DMA_TO_DEVICE);
+	memset(nandi->buf_list, 0x00, NANDI_BCH_BUF_LIST_SIZE);
+	nandi->buf_list[0] = buf_phys | (nandi->sectors_per_page - 1);
+
+	list_phys = dma_map_single(NULL, nandi->buf_list,
+				   NANDI_BCH_BUF_LIST_SIZE, DMA_TO_DEVICE);
+
+	writel(list_phys, nandi->base + NANDBCH_BUFFER_LIST_PTR);
+
+	bch_load_prog_cpu(nandi, prog);
+
+	bch_wait_seq(nandi);
+
+	nandi_disable_interrupts(nandi, NANDBCH_INT_SEQNODESOVER);
+
+	dma_unmap_single(NULL, list_phys, NANDI_BCH_BUF_LIST_SIZE,
+			 DMA_TO_DEVICE);
+	dma_unmap_single(NULL, buf_phys, page_size, DMA_FROM_DEVICE);
+
+	status = (uint8_t)(readl(nandi->base +
+				 NANDBCH_CHECK_STATUS_REG_A) & 0xff);
+
+	return status;
+}
+EXPORT_SYMBOL(bch_write_page);
+
+static int bch_write(struct mtd_info *mtd, struct nand_chip *chip,
+		     uint32_t offset, int data_len, const uint8_t *buf,
+		     int oob_required, int page, int cached, int raw)
+{
+	struct nandi_controller *nandi = chip->priv;
+	uint32_t page_size = mtd->writesize;
+	loff_t offs = (loff_t)page * page_size;
+	int ret;
+
+	ret = bch_write_page(nandi, offs, buf);
+	if (ret & NAND_STATUS_FAIL)
+		return -EIO;
+
+	return 0;
+}
+
+/*
+ * Hamming-FLEX operations
+ */
+static int flex_wait_rbn(struct nandi_controller *nandi)
+{
+	int ret;
+
+	ret = wait_for_completion_timeout(&nandi->rbn_completed, HZ/2);
+	if (!ret)
+		dev_err(nandi->dev, "FLEX RBn timeout\n");
+
+	return ret;
+}
+
+static void flex_cmd(struct nandi_controller *nandi, uint8_t cmd)
+{
+	uint32_t val;
+
+	val = (FLEX_CMD_CSN | FLEX_CMD_BEATS_1 | cmd);
+	writel(val, nandi->base + NANDHAM_FLEX_CMD);
+}
+
+static void flex_addr(struct nandi_controller *nandi,
+		      uint32_t addr, int cycles)
+{
+	addr &= 0x00ffffff;
+
+	BUG_ON(cycles < 1);
+	BUG_ON(cycles > 3);
+
+	addr |= (FLEX_ADDR_CSN | FLEX_ADDR_ADD8_VALID);
+	addr |= (cycles & 0x3) << 28;
+
+	writel(addr, nandi->base + NANDHAM_FLEX_ADD);
+}
+
+/*
+ * Partial implementation of MTD/NAND Interface, based on Hamming-FLEX
+ * operation.
+ *
+ * Allows us to make use of nand_base.c functions where possible
+ * (e.g. nand_scan_ident() and friends).
+ */
+static void flex_command_lp(struct mtd_info *mtd, unsigned int command,
+			    int column, int page)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct nandi_controller *nandi = chip->priv;
+
+	emiss_nandi_select(nandi, STM_NANDI_HAMMING);
+
+	switch (command) {
+	case NAND_CMD_READOOB:
+		/* Emulate NAND_CMD_READOOB */
+		column += mtd->writesize;
+		command = NAND_CMD_READ0;
+		break;
+	case NAND_CMD_READ0:
+	case NAND_CMD_ERASE1:
+	case NAND_CMD_SEQIN:
+	case NAND_CMD_RESET:
+	case NAND_CMD_PARAM:
+		/* Prime RBn wait */
+		nandi_enable_interrupts(nandi, NAND_INT_RBN);
+		reinit_completion(&nandi->rbn_completed);
+		break;
+	case NAND_CMD_READID:
+	case NAND_CMD_STATUS:
+	case NAND_CMD_ERASE2:
+		break;
+	default:
+		/* Catch unexpected commands */
+		BUG();
+	}
+
+	/*
+	 * Command Cycle
+	 */
+	flex_cmd(nandi, command);
+
+	/*
+	 * Address Cycles
+	 */
+	if (column != -1)
+		flex_addr(nandi, column,
+			  (command == NAND_CMD_READID) ? 1 : 2);
+
+	if (page != -1)
+		flex_addr(nandi, page, nandi->extra_addr ? 3 : 2);
+
+	/* Complete 'READ0' command */
+	if (command == NAND_CMD_READ0)
+		flex_cmd(nandi, NAND_CMD_READSTART);
+
+	/* Wait for RBn, if required                        */
+	/* (Note, other commands may handle wait elsewhere) */
+	if (command == NAND_CMD_RESET ||
+	    command == NAND_CMD_READ0 ||
+	    command == NAND_CMD_PARAM) {
+		flex_wait_rbn(nandi);
+		nandi_disable_interrupts(nandi, NAND_INT_RBN);
+	}
+}
+
+static uint8_t flex_read_byte(struct mtd_info *mtd)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct nandi_controller *nandi = chip->priv;
+
+	emiss_nandi_select(nandi, STM_NANDI_HAMMING);
+
+	return (uint8_t)(readl(nandi->base + NANDHAM_FLEX_DATA) & 0xff);
+}
+
+static int flex_wait_func(struct mtd_info *mtd, struct nand_chip *chip)
+{
+	struct nandi_controller *nandi = chip->priv;
+
+	emiss_nandi_select(nandi, STM_NANDI_HAMMING);
+
+	flex_wait_rbn(nandi);
+
+	flex_cmd(nandi, NAND_CMD_STATUS);
+
+	return (int)(readl(nandi->base + NANDHAM_FLEX_DATA) & 0xff);
+}
+
+/* Multi-CS devices not supported */
+static void flex_select_chip(struct mtd_info *mtd, int chipnr)
+{
+
+}
+
+static void flex_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct nandi_controller *nandi = chip->priv;
+	int aligned;
+
+	emiss_nandi_select(nandi, STM_NANDI_HAMMING);
+
+	/* Read bytes until buf is 4-byte aligned */
+	while (len && ((unsigned int)buf & 0x3)) {
+		*buf++ = (uint8_t)(readl(nandi->base + NANDHAM_FLEX_DATA)
+				   & 0xff);
+		len--;
+	};
+
+	/* Use 'BEATS_4'/readsl */
+	if (len > 8) {
+		aligned = len & ~0x3;
+		writel(FLEX_DATA_CFG_BEATS_4 | FLEX_DATA_CFG_CSN,
+		       nandi->base + NANDHAM_FLEX_DATAREAD_CONFIG);
+
+		readsl(nandi->base + NANDHAM_FLEX_DATA, buf, aligned >> 2);
+
+		buf += aligned;
+		len -= aligned;
+
+		writel(FLEX_DATA_CFG_BEATS_1 | FLEX_DATA_CFG_CSN,
+		       nandi->base + NANDHAM_FLEX_DATAREAD_CONFIG);
+	}
+
+	/* Mop up remaining bytes */
+	while (len > 0) {
+		*buf++ = (uint8_t)(readl(nandi->base + NANDHAM_FLEX_DATA)
+				   & 0xff);
+		len--;
+	}
+}
+
+static void flex_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct nandi_controller *nandi = chip->priv;
+	int aligned;
+
+	/* Write bytes until buf is 4-byte aligned */
+	while (len && ((unsigned int)buf & 0x3)) {
+		writel(*buf++, nandi->base + NANDHAM_FLEX_DATA);
+		len--;
+	};
+
+	/* USE 'BEATS_4/writesl */
+	if (len > 8) {
+		aligned = len & ~0x3;
+		writel(FLEX_DATA_CFG_BEATS_4 | FLEX_DATA_CFG_CSN,
+		       nandi->base + NANDHAM_FLEX_DATAWRITE_CONFIG);
+		writesl(nandi->base + NANDHAM_FLEX_DATA, buf, aligned >> 2);
+		buf += aligned;
+		len -= aligned;
+		writel(FLEX_DATA_CFG_BEATS_1 | FLEX_DATA_CFG_CSN,
+		       nandi->base + NANDHAM_FLEX_DATAWRITE_CONFIG);
+	}
+
+	/* Mop up remaining bytes */
+	while (len > 0) {
+		writel(*buf++, nandi->base + NANDHAM_FLEX_DATA);
+		len--;
+	}
+}
+
+int flex_read_raw(struct nandi_controller *nandi,
+			 uint32_t page_addr,
+			 uint32_t col_addr,
+			 uint8_t *buf, uint32_t len)
+{
+	dev_dbg(nandi->dev, "%s %u bytes at [0x%06x,0x%04x]\n",
+		__func__, len, page_addr, col_addr);
+
+	BUG_ON(len & 0x3);
+	BUG_ON((unsigned long)buf & 0x3);
+
+	emiss_nandi_select(nandi, STM_NANDI_HAMMING);
+	nandi_enable_interrupts(nandi, NAND_INT_RBN);
+	reinit_completion(&nandi->rbn_completed);
+
+	writel(FLEX_DATA_CFG_BEATS_4 | FLEX_DATA_CFG_CSN,
+	       nandi->base + NANDHAM_FLEX_DATAREAD_CONFIG);
+
+	flex_cmd(nandi, NAND_CMD_READ0);
+	flex_addr(nandi, col_addr, 2);
+	flex_addr(nandi, page_addr, nandi->extra_addr ? 3 : 2);
+	flex_cmd(nandi, NAND_CMD_READSTART);
+
+	flex_wait_rbn(nandi);
+
+	readsl(nandi->base + NANDHAM_FLEX_DATA, buf, len / 4);
+
+	nandi_disable_interrupts(nandi, NAND_INT_RBN);
+
+	writel(FLEX_DATA_CFG_BEATS_1 | FLEX_DATA_CFG_CSN,
+	       nandi->base + NANDHAM_FLEX_DATAREAD_CONFIG);
+
+	return 0;
+}
+EXPORT_SYMBOL(flex_read_raw);
+
+static int bch_mtd_read_oob(struct mtd_info *mtd,
+			    struct nand_chip *chip, int page)
+{
+	BUG();
+	return 0;
+}
+
+static int bch_mtd_write_oob(struct mtd_info *mtd,
+			     struct nand_chip *chip, int page)
+{
+	BUG();
+	return 0;
+}
+
+static int bch_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+			     uint8_t *buf, int oob_required, int page)
+{
+	BUG();
+	return 0;
+}
+
+static int bch_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+			      const uint8_t *buf, int oob_required)
+{
+	BUG();
+	return 0;
+}
+
+static void bch_hwctl(struct mtd_info *mtd, int mode)
+{
+	BUG();
+}
+
+static int bch_calculate(struct mtd_info *mtd, const uint8_t *dat,
+			 uint8_t *ecc_code)
+{
+	BUG();
+	return 0;
+}
+
+static int bch_correct(struct mtd_info *mtd, uint8_t *dat, uint8_t *read_ecc,
+		       uint8_t *calc_ecc)
+{
+	BUG();
+	return 0;
+}
+
+/*
+ * Initialisation
+ */
+static int bch_check_compatibility(struct nandi_controller *nandi,
+				   struct mtd_info *mtd,
+				   struct nand_chip *chip)
+{
+	if (chip->bits_per_cell > 1)
+		dev_warn(nandi->dev, "MLC NAND not fully supported\n");
+
+	if (chip->options & NAND_BUSWIDTH_16) {
+		dev_err(nandi->dev, "x16 NAND not supported\n");
+		return false;
+	}
+
+	if (nandi->blocks_per_device / 4 > mtd->writesize) {
+		/* Need to implement multi-page BBT support... */
+		dev_err(nandi->dev, "BBT too big to fit in single page\n");
+		return false;
+	}
+
+	if (bch_ecc_sizes[nandi->bch_ecc_mode] * nandi->sectors_per_page >
+	    mtd->oobsize) {
+		dev_err(nandi->dev, "insufficient OOB for selected ECC\n");
+		return false;
+	}
+
+	return true;
+}
+
+/* Select strongest ECC scheme compatible with OOB size */
+static int bch_set_ecc_auto(struct nandi_controller *nandi,
+			    struct mtd_info *mtd)
+{
+	int oob_bytes_per_sector = mtd->oobsize / nandi->sectors_per_page;
+	int try_ecc_modes[] = { BCH_30BIT_ECC, BCH_18BIT_ECC, -1 };
+	int m, ecc_mode;
+
+	for (m = 0; try_ecc_modes[m] >= 0; m++) {
+		ecc_mode = try_ecc_modes[m];
+		if (oob_bytes_per_sector >= bch_ecc_sizes[ecc_mode]) {
+			nandi->bch_ecc_mode = ecc_mode;
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
+
+static void nandi_set_mtd_defaults(struct nandi_controller *nandi,
+				   struct mtd_info *mtd, struct nand_chip *chip)
+{
+	struct nandi_info *info = &nandi->info;
+	int i;
+
+	/* ecclayout */
+	info->ecclayout.eccbytes = mtd->oobsize;
+	for (i = 0; i < 64; i++)
+		info->ecclayout.eccpos[i] = i;
+	info->ecclayout.oobfree[0].offset = 0;
+	info->ecclayout.oobfree[0].length = 0;
+	chip->ecc.mode = NAND_ECC_HW;
+
+	/* nand_chip */
+	chip->controller = &chip->hwcontrol;
+	spin_lock_init(&chip->controller->lock);
+	init_waitqueue_head(&chip->controller->wq);
+	chip->state = FL_READY;
+	chip->priv = nandi;
+	chip->ecc.layout = &info->ecclayout;
+	chip->options |= NAND_NO_SUBPAGE_WRITE;
+
+	chip->cmdfunc = flex_command_lp;
+	chip->read_byte = flex_read_byte;
+	chip->select_chip = flex_select_chip;
+	chip->waitfunc = flex_wait_func;
+	chip->read_buf = flex_read_buf;
+	chip->write_buf = flex_write_buf;
+
+	chip->bbt_options |= NAND_BBT_USE_FLASH;
+
+	/* mtd_info */
+	mtd->owner = THIS_MODULE;
+	mtd->type = MTD_NANDFLASH;
+	mtd->flags = MTD_CAP_NANDFLASH;
+	mtd->ecclayout = &info->ecclayout;
+
+	chip->ecc.hwctl = bch_hwctl;
+	chip->ecc.calculate = bch_calculate;
+	chip->ecc.correct = bch_correct;
+
+	chip->ecc.read_oob = bch_mtd_read_oob;
+	chip->ecc.write_oob = bch_mtd_write_oob;
+
+	chip->ecc.read_page = bch_read;
+	chip->ecc.read_page_raw = bch_read_page_raw;
+	chip->ecc.write_page_raw = bch_write_page_raw;
+	chip->write_page = bch_write;
+	chip->erase = bch_erase;
+
+#ifdef CONFIG_MTD_NAND_STM_BCH_BBT
+	chip->scan_bbt = bch_scan_bbt;
+	chip->block_bad = bch_block_isbad;
+	chip->block_markbad = bch_block_markbad;
+#endif
+}
+
+/*
+ * Timing and Clocks
+ */
+
+static void nandi_clk_enable(struct nandi_controller *nandi)
+{
+	clk_prepare_enable(nandi->emi_clk);
+	clk_prepare_enable(nandi->bch_clk);
+}
+
+static void nandi_clk_disable(struct nandi_controller *nandi)
+{
+	clk_disable_unprepare(nandi->emi_clk);
+	clk_disable_unprepare(nandi->bch_clk);
+}
+
+static struct clk *nandi_clk_setup(struct nandi_controller *nandi,
+				   char *clkname)
+{
+	struct clk *clk;
+	int ret;
+
+	clk = devm_clk_get(nandi->dev, clkname);
+	if (IS_ERR_OR_NULL(clk)) {
+		dev_warn(nandi->dev, "Failed to get %s clock\n", clkname);
+		return NULL;
+	}
+
+	ret = clk_prepare_enable(clk);
+	if (ret) {
+		dev_warn(nandi->dev, "Failed to enable %s clock\n", clkname);
+		return NULL;
+	}
+
+	return clk;
+}
+
+/* Derive Hamming-FLEX timing register values from 'nand_sdr_timings' data */
+static void flex_calc_timing_registers(const struct nand_sdr_timings *spec,
+				       int tCLK, int relax,
+				       uint32_t *ctl_timing,
+				       uint32_t *wen_timing,
+				       uint32_t *ren_timing)
+{
+	int tMAX_HOLD;
+	int n_ctl_setup;
+	int n_ctl_hold;
+	int n_ctl_wb;
+
+	int tMAX_WEN_OFF;
+	int n_wen_on;
+	int n_wen_off;
+
+	int tMAX_REN_OFF;
+	int n_ren_on;
+	int n_ren_off;
+
+	/*
+	 * CTL_TIMING
+	 */
+
+	/*	- SETUP */
+	n_ctl_setup = (PICO_TO_MILI(spec->tCLS_min - spec->tWP_min)
+		       + tCLK - 1)/tCLK;
+	if (n_ctl_setup < 1)
+		n_ctl_setup = 1;
+	n_ctl_setup += relax;
+
+	/*	- HOLD */
+	tMAX_HOLD = spec->tCLH_min;
+	if (spec->tCH_min > tMAX_HOLD)
+		tMAX_HOLD = spec->tCH_min;
+	if (spec->tALH_min > tMAX_HOLD)
+		tMAX_HOLD = spec->tALH_min;
+	if (spec->tDH_min > tMAX_HOLD)
+		tMAX_HOLD = spec->tDH_min;
+	n_ctl_hold = (PICO_TO_MILI(tMAX_HOLD) + tCLK - 1)/tCLK + relax;
+
+	/*	- CE_deassert_hold = 0 */
+
+	/*	- WE_high_to_RBn_low */
+	n_ctl_wb = (PICO_TO_MILI(spec->tWB_max) + tCLK - 1)/tCLK;
+
+	*ctl_timing = ((n_ctl_setup & 0xff) |
+		       (n_ctl_hold & 0xff) << 8 |
+		       (n_ctl_wb & 0xff) << 24);
+
+	/*
+	 * WEN_TIMING
+	 */
+
+	/*	- ON */
+	n_wen_on = (PICO_TO_MILI(spec->tWH_min) + tCLK - 1)/tCLK + relax;
+
+	/*	- OFF */
+	tMAX_WEN_OFF = spec->tWC_min - spec->tWH_min;
+	if (spec->tWP_min > tMAX_WEN_OFF)
+		tMAX_WEN_OFF = spec->tWP_min;
+	n_wen_off = (PICO_TO_MILI(tMAX_WEN_OFF) + tCLK - 1)/tCLK + relax;
+
+	*wen_timing = ((n_wen_on & 0xff) |
+		       (n_wen_off & 0xff) << 8);
+
+	/*
+	 * REN_TIMING
+	 */
+
+	/*	- ON */
+	n_ren_on = (PICO_TO_MILI(spec->tREH_min) + tCLK - 1)/tCLK + relax;
+
+	/*	- OFF */
+	tMAX_REN_OFF = spec->tRC_min - spec->tREH_min;
+	if (spec->tRP_min > tMAX_REN_OFF)
+		tMAX_REN_OFF = spec->tRP_min;
+	if (spec->tREA_max > tMAX_REN_OFF)
+		tMAX_REN_OFF = spec->tREA_max;
+	n_ren_off = (PICO_TO_MILI(tMAX_REN_OFF) + tCLK - 1)/tCLK + 1 + relax;
+
+	*ren_timing = ((n_ren_on & 0xff) |
+		       (n_ren_off & 0xff) << 8);
+}
+
+/* Derive BCH timing register values from 'nand_sdr_timings' data */
+static void bch_calc_timing_registers(const struct nand_sdr_timings *spec,
+				      int tCLK, int relax,
+				      uint32_t *ctl_timing,
+				      uint32_t *wen_timing,
+				      uint32_t *ren_timing)
+{
+	int tMAX_HOLD;
+	int n_ctl_setup;
+	int n_ctl_hold;
+	int n_ctl_wb;
+
+	int n_wen_on;
+	int n_wen_off;
+	int wen_half_on;
+	int wen_half_off;
+
+	int tMAX_REN_ON;
+	int tMAX_CS_DEASSERT;
+	int n_d_latch;
+	int n_telqv;
+	int n_ren_on;
+	int n_ren_off;
+	int ren_half_on;
+	int ren_half_off;
+
+	/*
+	 * CTL_TIMING
+	 */
+
+	/*	- SETUP */
+	if (spec->tCLS_min > spec->tWP_min)
+		n_ctl_setup = (PICO_TO_MILI(spec->tCLS_min - spec->tWP_min)
+			       + tCLK - 1)/tCLK;
+	else
+		n_ctl_setup = 0;
+	n_ctl_setup += relax;
+
+	/*	- HOLD */
+	tMAX_HOLD = spec->tCLH_min;
+	if (spec->tCH_min > tMAX_HOLD)
+		tMAX_HOLD = spec->tCH_min;
+	if (spec->tALH_min > tMAX_HOLD)
+		tMAX_HOLD = spec->tALH_min;
+	if (spec->tDH_min > tMAX_HOLD)
+		tMAX_HOLD = spec->tDH_min;
+	n_ctl_hold = (PICO_TO_MILI(tMAX_HOLD) + tCLK - 1)/tCLK + relax;
+	/*	- CE_deassert_hold = 0 */
+
+	/*	- WE_high_to_RBn_low */
+	n_ctl_wb = (PICO_TO_MILI(spec->tWB_max) + tCLK - 1)/tCLK;
+
+	*ctl_timing = ((n_ctl_setup & 0xff) |
+		       (n_ctl_hold & 0xff) << 8 |
+		       (n_ctl_wb & 0xff) << 24);
+
+	/*
+	 * WEN_TIMING
+	 */
+
+	/*	- ON */
+	n_wen_on = (2 * PICO_TO_MILI(spec->tWH_min) + tCLK - 1)/tCLK;
+	wen_half_on = n_wen_on % 2;
+	n_wen_on /= 2;
+	n_wen_on += relax;
+
+	/*	- OFF */
+	n_wen_off = (2 * PICO_TO_MILI(spec->tWP_min) + tCLK - 1)/tCLK;
+	wen_half_off = n_wen_off % 2;
+	n_wen_off /= 2;
+	n_wen_off += relax;
+
+	*wen_timing = ((n_wen_on & 0xff) |
+		       (n_wen_off & 0xff) << 8 |
+		       (wen_half_on << 16) |
+		       (wen_half_off << 17));
+
+	/*
+	 * REN_TIMING
+	 */
+
+	/*	- ON */
+	tMAX_REN_ON = spec->tRC_min - spec->tRP_min;
+	if (spec->tREH_min > tMAX_REN_ON)
+		tMAX_REN_ON = spec->tREH_min;
+
+	n_ren_on = (2 * PICO_TO_MILI(tMAX_REN_ON) + tCLK - 1)/tCLK;
+	ren_half_on = n_ren_on % 2;
+	n_ren_on /= 2;
+	n_ren_on += relax;
+
+	/*	- OFF */
+	n_ren_off = (2 * PICO_TO_MILI(spec->tREA_max) + tCLK - 1)/tCLK;
+	ren_half_off = n_ren_off % 2;
+	n_ren_off /= 2;
+	n_ren_off += relax;
+
+	/*	- DATA_LATCH */
+	if (PICO_TO_MILI(spec->tREA_max) <=
+	    (PICO_TO_MILI(spec->tRP_min) - (2 * tCLK)))
+		n_d_latch = 0;
+	else if (PICO_TO_MILI(spec->tREA_max) <=
+		 (PICO_TO_MILI(spec->tRP_min) - tCLK))
+		n_d_latch = 1;
+	else if ((spec->tREA_max <= spec->tRP_min) &&
+		 (PICO_TO_MILI(spec->tRHOH_min) >= 2 * tCLK))
+		n_d_latch = 2;
+	else
+		n_d_latch = 3;
+
+	/*	- TELQV */
+	tMAX_CS_DEASSERT = spec->tCOH_min;
+	if (spec->tCHZ_max > tMAX_CS_DEASSERT)
+		tMAX_CS_DEASSERT = spec->tCHZ_max;
+
+	n_telqv = (PICO_TO_MILI(tMAX_CS_DEASSERT) + tCLK - 1)/tCLK;
+
+	*ren_timing = ((n_ren_on & 0xff) |
+		       (n_ren_off & 0xff) << 8 |
+		       (n_d_latch & 0x3) << 16 |
+		       (wen_half_on << 18) |
+		       (wen_half_off << 19) |
+		       (n_telqv & 0xff) << 24);
+}
+
+static void flex_configure_timing_registers(struct nandi_controller *nandi,
+					    const struct nand_sdr_timings *spec,
+					    int relax)
+{
+	uint32_t ctl_timing;
+	uint32_t wen_timing;
+	uint32_t ren_timing;
+	int emi_t_ns;
+
+	/* Select Hamming Controller */
+	emiss_nandi_select(nandi, STM_NANDI_HAMMING);
+
+	/* Get EMI clock (default 100MHz) */
+	if (nandi->emi_clk)
+		emi_t_ns = 1000000000UL / clk_get_rate(nandi->emi_clk);
+	else {
+		dev_warn(nandi->dev,
+			 "No EMI clock available; assuming default 100MHz\n");
+		emi_t_ns = 10;
+	}
+
+	/* Derive timing register values from specification */
+	flex_calc_timing_registers(spec, emi_t_ns, relax,
+				   &ctl_timing, &wen_timing, &ren_timing);
+
+	dev_dbg(nandi->dev,
+		"updating FLEX timing configuration [0x%08x, 0x%08x, 0x%08x]\n",
+		ctl_timing, wen_timing, ren_timing);
+
+	/* Program timing registers */
+	writel(ctl_timing, nandi->base + NANDHAM_CTL_TIMING);
+	writel(wen_timing, nandi->base + NANDHAM_WEN_TIMING);
+	writel(ren_timing, nandi->base + NANDHAM_REN_TIMING);
+}
+
+static void bch_configure_timing_registers(struct nandi_controller *nandi,
+					   const struct nand_sdr_timings *spec,
+					   int relax)
+{
+	uint32_t ctl_timing;
+	uint32_t wen_timing;
+	uint32_t ren_timing;
+	int bch_t_ns;
+
+	/* Select BCH Controller */
+	emiss_nandi_select(nandi, STM_NANDI_BCH);
+
+	/* Get BCH clock (default 200MHz) */
+	if (nandi->bch_clk)
+		bch_t_ns = 1000000000UL / clk_get_rate(nandi->bch_clk);
+	else {
+		dev_warn(nandi->dev,
+			 "No BCH clock available; assuming default 200MHz\n");
+		bch_t_ns = 5;
+	}
+
+	/* Derive timing register values from specification */
+	bch_calc_timing_registers(spec, bch_t_ns, relax,
+				  &ctl_timing, &wen_timing, &ren_timing);
+
+	dev_dbg(nandi->dev,
+		"updating BCH timing configuration [0x%08x, 0x%08x, 0x%08x]\n",
+		ctl_timing, wen_timing, ren_timing);
+
+	/* Program timing registers */
+	writel(ctl_timing, nandi->base + NANDBCH_CTL_TIMING);
+	writel(wen_timing, nandi->base + NANDBCH_WEN_TIMING);
+	writel(ren_timing, nandi->base + NANDBCH_REN_TIMING);
+}
+
+static void nandi_configure_timing_registers(struct nandi_controller *nandi,
+					const struct nand_sdr_timings *spec,
+					int relax)
+{
+	bch_configure_timing_registers(nandi, spec, relax);
+	flex_configure_timing_registers(nandi, spec, relax);
+}
+
+static void nandi_init_hamming(struct nandi_controller *nandi, int emi_bank)
+{
+	dev_dbg(nandi->dev, "%s\n", __func__);
+
+	emiss_nandi_select(nandi, STM_NANDI_HAMMING);
+
+	/* Reset and disable boot-mode controller */
+	writel(BOOT_CFG_RESET, nandi->base + NANDHAM_BOOTBANK_CFG);
+	udelay(1);
+	writel(0x00000000, nandi->base + NANDHAM_BOOTBANK_CFG);
+
+	/* Reset controller */
+	writel(CFG_RESET, nandi->base + NANDHAM_FLEXMODE_CFG);
+	udelay(1);
+	writel(0x00000000, nandi->base + NANDHAM_FLEXMODE_CFG);
+
+	/* Set EMI Bank */
+	writel(0x1 << emi_bank, nandi->base + NANDHAM_FLEX_MUXCTRL);
+
+	/* Enable FLEX mode */
+	writel(CFG_ENABLE_FLEX, nandi->base + NANDHAM_FLEXMODE_CFG);
+
+	/* Configure FLEX_DATA_READ/WRITE for 1-byte access */
+	writel(FLEX_DATA_CFG_BEATS_1 | FLEX_DATA_CFG_CSN,
+	       nandi->base + NANDHAM_FLEX_DATAREAD_CONFIG);
+	writel(FLEX_DATA_CFG_BEATS_1 | FLEX_DATA_CFG_CSN,
+	       nandi->base + NANDHAM_FLEX_DATAREAD_CONFIG);
+
+	/* RBn interrupt on rising edge */
+	writel(NAND_EDGE_CFG_RBN_RISING, nandi->base + NANDHAM_INT_EDGE_CFG);
+
+	/* Enable interrupts */
+	nandi_enable_interrupts(nandi, NAND_INT_ENABLE);
+}
+
+static void nandi_init_bch(struct nandi_controller *nandi, int emi_bank)
+{
+	dev_dbg(nandi->dev, "%s\n", __func__);
+
+	/* Initialise BCH Controller */
+	emiss_nandi_select(nandi, STM_NANDI_BCH);
+
+	/* Reset and disable boot-mode controller */
+	writel(BOOT_CFG_RESET, nandi->base + NANDBCH_BOOTBANK_CFG);
+	udelay(1);
+	writel(0x00000000, nandi->base + NANDBCH_BOOTBANK_CFG);
+
+	/* Reset AFM controller */
+	writel(CFG_RESET, nandi->base + NANDBCH_CONTROLLER_CFG);
+	udelay(1);
+	writel(0x00000000, nandi->base + NANDBCH_CONTROLLER_CFG);
+
+	/* Set EMI Bank */
+	writel(0x1 << emi_bank, nandi->base + NANDBCH_FLEX_MUXCTRL);
+
+	/* Reset ECC stats */
+	writel(CFG_RESET_ECC_ALL, nandi->base + NANDBCH_CONTROLLER_CFG);
+	udelay(1);
+
+	/* Enable AFM */
+	writel(CFG_ENABLE_AFM, nandi->base + NANDBCH_CONTROLLER_CFG);
+
+	/* Configure Read DMA Plugs (values supplied by Validation) */
+	writel(0x00000005, nandi->dma + EMISS_NAND_RD_DMA_PAGE_SIZE);
+	writel(0x00000005, nandi->dma + EMISS_NAND_RD_DMA_MAX_OPCODE_SIZE);
+	writel(0x00000002, nandi->dma + EMISS_NAND_RD_DMA_MIN_OPCODE_SIZE);
+	writel(0x00000001, nandi->dma + EMISS_NAND_RD_DMA_MAX_CHUNK_SIZE);
+	writel(0x00000000, nandi->dma + EMISS_NAND_RD_DMA_MAX_MESSAGE_SIZE);
+
+	/* Configure Write DMA Plugs (values supplied by Validation) */
+	writel(0x00000005, nandi->dma + EMISS_NAND_WR_DMA_PAGE_SIZE);
+	writel(0x00000005, nandi->dma + EMISS_NAND_WR_DMA_MAX_OPCODE_SIZE);
+	writel(0x00000002, nandi->dma + EMISS_NAND_WR_DMA_MIN_OPCODE_SIZE);
+	writel(0x00000001, nandi->dma + EMISS_NAND_WR_DMA_MAX_CHUNK_SIZE);
+	writel(0x00000000, nandi->dma + EMISS_NAND_WR_DMA_MAX_MESSAGE_SIZE);
+
+	nandi_enable_interrupts(nandi, NAND_INT_ENABLE);
+}
+
+static void nandi_init_controller(struct nandi_controller *nandi,
+				  int emi_bank)
+{
+	nandi_init_bch(nandi, emi_bank);
+	nandi_init_hamming(nandi, emi_bank);
+}
+
+/* Initialise working buffers, accomodating DMA alignment constraints */
+static int nandi_init_working_buffers(struct nandi_controller *nandi,
+				      struct nandi_bbt_info *bbt_info,
+				      struct mtd_info *mtd)
+{
+	uint32_t bbt_buf_size;
+	uint32_t buf_size;
+
+	/*	- Page and OOB */
+	buf_size = mtd->writesize + mtd->oobsize + NANDI_BCH_DMA_ALIGNMENT;
+
+	/*	- BBT data (page-size aligned) */
+	bbt_info->bbt_size = nandi->blocks_per_device >> 2; /* 2 bits/block */
+	bbt_buf_size = ALIGN(bbt_info->bbt_size, mtd->writesize);
+	buf_size += bbt_buf_size + NANDI_BCH_DMA_ALIGNMENT;
+
+	/*	- BCH BUF list */
+	buf_size += NANDI_BCH_BUF_LIST_SIZE + NANDI_BCH_DMA_ALIGNMENT;
+
+	/* Allocate bufffer */
+	nandi->buf = devm_kzalloc(nandi->dev, buf_size, GFP_KERNEL);
+	if (!nandi->buf) {
+		dev_err(nandi->dev, "failed to allocate working buffers\n");
+		return -ENOMEM;
+	}
+
+	/* Set/Align buffer pointers */
+	nandi->page_buf = PTR_ALIGN(nandi->buf, NANDI_BCH_DMA_ALIGNMENT);
+	nandi->oob_buf  = nandi->page_buf + mtd->writesize;
+	bbt_info->bbt   = PTR_ALIGN(nandi->oob_buf + mtd->oobsize,
+				    NANDI_BCH_DMA_ALIGNMENT);
+	nandi->buf_list = (uint32_t *)PTR_ALIGN(bbt_info->bbt + bbt_buf_size,
+						NANDI_BCH_DMA_ALIGNMENT);
+	nandi->cached_page = -1;
+
+	return 0;
+}
+
+static int remap_named_resource(struct platform_device *pdev,
+				char *name,
+				void __iomem **io_ptr)
+{
+	struct resource *res, *mem;
+	resource_size_t size;
+	void __iomem *p;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
+	if (!res)
+		return -ENXIO;
+
+	size = resource_size(res);
+
+	mem = devm_request_mem_region(&pdev->dev, res->start, size, name);
+	if (!mem)
+		return -EBUSY;
+
+	p = devm_ioremap_nocache(&pdev->dev, res->start, size);
+	if (!p)
+		return -ENOMEM;
+
+	*io_ptr = p;
+
+	return 0;
+}
+
+static struct nandi_controller *
+nandi_init_resources(struct platform_device *pdev)
+{
+	struct stm_nand_bch_ddata *ddata = platform_get_drvdata(pdev);
+	struct nandi_controller *nandi;
+	int irq;
+	int err;
+
+	nandi = devm_kzalloc(&pdev->dev, sizeof(*nandi), GFP_KERNEL);
+	if (!nandi) {
+		dev_err(&pdev->dev,
+			"failed to allocate NANDi controller data\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	nandi->dev = &pdev->dev;
+
+	err = remap_named_resource(pdev, "emi_nand", &nandi->base);
+	if (err)
+		return ERR_PTR(err);
+
+	err = remap_named_resource(pdev, "emiss", &nandi->emiss);
+	if (err)
+		return ERR_PTR(err);
+
+	nandi->dma = nandi->emiss + EMISS_NAND_DMA;
+	nandi->emisscfg = nandi->emiss + EMISS_CFG;
+
+	irq = platform_get_irq_byname(pdev, "nand_irq");
+	if (irq < 0) {
+		dev_err(&pdev->dev, "failed to find IRQ resource\n");
+		return ERR_PTR(irq);
+	}
+
+	err = devm_request_irq(&pdev->dev, irq, nandi_irq_handler,
+			       IRQF_DISABLED, dev_name(&pdev->dev), nandi);
+	if (err) {
+		dev_err(&pdev->dev, "irq request failed\n");
+		return ERR_PTR(err);
+	}
+
+	nandi->emi_clk = nandi_clk_setup(nandi, "emi");
+	nandi->bch_clk = nandi_clk_setup(nandi, "bch");
+
+	ddata->nandi = nandi;
+
+	return nandi;
+}
+
+static void *stm_bch_dt_get_ddata(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct stm_nand_bch_ddata *ddata;
+	int ecc_strength;
+	int ret;
+
+	ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
+	if (!ddata)
+		return ERR_PTR(-ENOMEM);
+
+	of_property_read_u32(np, "nand-ecc-strength", &ecc_strength);
+	if (ecc_strength == 0)
+		ddata->bch_ecc_cfg = BCH_NO_ECC;
+	else if (ecc_strength == 18)
+		ddata->bch_ecc_cfg = BCH_18BIT_ECC;
+	else if (ecc_strength == 30)
+		ddata->bch_ecc_cfg = BCH_30BIT_ECC;
+	else
+		ddata->bch_ecc_cfg = BCH_ECC_AUTO;
+
+	ret = stm_of_get_nand_banks(&pdev->dev, np, &ddata->bank);
+	if (ret < 0)
+		return ERR_PTR(ret);
+
+	return ddata;
+}
+
+static int stm_nand_bch_probe(struct platform_device *pdev)
+{
+	const char *part_probes[] = { "cmdlinepart", "ofpart", NULL, };
+	struct device_node *np = pdev->dev.of_node;
+	struct mtd_part_parser_data ppdata;
+	struct stm_nand_bch_ddata *ddata;
+	struct stm_nand_bank_data *bank;
+	struct nandi_bbt_info *bbt_info;
+	struct nandi_controller *nandi;
+	struct nandi_info *info;
+	struct nand_chip *chip;
+	struct mtd_info *mtd;
+	int compatible, err;
+
+	if (!np) {
+		dev_err(&pdev->dev, "DT node found\n");
+		return -EINVAL;
+	}
+
+	ddata = stm_bch_dt_get_ddata(pdev);
+	if (IS_ERR(ddata))
+		return PTR_ERR(ddata);
+
+	ppdata.of_node = stm_of_get_partitions_node(np, 0);
+
+	platform_set_drvdata(pdev, ddata);
+
+	nandi = nandi_init_resources(pdev);
+	if (IS_ERR(nandi)) {
+		dev_err(&pdev->dev, "failed to initialise NANDi resources\n");
+		return PTR_ERR(nandi);
+	}
+
+	init_completion(&nandi->seq_completed);
+	init_completion(&nandi->rbn_completed);
+
+	bank = ddata->bank;
+	if (bank)
+		nandi_init_controller(nandi, bank->csn);
+
+	info            = &nandi->info;
+	chip            = &info->chip;
+	bbt_info        = &info->bbt_info;
+	mtd             = &info->mtd;
+	mtd->priv       = chip;
+	mtd->name       = dev_name(&pdev->dev);
+	mtd->dev.parent = &pdev->dev;
+
+	nandi_set_mtd_defaults(nandi, mtd, chip);
+
+	err = nand_scan_ident(mtd, 1, NULL);
+	if (err)
+		return err;
+
+	/*
+	 * Configure timing registers
+	 */
+	if (bank && bank->timing_spec) {
+		dev_info(&pdev->dev, "Using platform timing data\n");
+		nandi_configure_timing_registers(nandi, bank->timing_spec,
+						 bank->timing_relax);
+	} else if (chip->onfi_version) {
+		int mode = fls(onfi_get_async_timing_mode(chip) - 1);
+		const struct nand_sdr_timings *spec =
+			onfi_async_timing_mode_to_sdr_timings(mode);
+
+		/* Modes 4 and 5 (EDO) are not supported on our H/W */
+		if (mode > 3)
+			mode = 3;
+
+		dev_info(&pdev->dev, "Using ONFI Timing Mode %d\n", mode);
+		nandi_configure_timing_registers(nandi,	spec,
+					bank ? bank->timing_relax : 0);
+	} else {
+		dev_warn(&pdev->dev, "No timing data available\n");
+	}
+
+	if (mtd->writesize < NANDI_BCH_SECTOR_SIZE) {
+		dev_err(nandi->dev,
+			"page size incompatible with BCH ECC sector\n");
+		return -EINVAL;
+	}
+
+	/* Derive some working variables */
+	nandi->sectors_per_page = mtd->writesize / NANDI_BCH_SECTOR_SIZE;
+	nandi->blocks_per_device = mtd->size >> chip->phys_erase_shift;
+	nandi->extra_addr = ((chip->chipsize >> chip->page_shift) >
+			     0x10000) ? true : false;
+	mtd->writebufsize = mtd->writesize;
+
+	/* Set ECC mode */
+	if (ddata->bch_ecc_cfg == BCH_ECC_AUTO) {
+		err = bch_set_ecc_auto(nandi, mtd);
+		if (err) {
+			dev_err(nandi->dev, "insufficient OOB for BCH ECC\n");
+			return err;
+		}
+	} else {
+		nandi->bch_ecc_mode = ddata->bch_ecc_cfg;
+	}
+
+	chip->ecc.size = NANDI_BCH_SECTOR_SIZE;
+	chip->ecc.bytes = mtd->oobsize;
+	chip->ecc.strength = bch_ecc_strength[nandi->bch_ecc_mode];
+
+	info->ecclayout.eccbytes =
+		nandi->sectors_per_page * bch_ecc_sizes[nandi->bch_ecc_mode];
+
+	compatible = bch_check_compatibility(nandi, mtd, chip);
+	if (!compatible) {
+		dev_err(nandi->dev,
+			"NAND device incompatible with NANDi/BCH Controller\n");
+		return -EINVAL;
+	}
+
+	/* Tune BCH programs according to device found and ECC mode */
+	bch_configure_progs(nandi);
+
+	err = nandi_init_working_buffers(nandi, bbt_info, mtd);
+	if (err)
+		return err;
+
+	err = nand_scan_tail(mtd);
+	if (err)
+		return err;
+
+#ifdef CONFIG_MTD_NAND_STM_BCH_BBT
+	nandi_dump_bad_blocks(nandi);
+#endif
+	/* Add partitions */
+	return mtd_device_parse_register(mtd, part_probes, &ppdata,
+					bank->partitions, bank->nr_partitions);
+}
+
+static int stm_nand_bch_remove(struct platform_device *pdev)
+{
+	struct stm_nand_bch_ddata *ddata = platform_get_drvdata(pdev);
+	struct nandi_controller *nandi = ddata->nandi;
+
+	nand_release(&nandi->info.mtd);
+
+	nandi_clk_disable(nandi);
+
+	return 0;
+}
+
+static int stm_nand_bch_suspend(struct device *dev)
+{
+	struct stm_nand_bch_ddata *ddata = dev_get_drvdata(dev);
+	struct nandi_controller *nandi = ddata->nandi;
+
+	nandi_clk_disable(nandi);
+
+	return 0;
+}
+static int stm_nand_bch_resume(struct device *dev)
+{
+	struct stm_nand_bch_ddata *ddata = dev_get_drvdata(dev);
+	struct nandi_controller *nandi = ddata->nandi;
+
+	nandi_clk_enable(nandi);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(stm_nand_bch_pm_ops,
+			 stm_nand_bch_suspend,
+			 stm_nand_bch_resume);
+
+static struct of_device_id nand_bch_match[] = {
+	{ .compatible = "st,nand-bch", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, nand_bch_match);
+
+static struct platform_driver stm_nand_bch_driver = {
+	.probe	= stm_nand_bch_probe ,
+	.remove	= stm_nand_bch_remove,
+	.driver	= {
+		.name	= "stm-nand-bch",
+		.owner	= THIS_MODULE,
+		.of_match_table = of_match_ptr(nand_bch_match),
+		.pm	= &stm_nand_bch_pm_ops,
+	},
+};
+module_platform_driver(stm_nand_bch_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Angus Clark");
+MODULE_DESCRIPTION("STM NAND BCH driver");
diff --git a/drivers/mtd/nand/stm_nand_dt.c b/drivers/mtd/nand/stm_nand_dt.c
new file mode 100644
index 0000000..4f59873
--- /dev/null
+++ b/drivers/mtd/nand/stm_nand_dt.c
@@ -0,0 +1,109 @@
+/*
+ * drivers/mtd/nand/stm_nand_dt.c
+ *
+ * Support for NANDi BCH Controller Device Tree component
+ *
+ * Copyright (c) 2014 STMicroelectronics Limited
+ * Author: Author: Srinivas Kandagatla <srinivas.kandagatla@st.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/byteorder/generic.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/stm_nand.h>
+
+#include "stm_nand_regs.h"
+
+/**
+* stm_of_get_partitions_node - get partitions node from stm-nand type devices.
+*
+* @dev		device pointer to use for devm allocations.
+* @np		device node of the driver.
+* @bank_nr	which bank number to use to get partitions.
+*
+* Returns a node pointer if found, with refcount incremented, use
+* of_node_put() on it when done.
+*
+*/
+struct device_node *stm_of_get_partitions_node(struct device_node *np,
+					       int bank_nr)
+{
+	struct device_node *banknp, *partsnp = NULL;
+	char name[10];
+
+	sprintf(name, "bank%d", bank_nr);
+	banknp = of_get_child_by_name(np, name);
+	if (banknp)
+		return NULL;
+
+	partsnp = of_get_child_by_name(banknp, "partitions");
+	of_node_put(banknp);
+
+	return partsnp;
+}
+EXPORT_SYMBOL(stm_of_get_partitions_node);
+
+/**
+ * stm_of_get_nand_banks - Get nand banks info from a given device node.
+ *
+ * @dev			device pointer to use for devm allocations.
+ * @np			device node of the driver.
+ * @banksptr		double pointer to banks which is allocated
+ *			and filled with bank data.
+ *
+ * Returns a count of banks found in the given device node.
+ *
+ */
+int stm_of_get_nand_banks(struct device *dev, struct device_node *np,
+			  struct stm_nand_bank_data **banksptr)
+{
+	struct stm_nand_bank_data *banks;
+	struct device_node *banknp;
+	int nr_banks = 0;
+
+	if (!np)
+		return -ENODEV;
+
+	nr_banks = of_get_child_count(np);
+	if (!nr_banks) {
+		dev_err(dev, "No NAND banks specified in DT: %s\n",
+		np->full_name);
+		return -EINVAL;
+	}
+
+	*banksptr = devm_kzalloc(dev, sizeof(*banks) * nr_banks, GFP_KERNEL);
+	banks = *banksptr;
+	banknp = NULL;
+
+	for_each_child_of_node(np, banknp) {
+		int bank = 0;
+
+		of_property_read_u32(banknp, "st,nand-csn", &banks[bank].csn);
+
+		if (of_get_nand_bus_width(banknp) == 16)
+			banks[bank].options |= NAND_BUSWIDTH_16;
+		if (of_get_nand_on_flash_bbt(banknp))
+			banks[bank].bbt_options |= NAND_BBT_USE_FLASH;
+
+		banks[bank].nr_partitions = 0;
+		banks[bank].partitions = NULL;
+
+		of_property_read_u32(banknp, "st,nand-timing-relax",
+				     &banks[bank].timing_relax);
+		bank++;
+	}
+
+	return nr_banks;
+}
+EXPORT_SYMBOL(stm_of_get_nand_banks);
diff --git a/drivers/mtd/nand/stm_nand_dt.h b/drivers/mtd/nand/stm_nand_dt.h
new file mode 100644
index 0000000..53e1321
--- /dev/null
+++ b/drivers/mtd/nand/stm_nand_dt.h
@@ -0,0 +1,24 @@
+/*
+ * drivers/mtd/nand/stm_nand_dt.h
+ *
+ * Support for NANDi BCH Controller Device Tree component
+ *
+ * Copyright (c) 2014 STMicroelectronics Limited
+ * Author: Author: Srinivas Kandagatla <srinivas.kandagatla@st.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef STM_NAND_DT_H
+#define STM_NAND_DT_H
+
+struct device_node *stm_of_get_partitions_node(struct device_node *np,
+					       int bank_nr);
+
+int stm_of_get_nand_banks(struct device *dev, struct device_node *np,
+			  struct stm_nand_bank_data **banksp);
+
+#endif /* STM_NAND_DT_H */
diff --git a/include/linux/mtd/stm_nand.h b/include/linux/mtd/stm_nand.h
new file mode 100644
index 0000000..c3c805f
--- /dev/null
+++ b/include/linux/mtd/stm_nand.h
@@ -0,0 +1,147 @@
+/*
+ * include/linux/mtd/stm_mtd.h
+ *
+ * Support for STMicroelectronics NAND Controllers
+ *
+ * Copyright (c) 2014 STMicroelectronics Limited
+ * Author: Angus Clark <Angus.Clark@st.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef __LINUX_STM_NAND_H
+#define __LINUX_STM_NAND_H
+
+#include <linux/io.h>
+#include <linux/mtd/nand.h>
+
+/* ECC Modes */
+enum stm_nand_bch_ecc_config {
+	BCH_18BIT_ECC = 0,
+	BCH_30BIT_ECC,
+	BCH_NO_ECC,
+	BCH_ECC_RSRV,
+	BCH_ECC_AUTO,
+};
+
+/* Bad Block Table (BBT) */
+struct nandi_bbt_info {
+	uint32_t	bbt_size;		/* Size of bad-block table */
+	uint32_t	bbt_vers[2];		/* Version (Primary/Mirror) */
+	uint32_t	bbt_block[2];		/* Block No. (Primary/Mirror) */
+	uint8_t		*bbt;			/* Table data */
+};
+
+/* Collection of MTD/NAND device information */
+struct nandi_info {
+	struct mtd_info		mtd;		/* MTD info */
+	struct nand_chip	chip;		/* NAND chip info */
+
+	struct nand_ecclayout	ecclayout;	/* MTD ECC layout */
+	struct nandi_bbt_info	bbt_info;	/* Bad Block Table */
+	int			nr_parts;	/* Number of MTD partitions */
+	struct	mtd_partition	*parts;		/* MTD partitions */
+};
+
+/* NANDi Controller (Hamming/BCH) */
+struct nandi_controller {
+	void __iomem		*base;		/* Controller base*/
+	void __iomem		*emiss;		/* EMISS control base */
+	void __iomem		*emisscfg;	/* EMISS config base */
+	void __iomem		*dma;		/* DMA control base */
+
+	struct clk		*bch_clk;
+	struct clk		*emi_clk;
+						/* IRQ-triggered Completions: */
+	struct completion	seq_completed;	/*   SEQ Over */
+	struct completion	rbn_completed;	/*   RBn */
+
+	struct device		*dev;
+
+	int			bch_ecc_mode;	/* ECC mode */
+	bool			extra_addr;	/* Extra address cycle */
+
+	uint32_t		blocks_per_device;
+	uint32_t		sectors_per_page;
+
+	uint8_t			*buf;		/* Some buffers to use */
+	uint8_t			*page_buf;
+	uint8_t			*oob_buf;
+	uint32_t		*buf_list;
+
+	int			cached_page;	/* page number of page in */
+						/* 'page_buf'             */
+
+	struct nandi_info	info;		/* NAND device info */
+};
+
+/*
+ * Board-level specification relating to a 'bank' of NAND Flash
+ */
+struct stm_nand_bank_data {
+	int			csn;
+	int			nr_partitions;
+	struct mtd_partition	*partitions;
+	unsigned int		options;
+	unsigned int		bbt_options;
+
+	struct nand_sdr_timings *timing_spec;
+
+	/*
+	 * No. of IP clk cycles by which to 'relax' the timing configuration.
+	 * Required on some boards to to accommodate board-level limitations.
+	 * Used in conjunction with 'nand_sdr_timings' and ONFI configuration.
+	 */
+	int			timing_relax;
+};
+
+struct stm_nand_bch_ddata {
+	struct nandi_controller *nandi;
+	struct stm_nand_bank_data *bank;
+	enum stm_nand_bch_ecc_config bch_ecc_cfg;
+};
+
+enum nandi_controllers {
+	STM_NANDI_UNCONFIGURED,
+	STM_NANDI_HAMMING,
+	STM_NANDI_BCH
+};
+
+extern int flex_read_raw(struct nandi_controller *nandi,
+			 uint32_t page_addr,
+			 uint32_t col_addr,
+			 uint8_t *buf, uint32_t len);
+extern uint8_t bch_write_page(struct nandi_controller *nandi,
+			      loff_t offs, const uint8_t *buf);
+extern uint8_t bch_erase_block(struct nandi_controller *nandi,
+			       loff_t offs);
+extern int bch_read_page(struct nandi_controller *nandi,
+			 loff_t offs, uint8_t *buf);
+
+#define EMISS_NAND_CONFIG_HAMMING_NOT_BCH 	(0x1 << 6)
+
+static inline void emiss_nandi_select(struct nandi_controller *nandi,
+				      enum nandi_controllers controller)
+{
+	unsigned v;
+
+	v = readl(nandi->emisscfg);
+
+	if (controller == STM_NANDI_HAMMING) {
+		if (v & EMISS_NAND_CONFIG_HAMMING_NOT_BCH)
+			return;
+		v |= EMISS_NAND_CONFIG_HAMMING_NOT_BCH;
+	} else {
+		if (!(v & EMISS_NAND_CONFIG_HAMMING_NOT_BCH))
+			return;
+		v &= ~EMISS_NAND_CONFIG_HAMMING_NOT_BCH;
+	}
+
+	writel(v, nandi->emisscfg);
+	readl(nandi->emisscfg);
+}
+
+#endif /* __LINUX_STM_NAND_H */
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH 7/8] mtd: nand: stm_nand_bch: add support for ST's BCH NAND controller
@ 2014-08-13  9:12   ` Lee Jones
  0 siblings, 0 replies; 51+ messages in thread
From: Lee Jones @ 2014-08-13  9:12 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: pekon, computersforpeace, lee.jones, kernel, linux-mtd

Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
 drivers/mtd/nand/Kconfig        |    7 +
 drivers/mtd/nand/Makefile       |    1 +
 drivers/mtd/nand/stm_nand_bch.c | 1618 +++++++++++++++++++++++++++++++++++++++
 drivers/mtd/nand/stm_nand_dt.c  |  109 +++
 drivers/mtd/nand/stm_nand_dt.h  |   24 +
 include/linux/mtd/stm_nand.h    |  147 ++++
 6 files changed, 1906 insertions(+)
 create mode 100644 drivers/mtd/nand/stm_nand_bch.c
 create mode 100644 drivers/mtd/nand/stm_nand_dt.c
 create mode 100644 drivers/mtd/nand/stm_nand_dt.h
 create mode 100644 include/linux/mtd/stm_nand.h

diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index f1cf503..2738eec 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -513,4 +513,11 @@ 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_STM_BCH
+	tristate "STMicroelectronics: NANDi BCH Controller"
+	depends on ARCH_STI
+	depends on OF
+	help
+	  Adds support for the STMicroelectronics NANDi BCH Controller.
+
 endif # MTD_NAND
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index a035e7c..60f374b 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -46,6 +46,7 @@ obj-$(CONFIG_MTD_NAND_NUC900)		+= nuc900_nand.o
 obj-$(CONFIG_MTD_NAND_MPC5121_NFC)	+= mpc5121_nfc.o
 obj-$(CONFIG_MTD_NAND_RICOH)		+= r852.o
 obj-$(CONFIG_MTD_NAND_JZ4740)		+= jz4740_nand.o
+obj-$(CONFIG_MTD_NAND_STM_BCH)		+= stm_nand_bch.o stm_nand_dt.o
 obj-$(CONFIG_MTD_NAND_GPMI_NAND)	+= gpmi-nand/
 obj-$(CONFIG_MTD_NAND_XWAY)		+= xway_nand.o
 obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH)	+= bcm47xxnflash/
diff --git a/drivers/mtd/nand/stm_nand_bch.c b/drivers/mtd/nand/stm_nand_bch.c
new file mode 100644
index 0000000..a104343
--- /dev/null
+++ b/drivers/mtd/nand/stm_nand_bch.c
@@ -0,0 +1,1618 @@
+/*
+ * Support for STMicroelectronics NANDi BCH Controller
+ *
+ * Copyright (c) 2014 STMicroelectronics Limited
+ *
+ * Authors: Angus Clark <Angus.Clark@st.com>
+ *	    Lee Jones <lee.jones@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/completion.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/stm_nand_bbt.h>
+#include <linux/mtd/stm_nand.h>
+#include <linux/mtd/partitions.h>
+
+#include "stm_nand_dt.h"
+#include "stm_nand_regs.h"
+
+/* NANDi BCH Controller properties */
+#define NANDI_BCH_SECTOR_SIZE			1024
+#define NANDI_BCH_DMA_ALIGNMENT			64
+#define NANDI_BCH_MAX_BUF_LIST			8
+#define NANDI_BCH_BUF_LIST_SIZE			(4 * NANDI_BCH_MAX_BUF_LIST)
+
+/* ONFI define 6 timing modes */
+#define ST_NAND_ONFI_TIMING_MODES		6
+
+#define PICO_TO_MILI(pico)			(pico / 1000)
+
+static int bch_ecc_strength[] = {
+	[BCH_18BIT_ECC] = 18,
+	[BCH_30BIT_ECC] = 30,
+	[BCH_NO_ECC] = 0,
+};
+
+/* BCH 'program' structure */
+struct bch_prog {
+	u32	multi_cs_addr[3];
+	u32	multi_cs_config;
+	u8	seq[16];
+	u32	addr;
+	u32	extra;
+	u8	cmd[4];
+	u32	reserved1;
+	u32	gen_cfg;
+	u32	delay;
+	u32	reserved2;
+	u32	seq_cfg;
+};
+
+/* BCH template programs (modified on-the-fly) */
+static struct bch_prog bch_prog_read_page = {
+	.cmd = {
+		NAND_CMD_READ0,
+		NAND_CMD_READSTART,
+	},
+	.seq = {
+		BCH_ECC_SCORE(0),
+		BCH_CMD_ADDR,
+		BCH_CL_CMD_1,
+		BCH_DATA_2_SECTOR,
+		BCH_STOP,
+	},
+	.gen_cfg = (GEN_CFG_DATA_8_NOT_16 |
+		    GEN_CFG_EXTRA_ADD_CYCLE |
+		    GEN_CFG_LAST_SEQ_NODE),
+	.seq_cfg = SEQ_CFG_GO_STOP,
+};
+
+static struct bch_prog bch_prog_write_page = {
+	.cmd = {
+		NAND_CMD_SEQIN,
+		NAND_CMD_PAGEPROG,
+		NAND_CMD_STATUS,
+	},
+	.seq = {
+		BCH_CMD_ADDR,
+		BCH_DATA_4_SECTOR,
+		BCH_CL_CMD_1,
+		BCH_CL_CMD_2,
+		BCH_OP_ERR,
+		BCH_STOP,
+	},
+	.gen_cfg = (GEN_CFG_DATA_8_NOT_16 |
+		    GEN_CFG_EXTRA_ADD_CYCLE |
+		    GEN_CFG_LAST_SEQ_NODE),
+	.seq_cfg = (SEQ_CFG_GO_STOP |
+		    SEQ_CFG_DATA_WRITE),
+};
+
+static struct bch_prog bch_prog_erase_block = {
+	.seq = {
+		BCH_CL_CMD_1,
+		BCH_AL_EX_0,
+		BCH_AL_EX_1,
+		BCH_AL_EX_2,
+		BCH_CL_CMD_2,
+		BCH_CL_CMD_3,
+		BCH_OP_ERR,
+		BCH_STOP,
+	},
+	.cmd = {
+		NAND_CMD_ERASE1,
+		NAND_CMD_ERASE1,
+		NAND_CMD_ERASE2,
+		NAND_CMD_STATUS,
+	},
+	.gen_cfg = (GEN_CFG_DATA_8_NOT_16 |
+		    GEN_CFG_EXTRA_ADD_CYCLE |
+		    GEN_CFG_LAST_SEQ_NODE),
+	.seq_cfg = (SEQ_CFG_GO_STOP |
+		    SEQ_CFG_ERASE),
+};
+
+/* Configure BCH read/write/erase programs */
+static void bch_configure_progs(struct nandi_controller *nandi)
+{
+	uint8_t data_opa = ffs(nandi->sectors_per_page) - 1;
+	uint8_t data_instr = BCH_INSTR(BCH_OPC_DATA, data_opa);
+	uint32_t gen_cfg_ecc = nandi->bch_ecc_mode << GEN_CFG_ECC_SHIFT;
+
+	/* Set 'DATA' instruction */
+	bch_prog_read_page.seq[3] = data_instr;
+	bch_prog_write_page.seq[1] = data_instr;
+
+	/* Set ECC mode */
+	bch_prog_read_page.gen_cfg |= gen_cfg_ecc;
+	bch_prog_write_page.gen_cfg |= gen_cfg_ecc;
+	bch_prog_erase_block.gen_cfg |= gen_cfg_ecc;
+
+	/*
+	 * Template sequences above are defined for devices that use 5 address
+	 * cycles for page Read/Write operations (and 3 for Erase operations).
+	 * Update sequences for devices that use 4 address cycles.
+	 */
+	if (!nandi->extra_addr) {
+		/* Clear 'GEN_CFG_EXTRA_ADD_CYCLE' flag */
+		bch_prog_read_page.gen_cfg &= ~GEN_CFG_EXTRA_ADD_CYCLE;
+		bch_prog_write_page.gen_cfg &= ~GEN_CFG_EXTRA_ADD_CYCLE;
+		bch_prog_erase_block.gen_cfg &= ~GEN_CFG_EXTRA_ADD_CYCLE;
+
+		/* Configure Erase sequence for 2 address cycles */
+		/* (page address) */
+		bch_prog_erase_block.seq[0] = BCH_CL_CMD_1;
+		bch_prog_erase_block.seq[1] = BCH_AL_EX_0;
+		bch_prog_erase_block.seq[2] = BCH_AL_EX_1;
+		bch_prog_erase_block.seq[3] = BCH_CL_CMD_2;
+		bch_prog_erase_block.seq[4] = BCH_CL_CMD_3;
+		bch_prog_erase_block.seq[5] = BCH_OP_ERR;
+		bch_prog_erase_block.seq[6] = BCH_STOP;
+	}
+}
+
+/*
+ * NANDi Interrupts (shared by Hamming and BCH controllers)
+ */
+static irqreturn_t nandi_irq_handler(int irq, void *dev)
+{
+	struct nandi_controller *nandi = dev;
+	unsigned int status;
+
+	status = readl(nandi->base + NANDBCH_INT_STA);
+
+	if (nandi->bch_ecc_mode && (status & NANDBCH_INT_SEQNODESOVER)) {
+		/* BCH */
+		writel(NANDBCH_INT_CLR_SEQNODESOVER,
+		       nandi->base + NANDBCH_INT_CLR);
+		complete(&nandi->seq_completed);
+		return IRQ_HANDLED;
+	}
+	if (status & NAND_INT_RBN) {
+		/* Hamming */
+		writel(NAND_INT_CLR_RBN, nandi->base + NANDHAM_INT_CLR);
+		complete(&nandi->rbn_completed);
+		return IRQ_HANDLED;
+	}
+
+	return IRQ_NONE;
+}
+
+static void nandi_enable_interrupts(struct nandi_controller *nandi,
+				    uint32_t irqs)
+{
+	uint32_t val;
+
+	val = readl(nandi->base + NANDBCH_INT_EN);
+	val |= irqs;
+	writel(val, nandi->base + NANDBCH_INT_EN);
+}
+
+static void nandi_disable_interrupts(struct nandi_controller *nandi,
+				     uint32_t irqs)
+{
+	uint32_t val;
+
+	val = readl(nandi->base + NANDBCH_INT_EN);
+	val &= ~irqs;
+	writel(val, nandi->base + NANDBCH_INT_EN);
+}
+
+/*
+ * BCH Operations
+ */
+static inline void bch_load_prog_cpu(struct nandi_controller *nandi,
+				     struct bch_prog *prog)
+{
+	uint32_t *src = (uint32_t *)prog;
+	uint32_t *dst = (uint32_t *)(nandi->base + NANDBCH_ADDRESS_REG_1);
+	int i;
+
+	for (i = 0; i < 16; i++) {
+		/* Skip registers marked as "reserved" */
+		if (i != 11 && i != 14)
+			writel(*src, dst);
+		dst++;
+		src++;
+	}
+}
+
+static void bch_wait_seq(struct nandi_controller *nandi)
+{
+	int ret;
+
+	ret = wait_for_completion_timeout(&nandi->seq_completed, HZ/2);
+	if (!ret)
+		dev_err(nandi->dev, "BCH Seq timeout\n");
+}
+
+uint8_t bch_erase_block(struct nandi_controller *nandi,
+			loff_t offs)
+{
+	struct nand_chip *chip = &nandi->info.chip;
+	struct bch_prog *prog = &bch_prog_erase_block;
+	uint8_t status;
+
+	dev_dbg(nandi->dev, "%s: offs = 0x%012llx\n", __func__, offs);
+
+	prog->extra = (uint32_t)(offs >> chip->page_shift);
+
+	emiss_nandi_select(nandi, STM_NANDI_BCH);
+
+	nandi_enable_interrupts(nandi, NANDBCH_INT_SEQNODESOVER);
+	reinit_completion(&nandi->seq_completed);
+
+	bch_load_prog_cpu(nandi, prog);
+
+	bch_wait_seq(nandi);
+
+	nandi_disable_interrupts(nandi, NANDBCH_INT_SEQNODESOVER);
+
+	status = (uint8_t)(readl(nandi->base +
+				 NANDBCH_CHECK_STATUS_REG_A) & 0xff);
+
+	return status;
+}
+EXPORT_SYMBOL(bch_erase_block);
+
+static int bch_erase(struct mtd_info *mtd, int page)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct nandi_controller *nandi = chip->priv;
+	uint32_t page_size = mtd->writesize;
+	loff_t offs = (loff_t)page * page_size;
+
+	return bch_erase_block(nandi, offs);
+}
+
+/*
+ * Detect an erased page, tolerating and correcting up to a specified number of
+ * bits at '0'.  (For many devices, it is now deemed within spec for an erased
+ * page to include a number of bits at '0', either as a result of read-disturb
+ * behaviour or 'stuck-at-zero' failures.)  Returns the number of corrected
+ * bits, or a '-1' if we have exceeded the maximum number of bits at '0' (likely
+ * to be a genuine uncorrectable ECC error).  In the latter case, the data must
+ * be returned unmodified, in accordance with the MTD API.
+ */
+static int check_erased_page(uint8_t *data, uint32_t page_size, int max_zeros)
+{
+	uint8_t *b = data;
+	int zeros = 0;
+	int i;
+
+	for (i = 0; i < page_size; i++) {
+		zeros += hweight8(~*b++);
+		if (zeros > max_zeros)
+			return -1;
+	}
+
+	if (zeros)
+		memset(data, 0xff, page_size);
+
+	return zeros;
+}
+
+/* Returns the number of ECC errors, or '-1' for uncorrectable error */
+int bch_read_page(struct nandi_controller *nandi,
+		  loff_t offs,
+		  uint8_t *buf)
+{
+	struct nand_chip *chip = &nandi->info.chip;
+	struct bch_prog *prog = &bch_prog_read_page;
+	uint32_t page_size = nandi->info.mtd.writesize;
+	unsigned long list_phys;
+	unsigned long buf_phys;
+	uint32_t ecc_err;
+	int ret = 0;
+
+	dev_dbg(nandi->dev, "%s: offs = 0x%012llx\n", __func__, offs);
+
+	BUG_ON(offs & (NANDI_BCH_DMA_ALIGNMENT - 1));
+
+	emiss_nandi_select(nandi, STM_NANDI_BCH);
+
+	nandi_enable_interrupts(nandi, NANDBCH_INT_SEQNODESOVER);
+	reinit_completion(&nandi->seq_completed);
+
+	/* Reset ECC stats */
+	writel(CFG_RESET_ECC_ALL | CFG_ENABLE_AFM,
+	       nandi->base + NANDBCH_CONTROLLER_CFG);
+	writel(CFG_ENABLE_AFM, nandi->base + NANDBCH_CONTROLLER_CFG);
+
+	prog->addr = (uint32_t)((offs >> (chip->page_shift - 8)) & 0xffffff00);
+
+	buf_phys = dma_map_single(NULL, buf, page_size, DMA_FROM_DEVICE);
+
+	memset(nandi->buf_list, 0x00, NANDI_BCH_BUF_LIST_SIZE);
+	nandi->buf_list[0] = buf_phys | (nandi->sectors_per_page - 1);
+
+	list_phys = dma_map_single(NULL, nandi->buf_list,
+				   NANDI_BCH_BUF_LIST_SIZE, DMA_TO_DEVICE);
+
+	writel(list_phys, nandi->base + NANDBCH_BUFFER_LIST_PTR);
+
+	bch_load_prog_cpu(nandi, prog);
+
+	bch_wait_seq(nandi);
+
+	nandi_disable_interrupts(nandi, NANDBCH_INT_SEQNODESOVER);
+
+	dma_unmap_single(NULL, list_phys, NANDI_BCH_BUF_LIST_SIZE,
+			 DMA_TO_DEVICE);
+	dma_unmap_single(NULL, buf_phys, page_size, DMA_FROM_DEVICE);
+
+	/* Use the maximum per-sector ECC count! */
+	ecc_err = readl(nandi->base + NANDBCH_ECC_SCORE_REG_A) & 0xff;
+	if (ecc_err == 0xff) {
+		/*
+		 * Downgrade uncorrectable ECC error for an erased page,
+		 * tolerating 'bch_ecc_strength' bits at zero.
+		 */
+		ret = check_erased_page(buf, page_size, chip->ecc.strength);
+		if (ret >= 0)
+			dev_dbg(nandi->dev,
+				"%s: erased page detected: \n"
+				"  downgrading uncorrectable ECC error.\n",
+				__func__);
+	} else {
+		ret = (int)ecc_err;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(bch_read_page);
+
+static int bch_read(struct mtd_info *mtd, struct nand_chip *chip,
+			 uint8_t *buf, int oob_required, int page)
+{
+	struct nandi_controller *nandi = chip->priv;
+	uint32_t page_size = mtd->writesize;
+	loff_t offs = (loff_t)page * page_size;
+	bool bounce = false;
+	uint8_t *p;
+	int ret;
+
+	if (((unsigned int)buf & (NANDI_BCH_DMA_ALIGNMENT - 1)) ||
+	    (!virt_addr_valid(buf))) /* vmalloc'd buffer! */
+		bounce = true;
+
+	p = bounce ? nandi->page_buf : buf;
+
+	ret = bch_read_page(nandi, offs, p);
+	if (ret < 0)
+		mtd->ecc_stats.failed++;
+	else
+		mtd->ecc_stats.corrected += ret;
+
+	if (bounce)
+		memcpy(buf, p, page_size);
+
+	return ret;
+}
+
+/* Returns the status of the NAND device following the write operation */
+uint8_t bch_write_page(struct nandi_controller *nandi,
+		       loff_t offs, const uint8_t *buf)
+{
+	struct nand_chip *chip = &nandi->info.chip;
+	struct bch_prog *prog = &bch_prog_write_page;
+	uint32_t page_size = nandi->info.mtd.writesize;
+	uint8_t *p = (uint8_t *)buf;
+	unsigned long list_phys;
+	unsigned long buf_phys;
+	uint8_t status;
+	bool bounce = false;
+
+	dev_dbg(nandi->dev, "%s: offs = 0x%012llx\n", __func__, offs);
+
+	BUG_ON(offs & (page_size - 1));
+
+	if (((unsigned long)buf & (NANDI_BCH_DMA_ALIGNMENT - 1)) ||
+	    !virt_addr_valid(buf)) { /* vmalloc'd buffer! */
+		bounce = true;
+	}
+
+	if (bounce) {
+		memcpy(nandi->page_buf, buf, page_size);
+		p = nandi->page_buf;
+		nandi->cached_page = -1;
+	}
+
+	emiss_nandi_select(nandi, STM_NANDI_BCH);
+
+	nandi_enable_interrupts(nandi, NANDBCH_INT_SEQNODESOVER);
+	reinit_completion(&nandi->seq_completed);
+
+	prog->addr = (uint32_t)((offs >> (chip->page_shift - 8)) & 0xffffff00);
+
+	buf_phys = dma_map_single(NULL, p, page_size, DMA_TO_DEVICE);
+	memset(nandi->buf_list, 0x00, NANDI_BCH_BUF_LIST_SIZE);
+	nandi->buf_list[0] = buf_phys | (nandi->sectors_per_page - 1);
+
+	list_phys = dma_map_single(NULL, nandi->buf_list,
+				   NANDI_BCH_BUF_LIST_SIZE, DMA_TO_DEVICE);
+
+	writel(list_phys, nandi->base + NANDBCH_BUFFER_LIST_PTR);
+
+	bch_load_prog_cpu(nandi, prog);
+
+	bch_wait_seq(nandi);
+
+	nandi_disable_interrupts(nandi, NANDBCH_INT_SEQNODESOVER);
+
+	dma_unmap_single(NULL, list_phys, NANDI_BCH_BUF_LIST_SIZE,
+			 DMA_TO_DEVICE);
+	dma_unmap_single(NULL, buf_phys, page_size, DMA_FROM_DEVICE);
+
+	status = (uint8_t)(readl(nandi->base +
+				 NANDBCH_CHECK_STATUS_REG_A) & 0xff);
+
+	return status;
+}
+EXPORT_SYMBOL(bch_write_page);
+
+static int bch_write(struct mtd_info *mtd, struct nand_chip *chip,
+		     uint32_t offset, int data_len, const uint8_t *buf,
+		     int oob_required, int page, int cached, int raw)
+{
+	struct nandi_controller *nandi = chip->priv;
+	uint32_t page_size = mtd->writesize;
+	loff_t offs = (loff_t)page * page_size;
+	int ret;
+
+	ret = bch_write_page(nandi, offs, buf);
+	if (ret & NAND_STATUS_FAIL)
+		return -EIO;
+
+	return 0;
+}
+
+/*
+ * Hamming-FLEX operations
+ */
+static int flex_wait_rbn(struct nandi_controller *nandi)
+{
+	int ret;
+
+	ret = wait_for_completion_timeout(&nandi->rbn_completed, HZ/2);
+	if (!ret)
+		dev_err(nandi->dev, "FLEX RBn timeout\n");
+
+	return ret;
+}
+
+static void flex_cmd(struct nandi_controller *nandi, uint8_t cmd)
+{
+	uint32_t val;
+
+	val = (FLEX_CMD_CSN | FLEX_CMD_BEATS_1 | cmd);
+	writel(val, nandi->base + NANDHAM_FLEX_CMD);
+}
+
+static void flex_addr(struct nandi_controller *nandi,
+		      uint32_t addr, int cycles)
+{
+	addr &= 0x00ffffff;
+
+	BUG_ON(cycles < 1);
+	BUG_ON(cycles > 3);
+
+	addr |= (FLEX_ADDR_CSN | FLEX_ADDR_ADD8_VALID);
+	addr |= (cycles & 0x3) << 28;
+
+	writel(addr, nandi->base + NANDHAM_FLEX_ADD);
+}
+
+/*
+ * Partial implementation of MTD/NAND Interface, based on Hamming-FLEX
+ * operation.
+ *
+ * Allows us to make use of nand_base.c functions where possible
+ * (e.g. nand_scan_ident() and friends).
+ */
+static void flex_command_lp(struct mtd_info *mtd, unsigned int command,
+			    int column, int page)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct nandi_controller *nandi = chip->priv;
+
+	emiss_nandi_select(nandi, STM_NANDI_HAMMING);
+
+	switch (command) {
+	case NAND_CMD_READOOB:
+		/* Emulate NAND_CMD_READOOB */
+		column += mtd->writesize;
+		command = NAND_CMD_READ0;
+		break;
+	case NAND_CMD_READ0:
+	case NAND_CMD_ERASE1:
+	case NAND_CMD_SEQIN:
+	case NAND_CMD_RESET:
+	case NAND_CMD_PARAM:
+		/* Prime RBn wait */
+		nandi_enable_interrupts(nandi, NAND_INT_RBN);
+		reinit_completion(&nandi->rbn_completed);
+		break;
+	case NAND_CMD_READID:
+	case NAND_CMD_STATUS:
+	case NAND_CMD_ERASE2:
+		break;
+	default:
+		/* Catch unexpected commands */
+		BUG();
+	}
+
+	/*
+	 * Command Cycle
+	 */
+	flex_cmd(nandi, command);
+
+	/*
+	 * Address Cycles
+	 */
+	if (column != -1)
+		flex_addr(nandi, column,
+			  (command == NAND_CMD_READID) ? 1 : 2);
+
+	if (page != -1)
+		flex_addr(nandi, page, nandi->extra_addr ? 3 : 2);
+
+	/* Complete 'READ0' command */
+	if (command == NAND_CMD_READ0)
+		flex_cmd(nandi, NAND_CMD_READSTART);
+
+	/* Wait for RBn, if required                        */
+	/* (Note, other commands may handle wait elsewhere) */
+	if (command == NAND_CMD_RESET ||
+	    command == NAND_CMD_READ0 ||
+	    command == NAND_CMD_PARAM) {
+		flex_wait_rbn(nandi);
+		nandi_disable_interrupts(nandi, NAND_INT_RBN);
+	}
+}
+
+static uint8_t flex_read_byte(struct mtd_info *mtd)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct nandi_controller *nandi = chip->priv;
+
+	emiss_nandi_select(nandi, STM_NANDI_HAMMING);
+
+	return (uint8_t)(readl(nandi->base + NANDHAM_FLEX_DATA) & 0xff);
+}
+
+static int flex_wait_func(struct mtd_info *mtd, struct nand_chip *chip)
+{
+	struct nandi_controller *nandi = chip->priv;
+
+	emiss_nandi_select(nandi, STM_NANDI_HAMMING);
+
+	flex_wait_rbn(nandi);
+
+	flex_cmd(nandi, NAND_CMD_STATUS);
+
+	return (int)(readl(nandi->base + NANDHAM_FLEX_DATA) & 0xff);
+}
+
+/* Multi-CS devices not supported */
+static void flex_select_chip(struct mtd_info *mtd, int chipnr)
+{
+
+}
+
+static void flex_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct nandi_controller *nandi = chip->priv;
+	int aligned;
+
+	emiss_nandi_select(nandi, STM_NANDI_HAMMING);
+
+	/* Read bytes until buf is 4-byte aligned */
+	while (len && ((unsigned int)buf & 0x3)) {
+		*buf++ = (uint8_t)(readl(nandi->base + NANDHAM_FLEX_DATA)
+				   & 0xff);
+		len--;
+	};
+
+	/* Use 'BEATS_4'/readsl */
+	if (len > 8) {
+		aligned = len & ~0x3;
+		writel(FLEX_DATA_CFG_BEATS_4 | FLEX_DATA_CFG_CSN,
+		       nandi->base + NANDHAM_FLEX_DATAREAD_CONFIG);
+
+		readsl(nandi->base + NANDHAM_FLEX_DATA, buf, aligned >> 2);
+
+		buf += aligned;
+		len -= aligned;
+
+		writel(FLEX_DATA_CFG_BEATS_1 | FLEX_DATA_CFG_CSN,
+		       nandi->base + NANDHAM_FLEX_DATAREAD_CONFIG);
+	}
+
+	/* Mop up remaining bytes */
+	while (len > 0) {
+		*buf++ = (uint8_t)(readl(nandi->base + NANDHAM_FLEX_DATA)
+				   & 0xff);
+		len--;
+	}
+}
+
+static void flex_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct nandi_controller *nandi = chip->priv;
+	int aligned;
+
+	/* Write bytes until buf is 4-byte aligned */
+	while (len && ((unsigned int)buf & 0x3)) {
+		writel(*buf++, nandi->base + NANDHAM_FLEX_DATA);
+		len--;
+	};
+
+	/* USE 'BEATS_4/writesl */
+	if (len > 8) {
+		aligned = len & ~0x3;
+		writel(FLEX_DATA_CFG_BEATS_4 | FLEX_DATA_CFG_CSN,
+		       nandi->base + NANDHAM_FLEX_DATAWRITE_CONFIG);
+		writesl(nandi->base + NANDHAM_FLEX_DATA, buf, aligned >> 2);
+		buf += aligned;
+		len -= aligned;
+		writel(FLEX_DATA_CFG_BEATS_1 | FLEX_DATA_CFG_CSN,
+		       nandi->base + NANDHAM_FLEX_DATAWRITE_CONFIG);
+	}
+
+	/* Mop up remaining bytes */
+	while (len > 0) {
+		writel(*buf++, nandi->base + NANDHAM_FLEX_DATA);
+		len--;
+	}
+}
+
+int flex_read_raw(struct nandi_controller *nandi,
+			 uint32_t page_addr,
+			 uint32_t col_addr,
+			 uint8_t *buf, uint32_t len)
+{
+	dev_dbg(nandi->dev, "%s %u bytes at [0x%06x,0x%04x]\n",
+		__func__, len, page_addr, col_addr);
+
+	BUG_ON(len & 0x3);
+	BUG_ON((unsigned long)buf & 0x3);
+
+	emiss_nandi_select(nandi, STM_NANDI_HAMMING);
+	nandi_enable_interrupts(nandi, NAND_INT_RBN);
+	reinit_completion(&nandi->rbn_completed);
+
+	writel(FLEX_DATA_CFG_BEATS_4 | FLEX_DATA_CFG_CSN,
+	       nandi->base + NANDHAM_FLEX_DATAREAD_CONFIG);
+
+	flex_cmd(nandi, NAND_CMD_READ0);
+	flex_addr(nandi, col_addr, 2);
+	flex_addr(nandi, page_addr, nandi->extra_addr ? 3 : 2);
+	flex_cmd(nandi, NAND_CMD_READSTART);
+
+	flex_wait_rbn(nandi);
+
+	readsl(nandi->base + NANDHAM_FLEX_DATA, buf, len / 4);
+
+	nandi_disable_interrupts(nandi, NAND_INT_RBN);
+
+	writel(FLEX_DATA_CFG_BEATS_1 | FLEX_DATA_CFG_CSN,
+	       nandi->base + NANDHAM_FLEX_DATAREAD_CONFIG);
+
+	return 0;
+}
+EXPORT_SYMBOL(flex_read_raw);
+
+static int bch_mtd_read_oob(struct mtd_info *mtd,
+			    struct nand_chip *chip, int page)
+{
+	BUG();
+	return 0;
+}
+
+static int bch_mtd_write_oob(struct mtd_info *mtd,
+			     struct nand_chip *chip, int page)
+{
+	BUG();
+	return 0;
+}
+
+static int bch_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+			     uint8_t *buf, int oob_required, int page)
+{
+	BUG();
+	return 0;
+}
+
+static int bch_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+			      const uint8_t *buf, int oob_required)
+{
+	BUG();
+	return 0;
+}
+
+static void bch_hwctl(struct mtd_info *mtd, int mode)
+{
+	BUG();
+}
+
+static int bch_calculate(struct mtd_info *mtd, const uint8_t *dat,
+			 uint8_t *ecc_code)
+{
+	BUG();
+	return 0;
+}
+
+static int bch_correct(struct mtd_info *mtd, uint8_t *dat, uint8_t *read_ecc,
+		       uint8_t *calc_ecc)
+{
+	BUG();
+	return 0;
+}
+
+/*
+ * Initialisation
+ */
+static int bch_check_compatibility(struct nandi_controller *nandi,
+				   struct mtd_info *mtd,
+				   struct nand_chip *chip)
+{
+	if (chip->bits_per_cell > 1)
+		dev_warn(nandi->dev, "MLC NAND not fully supported\n");
+
+	if (chip->options & NAND_BUSWIDTH_16) {
+		dev_err(nandi->dev, "x16 NAND not supported\n");
+		return false;
+	}
+
+	if (nandi->blocks_per_device / 4 > mtd->writesize) {
+		/* Need to implement multi-page BBT support... */
+		dev_err(nandi->dev, "BBT too big to fit in single page\n");
+		return false;
+	}
+
+	if (bch_ecc_sizes[nandi->bch_ecc_mode] * nandi->sectors_per_page >
+	    mtd->oobsize) {
+		dev_err(nandi->dev, "insufficient OOB for selected ECC\n");
+		return false;
+	}
+
+	return true;
+}
+
+/* Select strongest ECC scheme compatible with OOB size */
+static int bch_set_ecc_auto(struct nandi_controller *nandi,
+			    struct mtd_info *mtd)
+{
+	int oob_bytes_per_sector = mtd->oobsize / nandi->sectors_per_page;
+	int try_ecc_modes[] = { BCH_30BIT_ECC, BCH_18BIT_ECC, -1 };
+	int m, ecc_mode;
+
+	for (m = 0; try_ecc_modes[m] >= 0; m++) {
+		ecc_mode = try_ecc_modes[m];
+		if (oob_bytes_per_sector >= bch_ecc_sizes[ecc_mode]) {
+			nandi->bch_ecc_mode = ecc_mode;
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
+
+static void nandi_set_mtd_defaults(struct nandi_controller *nandi,
+				   struct mtd_info *mtd, struct nand_chip *chip)
+{
+	struct nandi_info *info = &nandi->info;
+	int i;
+
+	/* ecclayout */
+	info->ecclayout.eccbytes = mtd->oobsize;
+	for (i = 0; i < 64; i++)
+		info->ecclayout.eccpos[i] = i;
+	info->ecclayout.oobfree[0].offset = 0;
+	info->ecclayout.oobfree[0].length = 0;
+	chip->ecc.mode = NAND_ECC_HW;
+
+	/* nand_chip */
+	chip->controller = &chip->hwcontrol;
+	spin_lock_init(&chip->controller->lock);
+	init_waitqueue_head(&chip->controller->wq);
+	chip->state = FL_READY;
+	chip->priv = nandi;
+	chip->ecc.layout = &info->ecclayout;
+	chip->options |= NAND_NO_SUBPAGE_WRITE;
+
+	chip->cmdfunc = flex_command_lp;
+	chip->read_byte = flex_read_byte;
+	chip->select_chip = flex_select_chip;
+	chip->waitfunc = flex_wait_func;
+	chip->read_buf = flex_read_buf;
+	chip->write_buf = flex_write_buf;
+
+	chip->bbt_options |= NAND_BBT_USE_FLASH;
+
+	/* mtd_info */
+	mtd->owner = THIS_MODULE;
+	mtd->type = MTD_NANDFLASH;
+	mtd->flags = MTD_CAP_NANDFLASH;
+	mtd->ecclayout = &info->ecclayout;
+
+	chip->ecc.hwctl = bch_hwctl;
+	chip->ecc.calculate = bch_calculate;
+	chip->ecc.correct = bch_correct;
+
+	chip->ecc.read_oob = bch_mtd_read_oob;
+	chip->ecc.write_oob = bch_mtd_write_oob;
+
+	chip->ecc.read_page = bch_read;
+	chip->ecc.read_page_raw = bch_read_page_raw;
+	chip->ecc.write_page_raw = bch_write_page_raw;
+	chip->write_page = bch_write;
+	chip->erase = bch_erase;
+
+#ifdef CONFIG_MTD_NAND_STM_BCH_BBT
+	chip->scan_bbt = bch_scan_bbt;
+	chip->block_bad = bch_block_isbad;
+	chip->block_markbad = bch_block_markbad;
+#endif
+}
+
+/*
+ * Timing and Clocks
+ */
+
+static void nandi_clk_enable(struct nandi_controller *nandi)
+{
+	clk_prepare_enable(nandi->emi_clk);
+	clk_prepare_enable(nandi->bch_clk);
+}
+
+static void nandi_clk_disable(struct nandi_controller *nandi)
+{
+	clk_disable_unprepare(nandi->emi_clk);
+	clk_disable_unprepare(nandi->bch_clk);
+}
+
+static struct clk *nandi_clk_setup(struct nandi_controller *nandi,
+				   char *clkname)
+{
+	struct clk *clk;
+	int ret;
+
+	clk = devm_clk_get(nandi->dev, clkname);
+	if (IS_ERR_OR_NULL(clk)) {
+		dev_warn(nandi->dev, "Failed to get %s clock\n", clkname);
+		return NULL;
+	}
+
+	ret = clk_prepare_enable(clk);
+	if (ret) {
+		dev_warn(nandi->dev, "Failed to enable %s clock\n", clkname);
+		return NULL;
+	}
+
+	return clk;
+}
+
+/* Derive Hamming-FLEX timing register values from 'nand_sdr_timings' data */
+static void flex_calc_timing_registers(const struct nand_sdr_timings *spec,
+				       int tCLK, int relax,
+				       uint32_t *ctl_timing,
+				       uint32_t *wen_timing,
+				       uint32_t *ren_timing)
+{
+	int tMAX_HOLD;
+	int n_ctl_setup;
+	int n_ctl_hold;
+	int n_ctl_wb;
+
+	int tMAX_WEN_OFF;
+	int n_wen_on;
+	int n_wen_off;
+
+	int tMAX_REN_OFF;
+	int n_ren_on;
+	int n_ren_off;
+
+	/*
+	 * CTL_TIMING
+	 */
+
+	/*	- SETUP */
+	n_ctl_setup = (PICO_TO_MILI(spec->tCLS_min - spec->tWP_min)
+		       + tCLK - 1)/tCLK;
+	if (n_ctl_setup < 1)
+		n_ctl_setup = 1;
+	n_ctl_setup += relax;
+
+	/*	- HOLD */
+	tMAX_HOLD = spec->tCLH_min;
+	if (spec->tCH_min > tMAX_HOLD)
+		tMAX_HOLD = spec->tCH_min;
+	if (spec->tALH_min > tMAX_HOLD)
+		tMAX_HOLD = spec->tALH_min;
+	if (spec->tDH_min > tMAX_HOLD)
+		tMAX_HOLD = spec->tDH_min;
+	n_ctl_hold = (PICO_TO_MILI(tMAX_HOLD) + tCLK - 1)/tCLK + relax;
+
+	/*	- CE_deassert_hold = 0 */
+
+	/*	- WE_high_to_RBn_low */
+	n_ctl_wb = (PICO_TO_MILI(spec->tWB_max) + tCLK - 1)/tCLK;
+
+	*ctl_timing = ((n_ctl_setup & 0xff) |
+		       (n_ctl_hold & 0xff) << 8 |
+		       (n_ctl_wb & 0xff) << 24);
+
+	/*
+	 * WEN_TIMING
+	 */
+
+	/*	- ON */
+	n_wen_on = (PICO_TO_MILI(spec->tWH_min) + tCLK - 1)/tCLK + relax;
+
+	/*	- OFF */
+	tMAX_WEN_OFF = spec->tWC_min - spec->tWH_min;
+	if (spec->tWP_min > tMAX_WEN_OFF)
+		tMAX_WEN_OFF = spec->tWP_min;
+	n_wen_off = (PICO_TO_MILI(tMAX_WEN_OFF) + tCLK - 1)/tCLK + relax;
+
+	*wen_timing = ((n_wen_on & 0xff) |
+		       (n_wen_off & 0xff) << 8);
+
+	/*
+	 * REN_TIMING
+	 */
+
+	/*	- ON */
+	n_ren_on = (PICO_TO_MILI(spec->tREH_min) + tCLK - 1)/tCLK + relax;
+
+	/*	- OFF */
+	tMAX_REN_OFF = spec->tRC_min - spec->tREH_min;
+	if (spec->tRP_min > tMAX_REN_OFF)
+		tMAX_REN_OFF = spec->tRP_min;
+	if (spec->tREA_max > tMAX_REN_OFF)
+		tMAX_REN_OFF = spec->tREA_max;
+	n_ren_off = (PICO_TO_MILI(tMAX_REN_OFF) + tCLK - 1)/tCLK + 1 + relax;
+
+	*ren_timing = ((n_ren_on & 0xff) |
+		       (n_ren_off & 0xff) << 8);
+}
+
+/* Derive BCH timing register values from 'nand_sdr_timings' data */
+static void bch_calc_timing_registers(const struct nand_sdr_timings *spec,
+				      int tCLK, int relax,
+				      uint32_t *ctl_timing,
+				      uint32_t *wen_timing,
+				      uint32_t *ren_timing)
+{
+	int tMAX_HOLD;
+	int n_ctl_setup;
+	int n_ctl_hold;
+	int n_ctl_wb;
+
+	int n_wen_on;
+	int n_wen_off;
+	int wen_half_on;
+	int wen_half_off;
+
+	int tMAX_REN_ON;
+	int tMAX_CS_DEASSERT;
+	int n_d_latch;
+	int n_telqv;
+	int n_ren_on;
+	int n_ren_off;
+	int ren_half_on;
+	int ren_half_off;
+
+	/*
+	 * CTL_TIMING
+	 */
+
+	/*	- SETUP */
+	if (spec->tCLS_min > spec->tWP_min)
+		n_ctl_setup = (PICO_TO_MILI(spec->tCLS_min - spec->tWP_min)
+			       + tCLK - 1)/tCLK;
+	else
+		n_ctl_setup = 0;
+	n_ctl_setup += relax;
+
+	/*	- HOLD */
+	tMAX_HOLD = spec->tCLH_min;
+	if (spec->tCH_min > tMAX_HOLD)
+		tMAX_HOLD = spec->tCH_min;
+	if (spec->tALH_min > tMAX_HOLD)
+		tMAX_HOLD = spec->tALH_min;
+	if (spec->tDH_min > tMAX_HOLD)
+		tMAX_HOLD = spec->tDH_min;
+	n_ctl_hold = (PICO_TO_MILI(tMAX_HOLD) + tCLK - 1)/tCLK + relax;
+	/*	- CE_deassert_hold = 0 */
+
+	/*	- WE_high_to_RBn_low */
+	n_ctl_wb = (PICO_TO_MILI(spec->tWB_max) + tCLK - 1)/tCLK;
+
+	*ctl_timing = ((n_ctl_setup & 0xff) |
+		       (n_ctl_hold & 0xff) << 8 |
+		       (n_ctl_wb & 0xff) << 24);
+
+	/*
+	 * WEN_TIMING
+	 */
+
+	/*	- ON */
+	n_wen_on = (2 * PICO_TO_MILI(spec->tWH_min) + tCLK - 1)/tCLK;
+	wen_half_on = n_wen_on % 2;
+	n_wen_on /= 2;
+	n_wen_on += relax;
+
+	/*	- OFF */
+	n_wen_off = (2 * PICO_TO_MILI(spec->tWP_min) + tCLK - 1)/tCLK;
+	wen_half_off = n_wen_off % 2;
+	n_wen_off /= 2;
+	n_wen_off += relax;
+
+	*wen_timing = ((n_wen_on & 0xff) |
+		       (n_wen_off & 0xff) << 8 |
+		       (wen_half_on << 16) |
+		       (wen_half_off << 17));
+
+	/*
+	 * REN_TIMING
+	 */
+
+	/*	- ON */
+	tMAX_REN_ON = spec->tRC_min - spec->tRP_min;
+	if (spec->tREH_min > tMAX_REN_ON)
+		tMAX_REN_ON = spec->tREH_min;
+
+	n_ren_on = (2 * PICO_TO_MILI(tMAX_REN_ON) + tCLK - 1)/tCLK;
+	ren_half_on = n_ren_on % 2;
+	n_ren_on /= 2;
+	n_ren_on += relax;
+
+	/*	- OFF */
+	n_ren_off = (2 * PICO_TO_MILI(spec->tREA_max) + tCLK - 1)/tCLK;
+	ren_half_off = n_ren_off % 2;
+	n_ren_off /= 2;
+	n_ren_off += relax;
+
+	/*	- DATA_LATCH */
+	if (PICO_TO_MILI(spec->tREA_max) <=
+	    (PICO_TO_MILI(spec->tRP_min) - (2 * tCLK)))
+		n_d_latch = 0;
+	else if (PICO_TO_MILI(spec->tREA_max) <=
+		 (PICO_TO_MILI(spec->tRP_min) - tCLK))
+		n_d_latch = 1;
+	else if ((spec->tREA_max <= spec->tRP_min) &&
+		 (PICO_TO_MILI(spec->tRHOH_min) >= 2 * tCLK))
+		n_d_latch = 2;
+	else
+		n_d_latch = 3;
+
+	/*	- TELQV */
+	tMAX_CS_DEASSERT = spec->tCOH_min;
+	if (spec->tCHZ_max > tMAX_CS_DEASSERT)
+		tMAX_CS_DEASSERT = spec->tCHZ_max;
+
+	n_telqv = (PICO_TO_MILI(tMAX_CS_DEASSERT) + tCLK - 1)/tCLK;
+
+	*ren_timing = ((n_ren_on & 0xff) |
+		       (n_ren_off & 0xff) << 8 |
+		       (n_d_latch & 0x3) << 16 |
+		       (wen_half_on << 18) |
+		       (wen_half_off << 19) |
+		       (n_telqv & 0xff) << 24);
+}
+
+static void flex_configure_timing_registers(struct nandi_controller *nandi,
+					    const struct nand_sdr_timings *spec,
+					    int relax)
+{
+	uint32_t ctl_timing;
+	uint32_t wen_timing;
+	uint32_t ren_timing;
+	int emi_t_ns;
+
+	/* Select Hamming Controller */
+	emiss_nandi_select(nandi, STM_NANDI_HAMMING);
+
+	/* Get EMI clock (default 100MHz) */
+	if (nandi->emi_clk)
+		emi_t_ns = 1000000000UL / clk_get_rate(nandi->emi_clk);
+	else {
+		dev_warn(nandi->dev,
+			 "No EMI clock available; assuming default 100MHz\n");
+		emi_t_ns = 10;
+	}
+
+	/* Derive timing register values from specification */
+	flex_calc_timing_registers(spec, emi_t_ns, relax,
+				   &ctl_timing, &wen_timing, &ren_timing);
+
+	dev_dbg(nandi->dev,
+		"updating FLEX timing configuration [0x%08x, 0x%08x, 0x%08x]\n",
+		ctl_timing, wen_timing, ren_timing);
+
+	/* Program timing registers */
+	writel(ctl_timing, nandi->base + NANDHAM_CTL_TIMING);
+	writel(wen_timing, nandi->base + NANDHAM_WEN_TIMING);
+	writel(ren_timing, nandi->base + NANDHAM_REN_TIMING);
+}
+
+static void bch_configure_timing_registers(struct nandi_controller *nandi,
+					   const struct nand_sdr_timings *spec,
+					   int relax)
+{
+	uint32_t ctl_timing;
+	uint32_t wen_timing;
+	uint32_t ren_timing;
+	int bch_t_ns;
+
+	/* Select BCH Controller */
+	emiss_nandi_select(nandi, STM_NANDI_BCH);
+
+	/* Get BCH clock (default 200MHz) */
+	if (nandi->bch_clk)
+		bch_t_ns = 1000000000UL / clk_get_rate(nandi->bch_clk);
+	else {
+		dev_warn(nandi->dev,
+			 "No BCH clock available; assuming default 200MHz\n");
+		bch_t_ns = 5;
+	}
+
+	/* Derive timing register values from specification */
+	bch_calc_timing_registers(spec, bch_t_ns, relax,
+				  &ctl_timing, &wen_timing, &ren_timing);
+
+	dev_dbg(nandi->dev,
+		"updating BCH timing configuration [0x%08x, 0x%08x, 0x%08x]\n",
+		ctl_timing, wen_timing, ren_timing);
+
+	/* Program timing registers */
+	writel(ctl_timing, nandi->base + NANDBCH_CTL_TIMING);
+	writel(wen_timing, nandi->base + NANDBCH_WEN_TIMING);
+	writel(ren_timing, nandi->base + NANDBCH_REN_TIMING);
+}
+
+static void nandi_configure_timing_registers(struct nandi_controller *nandi,
+					const struct nand_sdr_timings *spec,
+					int relax)
+{
+	bch_configure_timing_registers(nandi, spec, relax);
+	flex_configure_timing_registers(nandi, spec, relax);
+}
+
+static void nandi_init_hamming(struct nandi_controller *nandi, int emi_bank)
+{
+	dev_dbg(nandi->dev, "%s\n", __func__);
+
+	emiss_nandi_select(nandi, STM_NANDI_HAMMING);
+
+	/* Reset and disable boot-mode controller */
+	writel(BOOT_CFG_RESET, nandi->base + NANDHAM_BOOTBANK_CFG);
+	udelay(1);
+	writel(0x00000000, nandi->base + NANDHAM_BOOTBANK_CFG);
+
+	/* Reset controller */
+	writel(CFG_RESET, nandi->base + NANDHAM_FLEXMODE_CFG);
+	udelay(1);
+	writel(0x00000000, nandi->base + NANDHAM_FLEXMODE_CFG);
+
+	/* Set EMI Bank */
+	writel(0x1 << emi_bank, nandi->base + NANDHAM_FLEX_MUXCTRL);
+
+	/* Enable FLEX mode */
+	writel(CFG_ENABLE_FLEX, nandi->base + NANDHAM_FLEXMODE_CFG);
+
+	/* Configure FLEX_DATA_READ/WRITE for 1-byte access */
+	writel(FLEX_DATA_CFG_BEATS_1 | FLEX_DATA_CFG_CSN,
+	       nandi->base + NANDHAM_FLEX_DATAREAD_CONFIG);
+	writel(FLEX_DATA_CFG_BEATS_1 | FLEX_DATA_CFG_CSN,
+	       nandi->base + NANDHAM_FLEX_DATAREAD_CONFIG);
+
+	/* RBn interrupt on rising edge */
+	writel(NAND_EDGE_CFG_RBN_RISING, nandi->base + NANDHAM_INT_EDGE_CFG);
+
+	/* Enable interrupts */
+	nandi_enable_interrupts(nandi, NAND_INT_ENABLE);
+}
+
+static void nandi_init_bch(struct nandi_controller *nandi, int emi_bank)
+{
+	dev_dbg(nandi->dev, "%s\n", __func__);
+
+	/* Initialise BCH Controller */
+	emiss_nandi_select(nandi, STM_NANDI_BCH);
+
+	/* Reset and disable boot-mode controller */
+	writel(BOOT_CFG_RESET, nandi->base + NANDBCH_BOOTBANK_CFG);
+	udelay(1);
+	writel(0x00000000, nandi->base + NANDBCH_BOOTBANK_CFG);
+
+	/* Reset AFM controller */
+	writel(CFG_RESET, nandi->base + NANDBCH_CONTROLLER_CFG);
+	udelay(1);
+	writel(0x00000000, nandi->base + NANDBCH_CONTROLLER_CFG);
+
+	/* Set EMI Bank */
+	writel(0x1 << emi_bank, nandi->base + NANDBCH_FLEX_MUXCTRL);
+
+	/* Reset ECC stats */
+	writel(CFG_RESET_ECC_ALL, nandi->base + NANDBCH_CONTROLLER_CFG);
+	udelay(1);
+
+	/* Enable AFM */
+	writel(CFG_ENABLE_AFM, nandi->base + NANDBCH_CONTROLLER_CFG);
+
+	/* Configure Read DMA Plugs (values supplied by Validation) */
+	writel(0x00000005, nandi->dma + EMISS_NAND_RD_DMA_PAGE_SIZE);
+	writel(0x00000005, nandi->dma + EMISS_NAND_RD_DMA_MAX_OPCODE_SIZE);
+	writel(0x00000002, nandi->dma + EMISS_NAND_RD_DMA_MIN_OPCODE_SIZE);
+	writel(0x00000001, nandi->dma + EMISS_NAND_RD_DMA_MAX_CHUNK_SIZE);
+	writel(0x00000000, nandi->dma + EMISS_NAND_RD_DMA_MAX_MESSAGE_SIZE);
+
+	/* Configure Write DMA Plugs (values supplied by Validation) */
+	writel(0x00000005, nandi->dma + EMISS_NAND_WR_DMA_PAGE_SIZE);
+	writel(0x00000005, nandi->dma + EMISS_NAND_WR_DMA_MAX_OPCODE_SIZE);
+	writel(0x00000002, nandi->dma + EMISS_NAND_WR_DMA_MIN_OPCODE_SIZE);
+	writel(0x00000001, nandi->dma + EMISS_NAND_WR_DMA_MAX_CHUNK_SIZE);
+	writel(0x00000000, nandi->dma + EMISS_NAND_WR_DMA_MAX_MESSAGE_SIZE);
+
+	nandi_enable_interrupts(nandi, NAND_INT_ENABLE);
+}
+
+static void nandi_init_controller(struct nandi_controller *nandi,
+				  int emi_bank)
+{
+	nandi_init_bch(nandi, emi_bank);
+	nandi_init_hamming(nandi, emi_bank);
+}
+
+/* Initialise working buffers, accomodating DMA alignment constraints */
+static int nandi_init_working_buffers(struct nandi_controller *nandi,
+				      struct nandi_bbt_info *bbt_info,
+				      struct mtd_info *mtd)
+{
+	uint32_t bbt_buf_size;
+	uint32_t buf_size;
+
+	/*	- Page and OOB */
+	buf_size = mtd->writesize + mtd->oobsize + NANDI_BCH_DMA_ALIGNMENT;
+
+	/*	- BBT data (page-size aligned) */
+	bbt_info->bbt_size = nandi->blocks_per_device >> 2; /* 2 bits/block */
+	bbt_buf_size = ALIGN(bbt_info->bbt_size, mtd->writesize);
+	buf_size += bbt_buf_size + NANDI_BCH_DMA_ALIGNMENT;
+
+	/*	- BCH BUF list */
+	buf_size += NANDI_BCH_BUF_LIST_SIZE + NANDI_BCH_DMA_ALIGNMENT;
+
+	/* Allocate bufffer */
+	nandi->buf = devm_kzalloc(nandi->dev, buf_size, GFP_KERNEL);
+	if (!nandi->buf) {
+		dev_err(nandi->dev, "failed to allocate working buffers\n");
+		return -ENOMEM;
+	}
+
+	/* Set/Align buffer pointers */
+	nandi->page_buf = PTR_ALIGN(nandi->buf, NANDI_BCH_DMA_ALIGNMENT);
+	nandi->oob_buf  = nandi->page_buf + mtd->writesize;
+	bbt_info->bbt   = PTR_ALIGN(nandi->oob_buf + mtd->oobsize,
+				    NANDI_BCH_DMA_ALIGNMENT);
+	nandi->buf_list = (uint32_t *)PTR_ALIGN(bbt_info->bbt + bbt_buf_size,
+						NANDI_BCH_DMA_ALIGNMENT);
+	nandi->cached_page = -1;
+
+	return 0;
+}
+
+static int remap_named_resource(struct platform_device *pdev,
+				char *name,
+				void __iomem **io_ptr)
+{
+	struct resource *res, *mem;
+	resource_size_t size;
+	void __iomem *p;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
+	if (!res)
+		return -ENXIO;
+
+	size = resource_size(res);
+
+	mem = devm_request_mem_region(&pdev->dev, res->start, size, name);
+	if (!mem)
+		return -EBUSY;
+
+	p = devm_ioremap_nocache(&pdev->dev, res->start, size);
+	if (!p)
+		return -ENOMEM;
+
+	*io_ptr = p;
+
+	return 0;
+}
+
+static struct nandi_controller *
+nandi_init_resources(struct platform_device *pdev)
+{
+	struct stm_nand_bch_ddata *ddata = platform_get_drvdata(pdev);
+	struct nandi_controller *nandi;
+	int irq;
+	int err;
+
+	nandi = devm_kzalloc(&pdev->dev, sizeof(*nandi), GFP_KERNEL);
+	if (!nandi) {
+		dev_err(&pdev->dev,
+			"failed to allocate NANDi controller data\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	nandi->dev = &pdev->dev;
+
+	err = remap_named_resource(pdev, "emi_nand", &nandi->base);
+	if (err)
+		return ERR_PTR(err);
+
+	err = remap_named_resource(pdev, "emiss", &nandi->emiss);
+	if (err)
+		return ERR_PTR(err);
+
+	nandi->dma = nandi->emiss + EMISS_NAND_DMA;
+	nandi->emisscfg = nandi->emiss + EMISS_CFG;
+
+	irq = platform_get_irq_byname(pdev, "nand_irq");
+	if (irq < 0) {
+		dev_err(&pdev->dev, "failed to find IRQ resource\n");
+		return ERR_PTR(irq);
+	}
+
+	err = devm_request_irq(&pdev->dev, irq, nandi_irq_handler,
+			       IRQF_DISABLED, dev_name(&pdev->dev), nandi);
+	if (err) {
+		dev_err(&pdev->dev, "irq request failed\n");
+		return ERR_PTR(err);
+	}
+
+	nandi->emi_clk = nandi_clk_setup(nandi, "emi");
+	nandi->bch_clk = nandi_clk_setup(nandi, "bch");
+
+	ddata->nandi = nandi;
+
+	return nandi;
+}
+
+static void *stm_bch_dt_get_ddata(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct stm_nand_bch_ddata *ddata;
+	int ecc_strength;
+	int ret;
+
+	ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
+	if (!ddata)
+		return ERR_PTR(-ENOMEM);
+
+	of_property_read_u32(np, "nand-ecc-strength", &ecc_strength);
+	if (ecc_strength == 0)
+		ddata->bch_ecc_cfg = BCH_NO_ECC;
+	else if (ecc_strength == 18)
+		ddata->bch_ecc_cfg = BCH_18BIT_ECC;
+	else if (ecc_strength == 30)
+		ddata->bch_ecc_cfg = BCH_30BIT_ECC;
+	else
+		ddata->bch_ecc_cfg = BCH_ECC_AUTO;
+
+	ret = stm_of_get_nand_banks(&pdev->dev, np, &ddata->bank);
+	if (ret < 0)
+		return ERR_PTR(ret);
+
+	return ddata;
+}
+
+static int stm_nand_bch_probe(struct platform_device *pdev)
+{
+	const char *part_probes[] = { "cmdlinepart", "ofpart", NULL, };
+	struct device_node *np = pdev->dev.of_node;
+	struct mtd_part_parser_data ppdata;
+	struct stm_nand_bch_ddata *ddata;
+	struct stm_nand_bank_data *bank;
+	struct nandi_bbt_info *bbt_info;
+	struct nandi_controller *nandi;
+	struct nandi_info *info;
+	struct nand_chip *chip;
+	struct mtd_info *mtd;
+	int compatible, err;
+
+	if (!np) {
+		dev_err(&pdev->dev, "DT node found\n");
+		return -EINVAL;
+	}
+
+	ddata = stm_bch_dt_get_ddata(pdev);
+	if (IS_ERR(ddata))
+		return PTR_ERR(ddata);
+
+	ppdata.of_node = stm_of_get_partitions_node(np, 0);
+
+	platform_set_drvdata(pdev, ddata);
+
+	nandi = nandi_init_resources(pdev);
+	if (IS_ERR(nandi)) {
+		dev_err(&pdev->dev, "failed to initialise NANDi resources\n");
+		return PTR_ERR(nandi);
+	}
+
+	init_completion(&nandi->seq_completed);
+	init_completion(&nandi->rbn_completed);
+
+	bank = ddata->bank;
+	if (bank)
+		nandi_init_controller(nandi, bank->csn);
+
+	info            = &nandi->info;
+	chip            = &info->chip;
+	bbt_info        = &info->bbt_info;
+	mtd             = &info->mtd;
+	mtd->priv       = chip;
+	mtd->name       = dev_name(&pdev->dev);
+	mtd->dev.parent = &pdev->dev;
+
+	nandi_set_mtd_defaults(nandi, mtd, chip);
+
+	err = nand_scan_ident(mtd, 1, NULL);
+	if (err)
+		return err;
+
+	/*
+	 * Configure timing registers
+	 */
+	if (bank && bank->timing_spec) {
+		dev_info(&pdev->dev, "Using platform timing data\n");
+		nandi_configure_timing_registers(nandi, bank->timing_spec,
+						 bank->timing_relax);
+	} else if (chip->onfi_version) {
+		int mode = fls(onfi_get_async_timing_mode(chip) - 1);
+		const struct nand_sdr_timings *spec =
+			onfi_async_timing_mode_to_sdr_timings(mode);
+
+		/* Modes 4 and 5 (EDO) are not supported on our H/W */
+		if (mode > 3)
+			mode = 3;
+
+		dev_info(&pdev->dev, "Using ONFI Timing Mode %d\n", mode);
+		nandi_configure_timing_registers(nandi,	spec,
+					bank ? bank->timing_relax : 0);
+	} else {
+		dev_warn(&pdev->dev, "No timing data available\n");
+	}
+
+	if (mtd->writesize < NANDI_BCH_SECTOR_SIZE) {
+		dev_err(nandi->dev,
+			"page size incompatible with BCH ECC sector\n");
+		return -EINVAL;
+	}
+
+	/* Derive some working variables */
+	nandi->sectors_per_page = mtd->writesize / NANDI_BCH_SECTOR_SIZE;
+	nandi->blocks_per_device = mtd->size >> chip->phys_erase_shift;
+	nandi->extra_addr = ((chip->chipsize >> chip->page_shift) >
+			     0x10000) ? true : false;
+	mtd->writebufsize = mtd->writesize;
+
+	/* Set ECC mode */
+	if (ddata->bch_ecc_cfg == BCH_ECC_AUTO) {
+		err = bch_set_ecc_auto(nandi, mtd);
+		if (err) {
+			dev_err(nandi->dev, "insufficient OOB for BCH ECC\n");
+			return err;
+		}
+	} else {
+		nandi->bch_ecc_mode = ddata->bch_ecc_cfg;
+	}
+
+	chip->ecc.size = NANDI_BCH_SECTOR_SIZE;
+	chip->ecc.bytes = mtd->oobsize;
+	chip->ecc.strength = bch_ecc_strength[nandi->bch_ecc_mode];
+
+	info->ecclayout.eccbytes =
+		nandi->sectors_per_page * bch_ecc_sizes[nandi->bch_ecc_mode];
+
+	compatible = bch_check_compatibility(nandi, mtd, chip);
+	if (!compatible) {
+		dev_err(nandi->dev,
+			"NAND device incompatible with NANDi/BCH Controller\n");
+		return -EINVAL;
+	}
+
+	/* Tune BCH programs according to device found and ECC mode */
+	bch_configure_progs(nandi);
+
+	err = nandi_init_working_buffers(nandi, bbt_info, mtd);
+	if (err)
+		return err;
+
+	err = nand_scan_tail(mtd);
+	if (err)
+		return err;
+
+#ifdef CONFIG_MTD_NAND_STM_BCH_BBT
+	nandi_dump_bad_blocks(nandi);
+#endif
+	/* Add partitions */
+	return mtd_device_parse_register(mtd, part_probes, &ppdata,
+					bank->partitions, bank->nr_partitions);
+}
+
+static int stm_nand_bch_remove(struct platform_device *pdev)
+{
+	struct stm_nand_bch_ddata *ddata = platform_get_drvdata(pdev);
+	struct nandi_controller *nandi = ddata->nandi;
+
+	nand_release(&nandi->info.mtd);
+
+	nandi_clk_disable(nandi);
+
+	return 0;
+}
+
+static int stm_nand_bch_suspend(struct device *dev)
+{
+	struct stm_nand_bch_ddata *ddata = dev_get_drvdata(dev);
+	struct nandi_controller *nandi = ddata->nandi;
+
+	nandi_clk_disable(nandi);
+
+	return 0;
+}
+static int stm_nand_bch_resume(struct device *dev)
+{
+	struct stm_nand_bch_ddata *ddata = dev_get_drvdata(dev);
+	struct nandi_controller *nandi = ddata->nandi;
+
+	nandi_clk_enable(nandi);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(stm_nand_bch_pm_ops,
+			 stm_nand_bch_suspend,
+			 stm_nand_bch_resume);
+
+static struct of_device_id nand_bch_match[] = {
+	{ .compatible = "st,nand-bch", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, nand_bch_match);
+
+static struct platform_driver stm_nand_bch_driver = {
+	.probe	= stm_nand_bch_probe ,
+	.remove	= stm_nand_bch_remove,
+	.driver	= {
+		.name	= "stm-nand-bch",
+		.owner	= THIS_MODULE,
+		.of_match_table = of_match_ptr(nand_bch_match),
+		.pm	= &stm_nand_bch_pm_ops,
+	},
+};
+module_platform_driver(stm_nand_bch_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Angus Clark");
+MODULE_DESCRIPTION("STM NAND BCH driver");
diff --git a/drivers/mtd/nand/stm_nand_dt.c b/drivers/mtd/nand/stm_nand_dt.c
new file mode 100644
index 0000000..4f59873
--- /dev/null
+++ b/drivers/mtd/nand/stm_nand_dt.c
@@ -0,0 +1,109 @@
+/*
+ * drivers/mtd/nand/stm_nand_dt.c
+ *
+ * Support for NANDi BCH Controller Device Tree component
+ *
+ * Copyright (c) 2014 STMicroelectronics Limited
+ * Author: Author: Srinivas Kandagatla <srinivas.kandagatla@st.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/byteorder/generic.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/stm_nand.h>
+
+#include "stm_nand_regs.h"
+
+/**
+* stm_of_get_partitions_node - get partitions node from stm-nand type devices.
+*
+* @dev		device pointer to use for devm allocations.
+* @np		device node of the driver.
+* @bank_nr	which bank number to use to get partitions.
+*
+* Returns a node pointer if found, with refcount incremented, use
+* of_node_put() on it when done.
+*
+*/
+struct device_node *stm_of_get_partitions_node(struct device_node *np,
+					       int bank_nr)
+{
+	struct device_node *banknp, *partsnp = NULL;
+	char name[10];
+
+	sprintf(name, "bank%d", bank_nr);
+	banknp = of_get_child_by_name(np, name);
+	if (banknp)
+		return NULL;
+
+	partsnp = of_get_child_by_name(banknp, "partitions");
+	of_node_put(banknp);
+
+	return partsnp;
+}
+EXPORT_SYMBOL(stm_of_get_partitions_node);
+
+/**
+ * stm_of_get_nand_banks - Get nand banks info from a given device node.
+ *
+ * @dev			device pointer to use for devm allocations.
+ * @np			device node of the driver.
+ * @banksptr		double pointer to banks which is allocated
+ *			and filled with bank data.
+ *
+ * Returns a count of banks found in the given device node.
+ *
+ */
+int stm_of_get_nand_banks(struct device *dev, struct device_node *np,
+			  struct stm_nand_bank_data **banksptr)
+{
+	struct stm_nand_bank_data *banks;
+	struct device_node *banknp;
+	int nr_banks = 0;
+
+	if (!np)
+		return -ENODEV;
+
+	nr_banks = of_get_child_count(np);
+	if (!nr_banks) {
+		dev_err(dev, "No NAND banks specified in DT: %s\n",
+		np->full_name);
+		return -EINVAL;
+	}
+
+	*banksptr = devm_kzalloc(dev, sizeof(*banks) * nr_banks, GFP_KERNEL);
+	banks = *banksptr;
+	banknp = NULL;
+
+	for_each_child_of_node(np, banknp) {
+		int bank = 0;
+
+		of_property_read_u32(banknp, "st,nand-csn", &banks[bank].csn);
+
+		if (of_get_nand_bus_width(banknp) == 16)
+			banks[bank].options |= NAND_BUSWIDTH_16;
+		if (of_get_nand_on_flash_bbt(banknp))
+			banks[bank].bbt_options |= NAND_BBT_USE_FLASH;
+
+		banks[bank].nr_partitions = 0;
+		banks[bank].partitions = NULL;
+
+		of_property_read_u32(banknp, "st,nand-timing-relax",
+				     &banks[bank].timing_relax);
+		bank++;
+	}
+
+	return nr_banks;
+}
+EXPORT_SYMBOL(stm_of_get_nand_banks);
diff --git a/drivers/mtd/nand/stm_nand_dt.h b/drivers/mtd/nand/stm_nand_dt.h
new file mode 100644
index 0000000..53e1321
--- /dev/null
+++ b/drivers/mtd/nand/stm_nand_dt.h
@@ -0,0 +1,24 @@
+/*
+ * drivers/mtd/nand/stm_nand_dt.h
+ *
+ * Support for NANDi BCH Controller Device Tree component
+ *
+ * Copyright (c) 2014 STMicroelectronics Limited
+ * Author: Author: Srinivas Kandagatla <srinivas.kandagatla@st.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef STM_NAND_DT_H
+#define STM_NAND_DT_H
+
+struct device_node *stm_of_get_partitions_node(struct device_node *np,
+					       int bank_nr);
+
+int stm_of_get_nand_banks(struct device *dev, struct device_node *np,
+			  struct stm_nand_bank_data **banksp);
+
+#endif /* STM_NAND_DT_H */
diff --git a/include/linux/mtd/stm_nand.h b/include/linux/mtd/stm_nand.h
new file mode 100644
index 0000000..c3c805f
--- /dev/null
+++ b/include/linux/mtd/stm_nand.h
@@ -0,0 +1,147 @@
+/*
+ * include/linux/mtd/stm_mtd.h
+ *
+ * Support for STMicroelectronics NAND Controllers
+ *
+ * Copyright (c) 2014 STMicroelectronics Limited
+ * Author: Angus Clark <Angus.Clark@st.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef __LINUX_STM_NAND_H
+#define __LINUX_STM_NAND_H
+
+#include <linux/io.h>
+#include <linux/mtd/nand.h>
+
+/* ECC Modes */
+enum stm_nand_bch_ecc_config {
+	BCH_18BIT_ECC = 0,
+	BCH_30BIT_ECC,
+	BCH_NO_ECC,
+	BCH_ECC_RSRV,
+	BCH_ECC_AUTO,
+};
+
+/* Bad Block Table (BBT) */
+struct nandi_bbt_info {
+	uint32_t	bbt_size;		/* Size of bad-block table */
+	uint32_t	bbt_vers[2];		/* Version (Primary/Mirror) */
+	uint32_t	bbt_block[2];		/* Block No. (Primary/Mirror) */
+	uint8_t		*bbt;			/* Table data */
+};
+
+/* Collection of MTD/NAND device information */
+struct nandi_info {
+	struct mtd_info		mtd;		/* MTD info */
+	struct nand_chip	chip;		/* NAND chip info */
+
+	struct nand_ecclayout	ecclayout;	/* MTD ECC layout */
+	struct nandi_bbt_info	bbt_info;	/* Bad Block Table */
+	int			nr_parts;	/* Number of MTD partitions */
+	struct	mtd_partition	*parts;		/* MTD partitions */
+};
+
+/* NANDi Controller (Hamming/BCH) */
+struct nandi_controller {
+	void __iomem		*base;		/* Controller base*/
+	void __iomem		*emiss;		/* EMISS control base */
+	void __iomem		*emisscfg;	/* EMISS config base */
+	void __iomem		*dma;		/* DMA control base */
+
+	struct clk		*bch_clk;
+	struct clk		*emi_clk;
+						/* IRQ-triggered Completions: */
+	struct completion	seq_completed;	/*   SEQ Over */
+	struct completion	rbn_completed;	/*   RBn */
+
+	struct device		*dev;
+
+	int			bch_ecc_mode;	/* ECC mode */
+	bool			extra_addr;	/* Extra address cycle */
+
+	uint32_t		blocks_per_device;
+	uint32_t		sectors_per_page;
+
+	uint8_t			*buf;		/* Some buffers to use */
+	uint8_t			*page_buf;
+	uint8_t			*oob_buf;
+	uint32_t		*buf_list;
+
+	int			cached_page;	/* page number of page in */
+						/* 'page_buf'             */
+
+	struct nandi_info	info;		/* NAND device info */
+};
+
+/*
+ * Board-level specification relating to a 'bank' of NAND Flash
+ */
+struct stm_nand_bank_data {
+	int			csn;
+	int			nr_partitions;
+	struct mtd_partition	*partitions;
+	unsigned int		options;
+	unsigned int		bbt_options;
+
+	struct nand_sdr_timings *timing_spec;
+
+	/*
+	 * No. of IP clk cycles by which to 'relax' the timing configuration.
+	 * Required on some boards to to accommodate board-level limitations.
+	 * Used in conjunction with 'nand_sdr_timings' and ONFI configuration.
+	 */
+	int			timing_relax;
+};
+
+struct stm_nand_bch_ddata {
+	struct nandi_controller *nandi;
+	struct stm_nand_bank_data *bank;
+	enum stm_nand_bch_ecc_config bch_ecc_cfg;
+};
+
+enum nandi_controllers {
+	STM_NANDI_UNCONFIGURED,
+	STM_NANDI_HAMMING,
+	STM_NANDI_BCH
+};
+
+extern int flex_read_raw(struct nandi_controller *nandi,
+			 uint32_t page_addr,
+			 uint32_t col_addr,
+			 uint8_t *buf, uint32_t len);
+extern uint8_t bch_write_page(struct nandi_controller *nandi,
+			      loff_t offs, const uint8_t *buf);
+extern uint8_t bch_erase_block(struct nandi_controller *nandi,
+			       loff_t offs);
+extern int bch_read_page(struct nandi_controller *nandi,
+			 loff_t offs, uint8_t *buf);
+
+#define EMISS_NAND_CONFIG_HAMMING_NOT_BCH 	(0x1 << 6)
+
+static inline void emiss_nandi_select(struct nandi_controller *nandi,
+				      enum nandi_controllers controller)
+{
+	unsigned v;
+
+	v = readl(nandi->emisscfg);
+
+	if (controller == STM_NANDI_HAMMING) {
+		if (v & EMISS_NAND_CONFIG_HAMMING_NOT_BCH)
+			return;
+		v |= EMISS_NAND_CONFIG_HAMMING_NOT_BCH;
+	} else {
+		if (!(v & EMISS_NAND_CONFIG_HAMMING_NOT_BCH))
+			return;
+		v &= ~EMISS_NAND_CONFIG_HAMMING_NOT_BCH;
+	}
+
+	writel(v, nandi->emisscfg);
+	readl(nandi->emisscfg);
+}
+
+#endif /* __LINUX_STM_NAND_H */
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH 7/8] mtd: nand: stm_nand_bch: add support for ST's BCH NAND controller
@ 2014-08-13  9:12   ` Lee Jones
  0 siblings, 0 replies; 51+ messages in thread
From: Lee Jones @ 2014-08-13  9:12 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
 drivers/mtd/nand/Kconfig        |    7 +
 drivers/mtd/nand/Makefile       |    1 +
 drivers/mtd/nand/stm_nand_bch.c | 1618 +++++++++++++++++++++++++++++++++++++++
 drivers/mtd/nand/stm_nand_dt.c  |  109 +++
 drivers/mtd/nand/stm_nand_dt.h  |   24 +
 include/linux/mtd/stm_nand.h    |  147 ++++
 6 files changed, 1906 insertions(+)
 create mode 100644 drivers/mtd/nand/stm_nand_bch.c
 create mode 100644 drivers/mtd/nand/stm_nand_dt.c
 create mode 100644 drivers/mtd/nand/stm_nand_dt.h
 create mode 100644 include/linux/mtd/stm_nand.h

diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index f1cf503..2738eec 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -513,4 +513,11 @@ 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_STM_BCH
+	tristate "STMicroelectronics: NANDi BCH Controller"
+	depends on ARCH_STI
+	depends on OF
+	help
+	  Adds support for the STMicroelectronics NANDi BCH Controller.
+
 endif # MTD_NAND
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index a035e7c..60f374b 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -46,6 +46,7 @@ obj-$(CONFIG_MTD_NAND_NUC900)		+= nuc900_nand.o
 obj-$(CONFIG_MTD_NAND_MPC5121_NFC)	+= mpc5121_nfc.o
 obj-$(CONFIG_MTD_NAND_RICOH)		+= r852.o
 obj-$(CONFIG_MTD_NAND_JZ4740)		+= jz4740_nand.o
+obj-$(CONFIG_MTD_NAND_STM_BCH)		+= stm_nand_bch.o stm_nand_dt.o
 obj-$(CONFIG_MTD_NAND_GPMI_NAND)	+= gpmi-nand/
 obj-$(CONFIG_MTD_NAND_XWAY)		+= xway_nand.o
 obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH)	+= bcm47xxnflash/
diff --git a/drivers/mtd/nand/stm_nand_bch.c b/drivers/mtd/nand/stm_nand_bch.c
new file mode 100644
index 0000000..a104343
--- /dev/null
+++ b/drivers/mtd/nand/stm_nand_bch.c
@@ -0,0 +1,1618 @@
+/*
+ * Support for STMicroelectronics NANDi BCH Controller
+ *
+ * Copyright (c) 2014 STMicroelectronics Limited
+ *
+ * Authors: Angus Clark <Angus.Clark@st.com>
+ *	    Lee Jones <lee.jones@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/completion.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/stm_nand_bbt.h>
+#include <linux/mtd/stm_nand.h>
+#include <linux/mtd/partitions.h>
+
+#include "stm_nand_dt.h"
+#include "stm_nand_regs.h"
+
+/* NANDi BCH Controller properties */
+#define NANDI_BCH_SECTOR_SIZE			1024
+#define NANDI_BCH_DMA_ALIGNMENT			64
+#define NANDI_BCH_MAX_BUF_LIST			8
+#define NANDI_BCH_BUF_LIST_SIZE			(4 * NANDI_BCH_MAX_BUF_LIST)
+
+/* ONFI define 6 timing modes */
+#define ST_NAND_ONFI_TIMING_MODES		6
+
+#define PICO_TO_MILI(pico)			(pico / 1000)
+
+static int bch_ecc_strength[] = {
+	[BCH_18BIT_ECC] = 18,
+	[BCH_30BIT_ECC] = 30,
+	[BCH_NO_ECC] = 0,
+};
+
+/* BCH 'program' structure */
+struct bch_prog {
+	u32	multi_cs_addr[3];
+	u32	multi_cs_config;
+	u8	seq[16];
+	u32	addr;
+	u32	extra;
+	u8	cmd[4];
+	u32	reserved1;
+	u32	gen_cfg;
+	u32	delay;
+	u32	reserved2;
+	u32	seq_cfg;
+};
+
+/* BCH template programs (modified on-the-fly) */
+static struct bch_prog bch_prog_read_page = {
+	.cmd = {
+		NAND_CMD_READ0,
+		NAND_CMD_READSTART,
+	},
+	.seq = {
+		BCH_ECC_SCORE(0),
+		BCH_CMD_ADDR,
+		BCH_CL_CMD_1,
+		BCH_DATA_2_SECTOR,
+		BCH_STOP,
+	},
+	.gen_cfg = (GEN_CFG_DATA_8_NOT_16 |
+		    GEN_CFG_EXTRA_ADD_CYCLE |
+		    GEN_CFG_LAST_SEQ_NODE),
+	.seq_cfg = SEQ_CFG_GO_STOP,
+};
+
+static struct bch_prog bch_prog_write_page = {
+	.cmd = {
+		NAND_CMD_SEQIN,
+		NAND_CMD_PAGEPROG,
+		NAND_CMD_STATUS,
+	},
+	.seq = {
+		BCH_CMD_ADDR,
+		BCH_DATA_4_SECTOR,
+		BCH_CL_CMD_1,
+		BCH_CL_CMD_2,
+		BCH_OP_ERR,
+		BCH_STOP,
+	},
+	.gen_cfg = (GEN_CFG_DATA_8_NOT_16 |
+		    GEN_CFG_EXTRA_ADD_CYCLE |
+		    GEN_CFG_LAST_SEQ_NODE),
+	.seq_cfg = (SEQ_CFG_GO_STOP |
+		    SEQ_CFG_DATA_WRITE),
+};
+
+static struct bch_prog bch_prog_erase_block = {
+	.seq = {
+		BCH_CL_CMD_1,
+		BCH_AL_EX_0,
+		BCH_AL_EX_1,
+		BCH_AL_EX_2,
+		BCH_CL_CMD_2,
+		BCH_CL_CMD_3,
+		BCH_OP_ERR,
+		BCH_STOP,
+	},
+	.cmd = {
+		NAND_CMD_ERASE1,
+		NAND_CMD_ERASE1,
+		NAND_CMD_ERASE2,
+		NAND_CMD_STATUS,
+	},
+	.gen_cfg = (GEN_CFG_DATA_8_NOT_16 |
+		    GEN_CFG_EXTRA_ADD_CYCLE |
+		    GEN_CFG_LAST_SEQ_NODE),
+	.seq_cfg = (SEQ_CFG_GO_STOP |
+		    SEQ_CFG_ERASE),
+};
+
+/* Configure BCH read/write/erase programs */
+static void bch_configure_progs(struct nandi_controller *nandi)
+{
+	uint8_t data_opa = ffs(nandi->sectors_per_page) - 1;
+	uint8_t data_instr = BCH_INSTR(BCH_OPC_DATA, data_opa);
+	uint32_t gen_cfg_ecc = nandi->bch_ecc_mode << GEN_CFG_ECC_SHIFT;
+
+	/* Set 'DATA' instruction */
+	bch_prog_read_page.seq[3] = data_instr;
+	bch_prog_write_page.seq[1] = data_instr;
+
+	/* Set ECC mode */
+	bch_prog_read_page.gen_cfg |= gen_cfg_ecc;
+	bch_prog_write_page.gen_cfg |= gen_cfg_ecc;
+	bch_prog_erase_block.gen_cfg |= gen_cfg_ecc;
+
+	/*
+	 * Template sequences above are defined for devices that use 5 address
+	 * cycles for page Read/Write operations (and 3 for Erase operations).
+	 * Update sequences for devices that use 4 address cycles.
+	 */
+	if (!nandi->extra_addr) {
+		/* Clear 'GEN_CFG_EXTRA_ADD_CYCLE' flag */
+		bch_prog_read_page.gen_cfg &= ~GEN_CFG_EXTRA_ADD_CYCLE;
+		bch_prog_write_page.gen_cfg &= ~GEN_CFG_EXTRA_ADD_CYCLE;
+		bch_prog_erase_block.gen_cfg &= ~GEN_CFG_EXTRA_ADD_CYCLE;
+
+		/* Configure Erase sequence for 2 address cycles */
+		/* (page address) */
+		bch_prog_erase_block.seq[0] = BCH_CL_CMD_1;
+		bch_prog_erase_block.seq[1] = BCH_AL_EX_0;
+		bch_prog_erase_block.seq[2] = BCH_AL_EX_1;
+		bch_prog_erase_block.seq[3] = BCH_CL_CMD_2;
+		bch_prog_erase_block.seq[4] = BCH_CL_CMD_3;
+		bch_prog_erase_block.seq[5] = BCH_OP_ERR;
+		bch_prog_erase_block.seq[6] = BCH_STOP;
+	}
+}
+
+/*
+ * NANDi Interrupts (shared by Hamming and BCH controllers)
+ */
+static irqreturn_t nandi_irq_handler(int irq, void *dev)
+{
+	struct nandi_controller *nandi = dev;
+	unsigned int status;
+
+	status = readl(nandi->base + NANDBCH_INT_STA);
+
+	if (nandi->bch_ecc_mode && (status & NANDBCH_INT_SEQNODESOVER)) {
+		/* BCH */
+		writel(NANDBCH_INT_CLR_SEQNODESOVER,
+		       nandi->base + NANDBCH_INT_CLR);
+		complete(&nandi->seq_completed);
+		return IRQ_HANDLED;
+	}
+	if (status & NAND_INT_RBN) {
+		/* Hamming */
+		writel(NAND_INT_CLR_RBN, nandi->base + NANDHAM_INT_CLR);
+		complete(&nandi->rbn_completed);
+		return IRQ_HANDLED;
+	}
+
+	return IRQ_NONE;
+}
+
+static void nandi_enable_interrupts(struct nandi_controller *nandi,
+				    uint32_t irqs)
+{
+	uint32_t val;
+
+	val = readl(nandi->base + NANDBCH_INT_EN);
+	val |= irqs;
+	writel(val, nandi->base + NANDBCH_INT_EN);
+}
+
+static void nandi_disable_interrupts(struct nandi_controller *nandi,
+				     uint32_t irqs)
+{
+	uint32_t val;
+
+	val = readl(nandi->base + NANDBCH_INT_EN);
+	val &= ~irqs;
+	writel(val, nandi->base + NANDBCH_INT_EN);
+}
+
+/*
+ * BCH Operations
+ */
+static inline void bch_load_prog_cpu(struct nandi_controller *nandi,
+				     struct bch_prog *prog)
+{
+	uint32_t *src = (uint32_t *)prog;
+	uint32_t *dst = (uint32_t *)(nandi->base + NANDBCH_ADDRESS_REG_1);
+	int i;
+
+	for (i = 0; i < 16; i++) {
+		/* Skip registers marked as "reserved" */
+		if (i != 11 && i != 14)
+			writel(*src, dst);
+		dst++;
+		src++;
+	}
+}
+
+static void bch_wait_seq(struct nandi_controller *nandi)
+{
+	int ret;
+
+	ret = wait_for_completion_timeout(&nandi->seq_completed, HZ/2);
+	if (!ret)
+		dev_err(nandi->dev, "BCH Seq timeout\n");
+}
+
+uint8_t bch_erase_block(struct nandi_controller *nandi,
+			loff_t offs)
+{
+	struct nand_chip *chip = &nandi->info.chip;
+	struct bch_prog *prog = &bch_prog_erase_block;
+	uint8_t status;
+
+	dev_dbg(nandi->dev, "%s: offs = 0x%012llx\n", __func__, offs);
+
+	prog->extra = (uint32_t)(offs >> chip->page_shift);
+
+	emiss_nandi_select(nandi, STM_NANDI_BCH);
+
+	nandi_enable_interrupts(nandi, NANDBCH_INT_SEQNODESOVER);
+	reinit_completion(&nandi->seq_completed);
+
+	bch_load_prog_cpu(nandi, prog);
+
+	bch_wait_seq(nandi);
+
+	nandi_disable_interrupts(nandi, NANDBCH_INT_SEQNODESOVER);
+
+	status = (uint8_t)(readl(nandi->base +
+				 NANDBCH_CHECK_STATUS_REG_A) & 0xff);
+
+	return status;
+}
+EXPORT_SYMBOL(bch_erase_block);
+
+static int bch_erase(struct mtd_info *mtd, int page)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct nandi_controller *nandi = chip->priv;
+	uint32_t page_size = mtd->writesize;
+	loff_t offs = (loff_t)page * page_size;
+
+	return bch_erase_block(nandi, offs);
+}
+
+/*
+ * Detect an erased page, tolerating and correcting up to a specified number of
+ * bits at '0'.  (For many devices, it is now deemed within spec for an erased
+ * page to include a number of bits at '0', either as a result of read-disturb
+ * behaviour or 'stuck-at-zero' failures.)  Returns the number of corrected
+ * bits, or a '-1' if we have exceeded the maximum number of bits at '0' (likely
+ * to be a genuine uncorrectable ECC error).  In the latter case, the data must
+ * be returned unmodified, in accordance with the MTD API.
+ */
+static int check_erased_page(uint8_t *data, uint32_t page_size, int max_zeros)
+{
+	uint8_t *b = data;
+	int zeros = 0;
+	int i;
+
+	for (i = 0; i < page_size; i++) {
+		zeros += hweight8(~*b++);
+		if (zeros > max_zeros)
+			return -1;
+	}
+
+	if (zeros)
+		memset(data, 0xff, page_size);
+
+	return zeros;
+}
+
+/* Returns the number of ECC errors, or '-1' for uncorrectable error */
+int bch_read_page(struct nandi_controller *nandi,
+		  loff_t offs,
+		  uint8_t *buf)
+{
+	struct nand_chip *chip = &nandi->info.chip;
+	struct bch_prog *prog = &bch_prog_read_page;
+	uint32_t page_size = nandi->info.mtd.writesize;
+	unsigned long list_phys;
+	unsigned long buf_phys;
+	uint32_t ecc_err;
+	int ret = 0;
+
+	dev_dbg(nandi->dev, "%s: offs = 0x%012llx\n", __func__, offs);
+
+	BUG_ON(offs & (NANDI_BCH_DMA_ALIGNMENT - 1));
+
+	emiss_nandi_select(nandi, STM_NANDI_BCH);
+
+	nandi_enable_interrupts(nandi, NANDBCH_INT_SEQNODESOVER);
+	reinit_completion(&nandi->seq_completed);
+
+	/* Reset ECC stats */
+	writel(CFG_RESET_ECC_ALL | CFG_ENABLE_AFM,
+	       nandi->base + NANDBCH_CONTROLLER_CFG);
+	writel(CFG_ENABLE_AFM, nandi->base + NANDBCH_CONTROLLER_CFG);
+
+	prog->addr = (uint32_t)((offs >> (chip->page_shift - 8)) & 0xffffff00);
+
+	buf_phys = dma_map_single(NULL, buf, page_size, DMA_FROM_DEVICE);
+
+	memset(nandi->buf_list, 0x00, NANDI_BCH_BUF_LIST_SIZE);
+	nandi->buf_list[0] = buf_phys | (nandi->sectors_per_page - 1);
+
+	list_phys = dma_map_single(NULL, nandi->buf_list,
+				   NANDI_BCH_BUF_LIST_SIZE, DMA_TO_DEVICE);
+
+	writel(list_phys, nandi->base + NANDBCH_BUFFER_LIST_PTR);
+
+	bch_load_prog_cpu(nandi, prog);
+
+	bch_wait_seq(nandi);
+
+	nandi_disable_interrupts(nandi, NANDBCH_INT_SEQNODESOVER);
+
+	dma_unmap_single(NULL, list_phys, NANDI_BCH_BUF_LIST_SIZE,
+			 DMA_TO_DEVICE);
+	dma_unmap_single(NULL, buf_phys, page_size, DMA_FROM_DEVICE);
+
+	/* Use the maximum per-sector ECC count! */
+	ecc_err = readl(nandi->base + NANDBCH_ECC_SCORE_REG_A) & 0xff;
+	if (ecc_err == 0xff) {
+		/*
+		 * Downgrade uncorrectable ECC error for an erased page,
+		 * tolerating 'bch_ecc_strength' bits at zero.
+		 */
+		ret = check_erased_page(buf, page_size, chip->ecc.strength);
+		if (ret >= 0)
+			dev_dbg(nandi->dev,
+				"%s: erased page detected: \n"
+				"  downgrading uncorrectable ECC error.\n",
+				__func__);
+	} else {
+		ret = (int)ecc_err;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(bch_read_page);
+
+static int bch_read(struct mtd_info *mtd, struct nand_chip *chip,
+			 uint8_t *buf, int oob_required, int page)
+{
+	struct nandi_controller *nandi = chip->priv;
+	uint32_t page_size = mtd->writesize;
+	loff_t offs = (loff_t)page * page_size;
+	bool bounce = false;
+	uint8_t *p;
+	int ret;
+
+	if (((unsigned int)buf & (NANDI_BCH_DMA_ALIGNMENT - 1)) ||
+	    (!virt_addr_valid(buf))) /* vmalloc'd buffer! */
+		bounce = true;
+
+	p = bounce ? nandi->page_buf : buf;
+
+	ret = bch_read_page(nandi, offs, p);
+	if (ret < 0)
+		mtd->ecc_stats.failed++;
+	else
+		mtd->ecc_stats.corrected += ret;
+
+	if (bounce)
+		memcpy(buf, p, page_size);
+
+	return ret;
+}
+
+/* Returns the status of the NAND device following the write operation */
+uint8_t bch_write_page(struct nandi_controller *nandi,
+		       loff_t offs, const uint8_t *buf)
+{
+	struct nand_chip *chip = &nandi->info.chip;
+	struct bch_prog *prog = &bch_prog_write_page;
+	uint32_t page_size = nandi->info.mtd.writesize;
+	uint8_t *p = (uint8_t *)buf;
+	unsigned long list_phys;
+	unsigned long buf_phys;
+	uint8_t status;
+	bool bounce = false;
+
+	dev_dbg(nandi->dev, "%s: offs = 0x%012llx\n", __func__, offs);
+
+	BUG_ON(offs & (page_size - 1));
+
+	if (((unsigned long)buf & (NANDI_BCH_DMA_ALIGNMENT - 1)) ||
+	    !virt_addr_valid(buf)) { /* vmalloc'd buffer! */
+		bounce = true;
+	}
+
+	if (bounce) {
+		memcpy(nandi->page_buf, buf, page_size);
+		p = nandi->page_buf;
+		nandi->cached_page = -1;
+	}
+
+	emiss_nandi_select(nandi, STM_NANDI_BCH);
+
+	nandi_enable_interrupts(nandi, NANDBCH_INT_SEQNODESOVER);
+	reinit_completion(&nandi->seq_completed);
+
+	prog->addr = (uint32_t)((offs >> (chip->page_shift - 8)) & 0xffffff00);
+
+	buf_phys = dma_map_single(NULL, p, page_size, DMA_TO_DEVICE);
+	memset(nandi->buf_list, 0x00, NANDI_BCH_BUF_LIST_SIZE);
+	nandi->buf_list[0] = buf_phys | (nandi->sectors_per_page - 1);
+
+	list_phys = dma_map_single(NULL, nandi->buf_list,
+				   NANDI_BCH_BUF_LIST_SIZE, DMA_TO_DEVICE);
+
+	writel(list_phys, nandi->base + NANDBCH_BUFFER_LIST_PTR);
+
+	bch_load_prog_cpu(nandi, prog);
+
+	bch_wait_seq(nandi);
+
+	nandi_disable_interrupts(nandi, NANDBCH_INT_SEQNODESOVER);
+
+	dma_unmap_single(NULL, list_phys, NANDI_BCH_BUF_LIST_SIZE,
+			 DMA_TO_DEVICE);
+	dma_unmap_single(NULL, buf_phys, page_size, DMA_FROM_DEVICE);
+
+	status = (uint8_t)(readl(nandi->base +
+				 NANDBCH_CHECK_STATUS_REG_A) & 0xff);
+
+	return status;
+}
+EXPORT_SYMBOL(bch_write_page);
+
+static int bch_write(struct mtd_info *mtd, struct nand_chip *chip,
+		     uint32_t offset, int data_len, const uint8_t *buf,
+		     int oob_required, int page, int cached, int raw)
+{
+	struct nandi_controller *nandi = chip->priv;
+	uint32_t page_size = mtd->writesize;
+	loff_t offs = (loff_t)page * page_size;
+	int ret;
+
+	ret = bch_write_page(nandi, offs, buf);
+	if (ret & NAND_STATUS_FAIL)
+		return -EIO;
+
+	return 0;
+}
+
+/*
+ * Hamming-FLEX operations
+ */
+static int flex_wait_rbn(struct nandi_controller *nandi)
+{
+	int ret;
+
+	ret = wait_for_completion_timeout(&nandi->rbn_completed, HZ/2);
+	if (!ret)
+		dev_err(nandi->dev, "FLEX RBn timeout\n");
+
+	return ret;
+}
+
+static void flex_cmd(struct nandi_controller *nandi, uint8_t cmd)
+{
+	uint32_t val;
+
+	val = (FLEX_CMD_CSN | FLEX_CMD_BEATS_1 | cmd);
+	writel(val, nandi->base + NANDHAM_FLEX_CMD);
+}
+
+static void flex_addr(struct nandi_controller *nandi,
+		      uint32_t addr, int cycles)
+{
+	addr &= 0x00ffffff;
+
+	BUG_ON(cycles < 1);
+	BUG_ON(cycles > 3);
+
+	addr |= (FLEX_ADDR_CSN | FLEX_ADDR_ADD8_VALID);
+	addr |= (cycles & 0x3) << 28;
+
+	writel(addr, nandi->base + NANDHAM_FLEX_ADD);
+}
+
+/*
+ * Partial implementation of MTD/NAND Interface, based on Hamming-FLEX
+ * operation.
+ *
+ * Allows us to make use of nand_base.c functions where possible
+ * (e.g. nand_scan_ident() and friends).
+ */
+static void flex_command_lp(struct mtd_info *mtd, unsigned int command,
+			    int column, int page)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct nandi_controller *nandi = chip->priv;
+
+	emiss_nandi_select(nandi, STM_NANDI_HAMMING);
+
+	switch (command) {
+	case NAND_CMD_READOOB:
+		/* Emulate NAND_CMD_READOOB */
+		column += mtd->writesize;
+		command = NAND_CMD_READ0;
+		break;
+	case NAND_CMD_READ0:
+	case NAND_CMD_ERASE1:
+	case NAND_CMD_SEQIN:
+	case NAND_CMD_RESET:
+	case NAND_CMD_PARAM:
+		/* Prime RBn wait */
+		nandi_enable_interrupts(nandi, NAND_INT_RBN);
+		reinit_completion(&nandi->rbn_completed);
+		break;
+	case NAND_CMD_READID:
+	case NAND_CMD_STATUS:
+	case NAND_CMD_ERASE2:
+		break;
+	default:
+		/* Catch unexpected commands */
+		BUG();
+	}
+
+	/*
+	 * Command Cycle
+	 */
+	flex_cmd(nandi, command);
+
+	/*
+	 * Address Cycles
+	 */
+	if (column != -1)
+		flex_addr(nandi, column,
+			  (command == NAND_CMD_READID) ? 1 : 2);
+
+	if (page != -1)
+		flex_addr(nandi, page, nandi->extra_addr ? 3 : 2);
+
+	/* Complete 'READ0' command */
+	if (command == NAND_CMD_READ0)
+		flex_cmd(nandi, NAND_CMD_READSTART);
+
+	/* Wait for RBn, if required                        */
+	/* (Note, other commands may handle wait elsewhere) */
+	if (command == NAND_CMD_RESET ||
+	    command == NAND_CMD_READ0 ||
+	    command == NAND_CMD_PARAM) {
+		flex_wait_rbn(nandi);
+		nandi_disable_interrupts(nandi, NAND_INT_RBN);
+	}
+}
+
+static uint8_t flex_read_byte(struct mtd_info *mtd)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct nandi_controller *nandi = chip->priv;
+
+	emiss_nandi_select(nandi, STM_NANDI_HAMMING);
+
+	return (uint8_t)(readl(nandi->base + NANDHAM_FLEX_DATA) & 0xff);
+}
+
+static int flex_wait_func(struct mtd_info *mtd, struct nand_chip *chip)
+{
+	struct nandi_controller *nandi = chip->priv;
+
+	emiss_nandi_select(nandi, STM_NANDI_HAMMING);
+
+	flex_wait_rbn(nandi);
+
+	flex_cmd(nandi, NAND_CMD_STATUS);
+
+	return (int)(readl(nandi->base + NANDHAM_FLEX_DATA) & 0xff);
+}
+
+/* Multi-CS devices not supported */
+static void flex_select_chip(struct mtd_info *mtd, int chipnr)
+{
+
+}
+
+static void flex_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct nandi_controller *nandi = chip->priv;
+	int aligned;
+
+	emiss_nandi_select(nandi, STM_NANDI_HAMMING);
+
+	/* Read bytes until buf is 4-byte aligned */
+	while (len && ((unsigned int)buf & 0x3)) {
+		*buf++ = (uint8_t)(readl(nandi->base + NANDHAM_FLEX_DATA)
+				   & 0xff);
+		len--;
+	};
+
+	/* Use 'BEATS_4'/readsl */
+	if (len > 8) {
+		aligned = len & ~0x3;
+		writel(FLEX_DATA_CFG_BEATS_4 | FLEX_DATA_CFG_CSN,
+		       nandi->base + NANDHAM_FLEX_DATAREAD_CONFIG);
+
+		readsl(nandi->base + NANDHAM_FLEX_DATA, buf, aligned >> 2);
+
+		buf += aligned;
+		len -= aligned;
+
+		writel(FLEX_DATA_CFG_BEATS_1 | FLEX_DATA_CFG_CSN,
+		       nandi->base + NANDHAM_FLEX_DATAREAD_CONFIG);
+	}
+
+	/* Mop up remaining bytes */
+	while (len > 0) {
+		*buf++ = (uint8_t)(readl(nandi->base + NANDHAM_FLEX_DATA)
+				   & 0xff);
+		len--;
+	}
+}
+
+static void flex_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct nandi_controller *nandi = chip->priv;
+	int aligned;
+
+	/* Write bytes until buf is 4-byte aligned */
+	while (len && ((unsigned int)buf & 0x3)) {
+		writel(*buf++, nandi->base + NANDHAM_FLEX_DATA);
+		len--;
+	};
+
+	/* USE 'BEATS_4/writesl */
+	if (len > 8) {
+		aligned = len & ~0x3;
+		writel(FLEX_DATA_CFG_BEATS_4 | FLEX_DATA_CFG_CSN,
+		       nandi->base + NANDHAM_FLEX_DATAWRITE_CONFIG);
+		writesl(nandi->base + NANDHAM_FLEX_DATA, buf, aligned >> 2);
+		buf += aligned;
+		len -= aligned;
+		writel(FLEX_DATA_CFG_BEATS_1 | FLEX_DATA_CFG_CSN,
+		       nandi->base + NANDHAM_FLEX_DATAWRITE_CONFIG);
+	}
+
+	/* Mop up remaining bytes */
+	while (len > 0) {
+		writel(*buf++, nandi->base + NANDHAM_FLEX_DATA);
+		len--;
+	}
+}
+
+int flex_read_raw(struct nandi_controller *nandi,
+			 uint32_t page_addr,
+			 uint32_t col_addr,
+			 uint8_t *buf, uint32_t len)
+{
+	dev_dbg(nandi->dev, "%s %u bytes at [0x%06x,0x%04x]\n",
+		__func__, len, page_addr, col_addr);
+
+	BUG_ON(len & 0x3);
+	BUG_ON((unsigned long)buf & 0x3);
+
+	emiss_nandi_select(nandi, STM_NANDI_HAMMING);
+	nandi_enable_interrupts(nandi, NAND_INT_RBN);
+	reinit_completion(&nandi->rbn_completed);
+
+	writel(FLEX_DATA_CFG_BEATS_4 | FLEX_DATA_CFG_CSN,
+	       nandi->base + NANDHAM_FLEX_DATAREAD_CONFIG);
+
+	flex_cmd(nandi, NAND_CMD_READ0);
+	flex_addr(nandi, col_addr, 2);
+	flex_addr(nandi, page_addr, nandi->extra_addr ? 3 : 2);
+	flex_cmd(nandi, NAND_CMD_READSTART);
+
+	flex_wait_rbn(nandi);
+
+	readsl(nandi->base + NANDHAM_FLEX_DATA, buf, len / 4);
+
+	nandi_disable_interrupts(nandi, NAND_INT_RBN);
+
+	writel(FLEX_DATA_CFG_BEATS_1 | FLEX_DATA_CFG_CSN,
+	       nandi->base + NANDHAM_FLEX_DATAREAD_CONFIG);
+
+	return 0;
+}
+EXPORT_SYMBOL(flex_read_raw);
+
+static int bch_mtd_read_oob(struct mtd_info *mtd,
+			    struct nand_chip *chip, int page)
+{
+	BUG();
+	return 0;
+}
+
+static int bch_mtd_write_oob(struct mtd_info *mtd,
+			     struct nand_chip *chip, int page)
+{
+	BUG();
+	return 0;
+}
+
+static int bch_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+			     uint8_t *buf, int oob_required, int page)
+{
+	BUG();
+	return 0;
+}
+
+static int bch_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+			      const uint8_t *buf, int oob_required)
+{
+	BUG();
+	return 0;
+}
+
+static void bch_hwctl(struct mtd_info *mtd, int mode)
+{
+	BUG();
+}
+
+static int bch_calculate(struct mtd_info *mtd, const uint8_t *dat,
+			 uint8_t *ecc_code)
+{
+	BUG();
+	return 0;
+}
+
+static int bch_correct(struct mtd_info *mtd, uint8_t *dat, uint8_t *read_ecc,
+		       uint8_t *calc_ecc)
+{
+	BUG();
+	return 0;
+}
+
+/*
+ * Initialisation
+ */
+static int bch_check_compatibility(struct nandi_controller *nandi,
+				   struct mtd_info *mtd,
+				   struct nand_chip *chip)
+{
+	if (chip->bits_per_cell > 1)
+		dev_warn(nandi->dev, "MLC NAND not fully supported\n");
+
+	if (chip->options & NAND_BUSWIDTH_16) {
+		dev_err(nandi->dev, "x16 NAND not supported\n");
+		return false;
+	}
+
+	if (nandi->blocks_per_device / 4 > mtd->writesize) {
+		/* Need to implement multi-page BBT support... */
+		dev_err(nandi->dev, "BBT too big to fit in single page\n");
+		return false;
+	}
+
+	if (bch_ecc_sizes[nandi->bch_ecc_mode] * nandi->sectors_per_page >
+	    mtd->oobsize) {
+		dev_err(nandi->dev, "insufficient OOB for selected ECC\n");
+		return false;
+	}
+
+	return true;
+}
+
+/* Select strongest ECC scheme compatible with OOB size */
+static int bch_set_ecc_auto(struct nandi_controller *nandi,
+			    struct mtd_info *mtd)
+{
+	int oob_bytes_per_sector = mtd->oobsize / nandi->sectors_per_page;
+	int try_ecc_modes[] = { BCH_30BIT_ECC, BCH_18BIT_ECC, -1 };
+	int m, ecc_mode;
+
+	for (m = 0; try_ecc_modes[m] >= 0; m++) {
+		ecc_mode = try_ecc_modes[m];
+		if (oob_bytes_per_sector >= bch_ecc_sizes[ecc_mode]) {
+			nandi->bch_ecc_mode = ecc_mode;
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
+
+static void nandi_set_mtd_defaults(struct nandi_controller *nandi,
+				   struct mtd_info *mtd, struct nand_chip *chip)
+{
+	struct nandi_info *info = &nandi->info;
+	int i;
+
+	/* ecclayout */
+	info->ecclayout.eccbytes = mtd->oobsize;
+	for (i = 0; i < 64; i++)
+		info->ecclayout.eccpos[i] = i;
+	info->ecclayout.oobfree[0].offset = 0;
+	info->ecclayout.oobfree[0].length = 0;
+	chip->ecc.mode = NAND_ECC_HW;
+
+	/* nand_chip */
+	chip->controller = &chip->hwcontrol;
+	spin_lock_init(&chip->controller->lock);
+	init_waitqueue_head(&chip->controller->wq);
+	chip->state = FL_READY;
+	chip->priv = nandi;
+	chip->ecc.layout = &info->ecclayout;
+	chip->options |= NAND_NO_SUBPAGE_WRITE;
+
+	chip->cmdfunc = flex_command_lp;
+	chip->read_byte = flex_read_byte;
+	chip->select_chip = flex_select_chip;
+	chip->waitfunc = flex_wait_func;
+	chip->read_buf = flex_read_buf;
+	chip->write_buf = flex_write_buf;
+
+	chip->bbt_options |= NAND_BBT_USE_FLASH;
+
+	/* mtd_info */
+	mtd->owner = THIS_MODULE;
+	mtd->type = MTD_NANDFLASH;
+	mtd->flags = MTD_CAP_NANDFLASH;
+	mtd->ecclayout = &info->ecclayout;
+
+	chip->ecc.hwctl = bch_hwctl;
+	chip->ecc.calculate = bch_calculate;
+	chip->ecc.correct = bch_correct;
+
+	chip->ecc.read_oob = bch_mtd_read_oob;
+	chip->ecc.write_oob = bch_mtd_write_oob;
+
+	chip->ecc.read_page = bch_read;
+	chip->ecc.read_page_raw = bch_read_page_raw;
+	chip->ecc.write_page_raw = bch_write_page_raw;
+	chip->write_page = bch_write;
+	chip->erase = bch_erase;
+
+#ifdef CONFIG_MTD_NAND_STM_BCH_BBT
+	chip->scan_bbt = bch_scan_bbt;
+	chip->block_bad = bch_block_isbad;
+	chip->block_markbad = bch_block_markbad;
+#endif
+}
+
+/*
+ * Timing and Clocks
+ */
+
+static void nandi_clk_enable(struct nandi_controller *nandi)
+{
+	clk_prepare_enable(nandi->emi_clk);
+	clk_prepare_enable(nandi->bch_clk);
+}
+
+static void nandi_clk_disable(struct nandi_controller *nandi)
+{
+	clk_disable_unprepare(nandi->emi_clk);
+	clk_disable_unprepare(nandi->bch_clk);
+}
+
+static struct clk *nandi_clk_setup(struct nandi_controller *nandi,
+				   char *clkname)
+{
+	struct clk *clk;
+	int ret;
+
+	clk = devm_clk_get(nandi->dev, clkname);
+	if (IS_ERR_OR_NULL(clk)) {
+		dev_warn(nandi->dev, "Failed to get %s clock\n", clkname);
+		return NULL;
+	}
+
+	ret = clk_prepare_enable(clk);
+	if (ret) {
+		dev_warn(nandi->dev, "Failed to enable %s clock\n", clkname);
+		return NULL;
+	}
+
+	return clk;
+}
+
+/* Derive Hamming-FLEX timing register values from 'nand_sdr_timings' data */
+static void flex_calc_timing_registers(const struct nand_sdr_timings *spec,
+				       int tCLK, int relax,
+				       uint32_t *ctl_timing,
+				       uint32_t *wen_timing,
+				       uint32_t *ren_timing)
+{
+	int tMAX_HOLD;
+	int n_ctl_setup;
+	int n_ctl_hold;
+	int n_ctl_wb;
+
+	int tMAX_WEN_OFF;
+	int n_wen_on;
+	int n_wen_off;
+
+	int tMAX_REN_OFF;
+	int n_ren_on;
+	int n_ren_off;
+
+	/*
+	 * CTL_TIMING
+	 */
+
+	/*	- SETUP */
+	n_ctl_setup = (PICO_TO_MILI(spec->tCLS_min - spec->tWP_min)
+		       + tCLK - 1)/tCLK;
+	if (n_ctl_setup < 1)
+		n_ctl_setup = 1;
+	n_ctl_setup += relax;
+
+	/*	- HOLD */
+	tMAX_HOLD = spec->tCLH_min;
+	if (spec->tCH_min > tMAX_HOLD)
+		tMAX_HOLD = spec->tCH_min;
+	if (spec->tALH_min > tMAX_HOLD)
+		tMAX_HOLD = spec->tALH_min;
+	if (spec->tDH_min > tMAX_HOLD)
+		tMAX_HOLD = spec->tDH_min;
+	n_ctl_hold = (PICO_TO_MILI(tMAX_HOLD) + tCLK - 1)/tCLK + relax;
+
+	/*	- CE_deassert_hold = 0 */
+
+	/*	- WE_high_to_RBn_low */
+	n_ctl_wb = (PICO_TO_MILI(spec->tWB_max) + tCLK - 1)/tCLK;
+
+	*ctl_timing = ((n_ctl_setup & 0xff) |
+		       (n_ctl_hold & 0xff) << 8 |
+		       (n_ctl_wb & 0xff) << 24);
+
+	/*
+	 * WEN_TIMING
+	 */
+
+	/*	- ON */
+	n_wen_on = (PICO_TO_MILI(spec->tWH_min) + tCLK - 1)/tCLK + relax;
+
+	/*	- OFF */
+	tMAX_WEN_OFF = spec->tWC_min - spec->tWH_min;
+	if (spec->tWP_min > tMAX_WEN_OFF)
+		tMAX_WEN_OFF = spec->tWP_min;
+	n_wen_off = (PICO_TO_MILI(tMAX_WEN_OFF) + tCLK - 1)/tCLK + relax;
+
+	*wen_timing = ((n_wen_on & 0xff) |
+		       (n_wen_off & 0xff) << 8);
+
+	/*
+	 * REN_TIMING
+	 */
+
+	/*	- ON */
+	n_ren_on = (PICO_TO_MILI(spec->tREH_min) + tCLK - 1)/tCLK + relax;
+
+	/*	- OFF */
+	tMAX_REN_OFF = spec->tRC_min - spec->tREH_min;
+	if (spec->tRP_min > tMAX_REN_OFF)
+		tMAX_REN_OFF = spec->tRP_min;
+	if (spec->tREA_max > tMAX_REN_OFF)
+		tMAX_REN_OFF = spec->tREA_max;
+	n_ren_off = (PICO_TO_MILI(tMAX_REN_OFF) + tCLK - 1)/tCLK + 1 + relax;
+
+	*ren_timing = ((n_ren_on & 0xff) |
+		       (n_ren_off & 0xff) << 8);
+}
+
+/* Derive BCH timing register values from 'nand_sdr_timings' data */
+static void bch_calc_timing_registers(const struct nand_sdr_timings *spec,
+				      int tCLK, int relax,
+				      uint32_t *ctl_timing,
+				      uint32_t *wen_timing,
+				      uint32_t *ren_timing)
+{
+	int tMAX_HOLD;
+	int n_ctl_setup;
+	int n_ctl_hold;
+	int n_ctl_wb;
+
+	int n_wen_on;
+	int n_wen_off;
+	int wen_half_on;
+	int wen_half_off;
+
+	int tMAX_REN_ON;
+	int tMAX_CS_DEASSERT;
+	int n_d_latch;
+	int n_telqv;
+	int n_ren_on;
+	int n_ren_off;
+	int ren_half_on;
+	int ren_half_off;
+
+	/*
+	 * CTL_TIMING
+	 */
+
+	/*	- SETUP */
+	if (spec->tCLS_min > spec->tWP_min)
+		n_ctl_setup = (PICO_TO_MILI(spec->tCLS_min - spec->tWP_min)
+			       + tCLK - 1)/tCLK;
+	else
+		n_ctl_setup = 0;
+	n_ctl_setup += relax;
+
+	/*	- HOLD */
+	tMAX_HOLD = spec->tCLH_min;
+	if (spec->tCH_min > tMAX_HOLD)
+		tMAX_HOLD = spec->tCH_min;
+	if (spec->tALH_min > tMAX_HOLD)
+		tMAX_HOLD = spec->tALH_min;
+	if (spec->tDH_min > tMAX_HOLD)
+		tMAX_HOLD = spec->tDH_min;
+	n_ctl_hold = (PICO_TO_MILI(tMAX_HOLD) + tCLK - 1)/tCLK + relax;
+	/*	- CE_deassert_hold = 0 */
+
+	/*	- WE_high_to_RBn_low */
+	n_ctl_wb = (PICO_TO_MILI(spec->tWB_max) + tCLK - 1)/tCLK;
+
+	*ctl_timing = ((n_ctl_setup & 0xff) |
+		       (n_ctl_hold & 0xff) << 8 |
+		       (n_ctl_wb & 0xff) << 24);
+
+	/*
+	 * WEN_TIMING
+	 */
+
+	/*	- ON */
+	n_wen_on = (2 * PICO_TO_MILI(spec->tWH_min) + tCLK - 1)/tCLK;
+	wen_half_on = n_wen_on % 2;
+	n_wen_on /= 2;
+	n_wen_on += relax;
+
+	/*	- OFF */
+	n_wen_off = (2 * PICO_TO_MILI(spec->tWP_min) + tCLK - 1)/tCLK;
+	wen_half_off = n_wen_off % 2;
+	n_wen_off /= 2;
+	n_wen_off += relax;
+
+	*wen_timing = ((n_wen_on & 0xff) |
+		       (n_wen_off & 0xff) << 8 |
+		       (wen_half_on << 16) |
+		       (wen_half_off << 17));
+
+	/*
+	 * REN_TIMING
+	 */
+
+	/*	- ON */
+	tMAX_REN_ON = spec->tRC_min - spec->tRP_min;
+	if (spec->tREH_min > tMAX_REN_ON)
+		tMAX_REN_ON = spec->tREH_min;
+
+	n_ren_on = (2 * PICO_TO_MILI(tMAX_REN_ON) + tCLK - 1)/tCLK;
+	ren_half_on = n_ren_on % 2;
+	n_ren_on /= 2;
+	n_ren_on += relax;
+
+	/*	- OFF */
+	n_ren_off = (2 * PICO_TO_MILI(spec->tREA_max) + tCLK - 1)/tCLK;
+	ren_half_off = n_ren_off % 2;
+	n_ren_off /= 2;
+	n_ren_off += relax;
+
+	/*	- DATA_LATCH */
+	if (PICO_TO_MILI(spec->tREA_max) <=
+	    (PICO_TO_MILI(spec->tRP_min) - (2 * tCLK)))
+		n_d_latch = 0;
+	else if (PICO_TO_MILI(spec->tREA_max) <=
+		 (PICO_TO_MILI(spec->tRP_min) - tCLK))
+		n_d_latch = 1;
+	else if ((spec->tREA_max <= spec->tRP_min) &&
+		 (PICO_TO_MILI(spec->tRHOH_min) >= 2 * tCLK))
+		n_d_latch = 2;
+	else
+		n_d_latch = 3;
+
+	/*	- TELQV */
+	tMAX_CS_DEASSERT = spec->tCOH_min;
+	if (spec->tCHZ_max > tMAX_CS_DEASSERT)
+		tMAX_CS_DEASSERT = spec->tCHZ_max;
+
+	n_telqv = (PICO_TO_MILI(tMAX_CS_DEASSERT) + tCLK - 1)/tCLK;
+
+	*ren_timing = ((n_ren_on & 0xff) |
+		       (n_ren_off & 0xff) << 8 |
+		       (n_d_latch & 0x3) << 16 |
+		       (wen_half_on << 18) |
+		       (wen_half_off << 19) |
+		       (n_telqv & 0xff) << 24);
+}
+
+static void flex_configure_timing_registers(struct nandi_controller *nandi,
+					    const struct nand_sdr_timings *spec,
+					    int relax)
+{
+	uint32_t ctl_timing;
+	uint32_t wen_timing;
+	uint32_t ren_timing;
+	int emi_t_ns;
+
+	/* Select Hamming Controller */
+	emiss_nandi_select(nandi, STM_NANDI_HAMMING);
+
+	/* Get EMI clock (default 100MHz) */
+	if (nandi->emi_clk)
+		emi_t_ns = 1000000000UL / clk_get_rate(nandi->emi_clk);
+	else {
+		dev_warn(nandi->dev,
+			 "No EMI clock available; assuming default 100MHz\n");
+		emi_t_ns = 10;
+	}
+
+	/* Derive timing register values from specification */
+	flex_calc_timing_registers(spec, emi_t_ns, relax,
+				   &ctl_timing, &wen_timing, &ren_timing);
+
+	dev_dbg(nandi->dev,
+		"updating FLEX timing configuration [0x%08x, 0x%08x, 0x%08x]\n",
+		ctl_timing, wen_timing, ren_timing);
+
+	/* Program timing registers */
+	writel(ctl_timing, nandi->base + NANDHAM_CTL_TIMING);
+	writel(wen_timing, nandi->base + NANDHAM_WEN_TIMING);
+	writel(ren_timing, nandi->base + NANDHAM_REN_TIMING);
+}
+
+static void bch_configure_timing_registers(struct nandi_controller *nandi,
+					   const struct nand_sdr_timings *spec,
+					   int relax)
+{
+	uint32_t ctl_timing;
+	uint32_t wen_timing;
+	uint32_t ren_timing;
+	int bch_t_ns;
+
+	/* Select BCH Controller */
+	emiss_nandi_select(nandi, STM_NANDI_BCH);
+
+	/* Get BCH clock (default 200MHz) */
+	if (nandi->bch_clk)
+		bch_t_ns = 1000000000UL / clk_get_rate(nandi->bch_clk);
+	else {
+		dev_warn(nandi->dev,
+			 "No BCH clock available; assuming default 200MHz\n");
+		bch_t_ns = 5;
+	}
+
+	/* Derive timing register values from specification */
+	bch_calc_timing_registers(spec, bch_t_ns, relax,
+				  &ctl_timing, &wen_timing, &ren_timing);
+
+	dev_dbg(nandi->dev,
+		"updating BCH timing configuration [0x%08x, 0x%08x, 0x%08x]\n",
+		ctl_timing, wen_timing, ren_timing);
+
+	/* Program timing registers */
+	writel(ctl_timing, nandi->base + NANDBCH_CTL_TIMING);
+	writel(wen_timing, nandi->base + NANDBCH_WEN_TIMING);
+	writel(ren_timing, nandi->base + NANDBCH_REN_TIMING);
+}
+
+static void nandi_configure_timing_registers(struct nandi_controller *nandi,
+					const struct nand_sdr_timings *spec,
+					int relax)
+{
+	bch_configure_timing_registers(nandi, spec, relax);
+	flex_configure_timing_registers(nandi, spec, relax);
+}
+
+static void nandi_init_hamming(struct nandi_controller *nandi, int emi_bank)
+{
+	dev_dbg(nandi->dev, "%s\n", __func__);
+
+	emiss_nandi_select(nandi, STM_NANDI_HAMMING);
+
+	/* Reset and disable boot-mode controller */
+	writel(BOOT_CFG_RESET, nandi->base + NANDHAM_BOOTBANK_CFG);
+	udelay(1);
+	writel(0x00000000, nandi->base + NANDHAM_BOOTBANK_CFG);
+
+	/* Reset controller */
+	writel(CFG_RESET, nandi->base + NANDHAM_FLEXMODE_CFG);
+	udelay(1);
+	writel(0x00000000, nandi->base + NANDHAM_FLEXMODE_CFG);
+
+	/* Set EMI Bank */
+	writel(0x1 << emi_bank, nandi->base + NANDHAM_FLEX_MUXCTRL);
+
+	/* Enable FLEX mode */
+	writel(CFG_ENABLE_FLEX, nandi->base + NANDHAM_FLEXMODE_CFG);
+
+	/* Configure FLEX_DATA_READ/WRITE for 1-byte access */
+	writel(FLEX_DATA_CFG_BEATS_1 | FLEX_DATA_CFG_CSN,
+	       nandi->base + NANDHAM_FLEX_DATAREAD_CONFIG);
+	writel(FLEX_DATA_CFG_BEATS_1 | FLEX_DATA_CFG_CSN,
+	       nandi->base + NANDHAM_FLEX_DATAREAD_CONFIG);
+
+	/* RBn interrupt on rising edge */
+	writel(NAND_EDGE_CFG_RBN_RISING, nandi->base + NANDHAM_INT_EDGE_CFG);
+
+	/* Enable interrupts */
+	nandi_enable_interrupts(nandi, NAND_INT_ENABLE);
+}
+
+static void nandi_init_bch(struct nandi_controller *nandi, int emi_bank)
+{
+	dev_dbg(nandi->dev, "%s\n", __func__);
+
+	/* Initialise BCH Controller */
+	emiss_nandi_select(nandi, STM_NANDI_BCH);
+
+	/* Reset and disable boot-mode controller */
+	writel(BOOT_CFG_RESET, nandi->base + NANDBCH_BOOTBANK_CFG);
+	udelay(1);
+	writel(0x00000000, nandi->base + NANDBCH_BOOTBANK_CFG);
+
+	/* Reset AFM controller */
+	writel(CFG_RESET, nandi->base + NANDBCH_CONTROLLER_CFG);
+	udelay(1);
+	writel(0x00000000, nandi->base + NANDBCH_CONTROLLER_CFG);
+
+	/* Set EMI Bank */
+	writel(0x1 << emi_bank, nandi->base + NANDBCH_FLEX_MUXCTRL);
+
+	/* Reset ECC stats */
+	writel(CFG_RESET_ECC_ALL, nandi->base + NANDBCH_CONTROLLER_CFG);
+	udelay(1);
+
+	/* Enable AFM */
+	writel(CFG_ENABLE_AFM, nandi->base + NANDBCH_CONTROLLER_CFG);
+
+	/* Configure Read DMA Plugs (values supplied by Validation) */
+	writel(0x00000005, nandi->dma + EMISS_NAND_RD_DMA_PAGE_SIZE);
+	writel(0x00000005, nandi->dma + EMISS_NAND_RD_DMA_MAX_OPCODE_SIZE);
+	writel(0x00000002, nandi->dma + EMISS_NAND_RD_DMA_MIN_OPCODE_SIZE);
+	writel(0x00000001, nandi->dma + EMISS_NAND_RD_DMA_MAX_CHUNK_SIZE);
+	writel(0x00000000, nandi->dma + EMISS_NAND_RD_DMA_MAX_MESSAGE_SIZE);
+
+	/* Configure Write DMA Plugs (values supplied by Validation) */
+	writel(0x00000005, nandi->dma + EMISS_NAND_WR_DMA_PAGE_SIZE);
+	writel(0x00000005, nandi->dma + EMISS_NAND_WR_DMA_MAX_OPCODE_SIZE);
+	writel(0x00000002, nandi->dma + EMISS_NAND_WR_DMA_MIN_OPCODE_SIZE);
+	writel(0x00000001, nandi->dma + EMISS_NAND_WR_DMA_MAX_CHUNK_SIZE);
+	writel(0x00000000, nandi->dma + EMISS_NAND_WR_DMA_MAX_MESSAGE_SIZE);
+
+	nandi_enable_interrupts(nandi, NAND_INT_ENABLE);
+}
+
+static void nandi_init_controller(struct nandi_controller *nandi,
+				  int emi_bank)
+{
+	nandi_init_bch(nandi, emi_bank);
+	nandi_init_hamming(nandi, emi_bank);
+}
+
+/* Initialise working buffers, accomodating DMA alignment constraints */
+static int nandi_init_working_buffers(struct nandi_controller *nandi,
+				      struct nandi_bbt_info *bbt_info,
+				      struct mtd_info *mtd)
+{
+	uint32_t bbt_buf_size;
+	uint32_t buf_size;
+
+	/*	- Page and OOB */
+	buf_size = mtd->writesize + mtd->oobsize + NANDI_BCH_DMA_ALIGNMENT;
+
+	/*	- BBT data (page-size aligned) */
+	bbt_info->bbt_size = nandi->blocks_per_device >> 2; /* 2 bits/block */
+	bbt_buf_size = ALIGN(bbt_info->bbt_size, mtd->writesize);
+	buf_size += bbt_buf_size + NANDI_BCH_DMA_ALIGNMENT;
+
+	/*	- BCH BUF list */
+	buf_size += NANDI_BCH_BUF_LIST_SIZE + NANDI_BCH_DMA_ALIGNMENT;
+
+	/* Allocate bufffer */
+	nandi->buf = devm_kzalloc(nandi->dev, buf_size, GFP_KERNEL);
+	if (!nandi->buf) {
+		dev_err(nandi->dev, "failed to allocate working buffers\n");
+		return -ENOMEM;
+	}
+
+	/* Set/Align buffer pointers */
+	nandi->page_buf = PTR_ALIGN(nandi->buf, NANDI_BCH_DMA_ALIGNMENT);
+	nandi->oob_buf  = nandi->page_buf + mtd->writesize;
+	bbt_info->bbt   = PTR_ALIGN(nandi->oob_buf + mtd->oobsize,
+				    NANDI_BCH_DMA_ALIGNMENT);
+	nandi->buf_list = (uint32_t *)PTR_ALIGN(bbt_info->bbt + bbt_buf_size,
+						NANDI_BCH_DMA_ALIGNMENT);
+	nandi->cached_page = -1;
+
+	return 0;
+}
+
+static int remap_named_resource(struct platform_device *pdev,
+				char *name,
+				void __iomem **io_ptr)
+{
+	struct resource *res, *mem;
+	resource_size_t size;
+	void __iomem *p;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
+	if (!res)
+		return -ENXIO;
+
+	size = resource_size(res);
+
+	mem = devm_request_mem_region(&pdev->dev, res->start, size, name);
+	if (!mem)
+		return -EBUSY;
+
+	p = devm_ioremap_nocache(&pdev->dev, res->start, size);
+	if (!p)
+		return -ENOMEM;
+
+	*io_ptr = p;
+
+	return 0;
+}
+
+static struct nandi_controller *
+nandi_init_resources(struct platform_device *pdev)
+{
+	struct stm_nand_bch_ddata *ddata = platform_get_drvdata(pdev);
+	struct nandi_controller *nandi;
+	int irq;
+	int err;
+
+	nandi = devm_kzalloc(&pdev->dev, sizeof(*nandi), GFP_KERNEL);
+	if (!nandi) {
+		dev_err(&pdev->dev,
+			"failed to allocate NANDi controller data\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	nandi->dev = &pdev->dev;
+
+	err = remap_named_resource(pdev, "emi_nand", &nandi->base);
+	if (err)
+		return ERR_PTR(err);
+
+	err = remap_named_resource(pdev, "emiss", &nandi->emiss);
+	if (err)
+		return ERR_PTR(err);
+
+	nandi->dma = nandi->emiss + EMISS_NAND_DMA;
+	nandi->emisscfg = nandi->emiss + EMISS_CFG;
+
+	irq = platform_get_irq_byname(pdev, "nand_irq");
+	if (irq < 0) {
+		dev_err(&pdev->dev, "failed to find IRQ resource\n");
+		return ERR_PTR(irq);
+	}
+
+	err = devm_request_irq(&pdev->dev, irq, nandi_irq_handler,
+			       IRQF_DISABLED, dev_name(&pdev->dev), nandi);
+	if (err) {
+		dev_err(&pdev->dev, "irq request failed\n");
+		return ERR_PTR(err);
+	}
+
+	nandi->emi_clk = nandi_clk_setup(nandi, "emi");
+	nandi->bch_clk = nandi_clk_setup(nandi, "bch");
+
+	ddata->nandi = nandi;
+
+	return nandi;
+}
+
+static void *stm_bch_dt_get_ddata(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct stm_nand_bch_ddata *ddata;
+	int ecc_strength;
+	int ret;
+
+	ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
+	if (!ddata)
+		return ERR_PTR(-ENOMEM);
+
+	of_property_read_u32(np, "nand-ecc-strength", &ecc_strength);
+	if (ecc_strength == 0)
+		ddata->bch_ecc_cfg = BCH_NO_ECC;
+	else if (ecc_strength == 18)
+		ddata->bch_ecc_cfg = BCH_18BIT_ECC;
+	else if (ecc_strength == 30)
+		ddata->bch_ecc_cfg = BCH_30BIT_ECC;
+	else
+		ddata->bch_ecc_cfg = BCH_ECC_AUTO;
+
+	ret = stm_of_get_nand_banks(&pdev->dev, np, &ddata->bank);
+	if (ret < 0)
+		return ERR_PTR(ret);
+
+	return ddata;
+}
+
+static int stm_nand_bch_probe(struct platform_device *pdev)
+{
+	const char *part_probes[] = { "cmdlinepart", "ofpart", NULL, };
+	struct device_node *np = pdev->dev.of_node;
+	struct mtd_part_parser_data ppdata;
+	struct stm_nand_bch_ddata *ddata;
+	struct stm_nand_bank_data *bank;
+	struct nandi_bbt_info *bbt_info;
+	struct nandi_controller *nandi;
+	struct nandi_info *info;
+	struct nand_chip *chip;
+	struct mtd_info *mtd;
+	int compatible, err;
+
+	if (!np) {
+		dev_err(&pdev->dev, "DT node found\n");
+		return -EINVAL;
+	}
+
+	ddata = stm_bch_dt_get_ddata(pdev);
+	if (IS_ERR(ddata))
+		return PTR_ERR(ddata);
+
+	ppdata.of_node = stm_of_get_partitions_node(np, 0);
+
+	platform_set_drvdata(pdev, ddata);
+
+	nandi = nandi_init_resources(pdev);
+	if (IS_ERR(nandi)) {
+		dev_err(&pdev->dev, "failed to initialise NANDi resources\n");
+		return PTR_ERR(nandi);
+	}
+
+	init_completion(&nandi->seq_completed);
+	init_completion(&nandi->rbn_completed);
+
+	bank = ddata->bank;
+	if (bank)
+		nandi_init_controller(nandi, bank->csn);
+
+	info            = &nandi->info;
+	chip            = &info->chip;
+	bbt_info        = &info->bbt_info;
+	mtd             = &info->mtd;
+	mtd->priv       = chip;
+	mtd->name       = dev_name(&pdev->dev);
+	mtd->dev.parent = &pdev->dev;
+
+	nandi_set_mtd_defaults(nandi, mtd, chip);
+
+	err = nand_scan_ident(mtd, 1, NULL);
+	if (err)
+		return err;
+
+	/*
+	 * Configure timing registers
+	 */
+	if (bank && bank->timing_spec) {
+		dev_info(&pdev->dev, "Using platform timing data\n");
+		nandi_configure_timing_registers(nandi, bank->timing_spec,
+						 bank->timing_relax);
+	} else if (chip->onfi_version) {
+		int mode = fls(onfi_get_async_timing_mode(chip) - 1);
+		const struct nand_sdr_timings *spec =
+			onfi_async_timing_mode_to_sdr_timings(mode);
+
+		/* Modes 4 and 5 (EDO) are not supported on our H/W */
+		if (mode > 3)
+			mode = 3;
+
+		dev_info(&pdev->dev, "Using ONFI Timing Mode %d\n", mode);
+		nandi_configure_timing_registers(nandi,	spec,
+					bank ? bank->timing_relax : 0);
+	} else {
+		dev_warn(&pdev->dev, "No timing data available\n");
+	}
+
+	if (mtd->writesize < NANDI_BCH_SECTOR_SIZE) {
+		dev_err(nandi->dev,
+			"page size incompatible with BCH ECC sector\n");
+		return -EINVAL;
+	}
+
+	/* Derive some working variables */
+	nandi->sectors_per_page = mtd->writesize / NANDI_BCH_SECTOR_SIZE;
+	nandi->blocks_per_device = mtd->size >> chip->phys_erase_shift;
+	nandi->extra_addr = ((chip->chipsize >> chip->page_shift) >
+			     0x10000) ? true : false;
+	mtd->writebufsize = mtd->writesize;
+
+	/* Set ECC mode */
+	if (ddata->bch_ecc_cfg == BCH_ECC_AUTO) {
+		err = bch_set_ecc_auto(nandi, mtd);
+		if (err) {
+			dev_err(nandi->dev, "insufficient OOB for BCH ECC\n");
+			return err;
+		}
+	} else {
+		nandi->bch_ecc_mode = ddata->bch_ecc_cfg;
+	}
+
+	chip->ecc.size = NANDI_BCH_SECTOR_SIZE;
+	chip->ecc.bytes = mtd->oobsize;
+	chip->ecc.strength = bch_ecc_strength[nandi->bch_ecc_mode];
+
+	info->ecclayout.eccbytes =
+		nandi->sectors_per_page * bch_ecc_sizes[nandi->bch_ecc_mode];
+
+	compatible = bch_check_compatibility(nandi, mtd, chip);
+	if (!compatible) {
+		dev_err(nandi->dev,
+			"NAND device incompatible with NANDi/BCH Controller\n");
+		return -EINVAL;
+	}
+
+	/* Tune BCH programs according to device found and ECC mode */
+	bch_configure_progs(nandi);
+
+	err = nandi_init_working_buffers(nandi, bbt_info, mtd);
+	if (err)
+		return err;
+
+	err = nand_scan_tail(mtd);
+	if (err)
+		return err;
+
+#ifdef CONFIG_MTD_NAND_STM_BCH_BBT
+	nandi_dump_bad_blocks(nandi);
+#endif
+	/* Add partitions */
+	return mtd_device_parse_register(mtd, part_probes, &ppdata,
+					bank->partitions, bank->nr_partitions);
+}
+
+static int stm_nand_bch_remove(struct platform_device *pdev)
+{
+	struct stm_nand_bch_ddata *ddata = platform_get_drvdata(pdev);
+	struct nandi_controller *nandi = ddata->nandi;
+
+	nand_release(&nandi->info.mtd);
+
+	nandi_clk_disable(nandi);
+
+	return 0;
+}
+
+static int stm_nand_bch_suspend(struct device *dev)
+{
+	struct stm_nand_bch_ddata *ddata = dev_get_drvdata(dev);
+	struct nandi_controller *nandi = ddata->nandi;
+
+	nandi_clk_disable(nandi);
+
+	return 0;
+}
+static int stm_nand_bch_resume(struct device *dev)
+{
+	struct stm_nand_bch_ddata *ddata = dev_get_drvdata(dev);
+	struct nandi_controller *nandi = ddata->nandi;
+
+	nandi_clk_enable(nandi);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(stm_nand_bch_pm_ops,
+			 stm_nand_bch_suspend,
+			 stm_nand_bch_resume);
+
+static struct of_device_id nand_bch_match[] = {
+	{ .compatible = "st,nand-bch", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, nand_bch_match);
+
+static struct platform_driver stm_nand_bch_driver = {
+	.probe	= stm_nand_bch_probe ,
+	.remove	= stm_nand_bch_remove,
+	.driver	= {
+		.name	= "stm-nand-bch",
+		.owner	= THIS_MODULE,
+		.of_match_table = of_match_ptr(nand_bch_match),
+		.pm	= &stm_nand_bch_pm_ops,
+	},
+};
+module_platform_driver(stm_nand_bch_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Angus Clark");
+MODULE_DESCRIPTION("STM NAND BCH driver");
diff --git a/drivers/mtd/nand/stm_nand_dt.c b/drivers/mtd/nand/stm_nand_dt.c
new file mode 100644
index 0000000..4f59873
--- /dev/null
+++ b/drivers/mtd/nand/stm_nand_dt.c
@@ -0,0 +1,109 @@
+/*
+ * drivers/mtd/nand/stm_nand_dt.c
+ *
+ * Support for NANDi BCH Controller Device Tree component
+ *
+ * Copyright (c) 2014 STMicroelectronics Limited
+ * Author: Author: Srinivas Kandagatla <srinivas.kandagatla@st.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/byteorder/generic.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/stm_nand.h>
+
+#include "stm_nand_regs.h"
+
+/**
+* stm_of_get_partitions_node - get partitions node from stm-nand type devices.
+*
+* @dev		device pointer to use for devm allocations.
+* @np		device node of the driver.
+* @bank_nr	which bank number to use to get partitions.
+*
+* Returns a node pointer if found, with refcount incremented, use
+* of_node_put() on it when done.
+*
+*/
+struct device_node *stm_of_get_partitions_node(struct device_node *np,
+					       int bank_nr)
+{
+	struct device_node *banknp, *partsnp = NULL;
+	char name[10];
+
+	sprintf(name, "bank%d", bank_nr);
+	banknp = of_get_child_by_name(np, name);
+	if (banknp)
+		return NULL;
+
+	partsnp = of_get_child_by_name(banknp, "partitions");
+	of_node_put(banknp);
+
+	return partsnp;
+}
+EXPORT_SYMBOL(stm_of_get_partitions_node);
+
+/**
+ * stm_of_get_nand_banks - Get nand banks info from a given device node.
+ *
+ * @dev			device pointer to use for devm allocations.
+ * @np			device node of the driver.
+ * @banksptr		double pointer to banks which is allocated
+ *			and filled with bank data.
+ *
+ * Returns a count of banks found in the given device node.
+ *
+ */
+int stm_of_get_nand_banks(struct device *dev, struct device_node *np,
+			  struct stm_nand_bank_data **banksptr)
+{
+	struct stm_nand_bank_data *banks;
+	struct device_node *banknp;
+	int nr_banks = 0;
+
+	if (!np)
+		return -ENODEV;
+
+	nr_banks = of_get_child_count(np);
+	if (!nr_banks) {
+		dev_err(dev, "No NAND banks specified in DT: %s\n",
+		np->full_name);
+		return -EINVAL;
+	}
+
+	*banksptr = devm_kzalloc(dev, sizeof(*banks) * nr_banks, GFP_KERNEL);
+	banks = *banksptr;
+	banknp = NULL;
+
+	for_each_child_of_node(np, banknp) {
+		int bank = 0;
+
+		of_property_read_u32(banknp, "st,nand-csn", &banks[bank].csn);
+
+		if (of_get_nand_bus_width(banknp) == 16)
+			banks[bank].options |= NAND_BUSWIDTH_16;
+		if (of_get_nand_on_flash_bbt(banknp))
+			banks[bank].bbt_options |= NAND_BBT_USE_FLASH;
+
+		banks[bank].nr_partitions = 0;
+		banks[bank].partitions = NULL;
+
+		of_property_read_u32(banknp, "st,nand-timing-relax",
+				     &banks[bank].timing_relax);
+		bank++;
+	}
+
+	return nr_banks;
+}
+EXPORT_SYMBOL(stm_of_get_nand_banks);
diff --git a/drivers/mtd/nand/stm_nand_dt.h b/drivers/mtd/nand/stm_nand_dt.h
new file mode 100644
index 0000000..53e1321
--- /dev/null
+++ b/drivers/mtd/nand/stm_nand_dt.h
@@ -0,0 +1,24 @@
+/*
+ * drivers/mtd/nand/stm_nand_dt.h
+ *
+ * Support for NANDi BCH Controller Device Tree component
+ *
+ * Copyright (c) 2014 STMicroelectronics Limited
+ * Author: Author: Srinivas Kandagatla <srinivas.kandagatla@st.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef STM_NAND_DT_H
+#define STM_NAND_DT_H
+
+struct device_node *stm_of_get_partitions_node(struct device_node *np,
+					       int bank_nr);
+
+int stm_of_get_nand_banks(struct device *dev, struct device_node *np,
+			  struct stm_nand_bank_data **banksp);
+
+#endif /* STM_NAND_DT_H */
diff --git a/include/linux/mtd/stm_nand.h b/include/linux/mtd/stm_nand.h
new file mode 100644
index 0000000..c3c805f
--- /dev/null
+++ b/include/linux/mtd/stm_nand.h
@@ -0,0 +1,147 @@
+/*
+ * include/linux/mtd/stm_mtd.h
+ *
+ * Support for STMicroelectronics NAND Controllers
+ *
+ * Copyright (c) 2014 STMicroelectronics Limited
+ * Author: Angus Clark <Angus.Clark@st.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef __LINUX_STM_NAND_H
+#define __LINUX_STM_NAND_H
+
+#include <linux/io.h>
+#include <linux/mtd/nand.h>
+
+/* ECC Modes */
+enum stm_nand_bch_ecc_config {
+	BCH_18BIT_ECC = 0,
+	BCH_30BIT_ECC,
+	BCH_NO_ECC,
+	BCH_ECC_RSRV,
+	BCH_ECC_AUTO,
+};
+
+/* Bad Block Table (BBT) */
+struct nandi_bbt_info {
+	uint32_t	bbt_size;		/* Size of bad-block table */
+	uint32_t	bbt_vers[2];		/* Version (Primary/Mirror) */
+	uint32_t	bbt_block[2];		/* Block No. (Primary/Mirror) */
+	uint8_t		*bbt;			/* Table data */
+};
+
+/* Collection of MTD/NAND device information */
+struct nandi_info {
+	struct mtd_info		mtd;		/* MTD info */
+	struct nand_chip	chip;		/* NAND chip info */
+
+	struct nand_ecclayout	ecclayout;	/* MTD ECC layout */
+	struct nandi_bbt_info	bbt_info;	/* Bad Block Table */
+	int			nr_parts;	/* Number of MTD partitions */
+	struct	mtd_partition	*parts;		/* MTD partitions */
+};
+
+/* NANDi Controller (Hamming/BCH) */
+struct nandi_controller {
+	void __iomem		*base;		/* Controller base*/
+	void __iomem		*emiss;		/* EMISS control base */
+	void __iomem		*emisscfg;	/* EMISS config base */
+	void __iomem		*dma;		/* DMA control base */
+
+	struct clk		*bch_clk;
+	struct clk		*emi_clk;
+						/* IRQ-triggered Completions: */
+	struct completion	seq_completed;	/*   SEQ Over */
+	struct completion	rbn_completed;	/*   RBn */
+
+	struct device		*dev;
+
+	int			bch_ecc_mode;	/* ECC mode */
+	bool			extra_addr;	/* Extra address cycle */
+
+	uint32_t		blocks_per_device;
+	uint32_t		sectors_per_page;
+
+	uint8_t			*buf;		/* Some buffers to use */
+	uint8_t			*page_buf;
+	uint8_t			*oob_buf;
+	uint32_t		*buf_list;
+
+	int			cached_page;	/* page number of page in */
+						/* 'page_buf'             */
+
+	struct nandi_info	info;		/* NAND device info */
+};
+
+/*
+ * Board-level specification relating to a 'bank' of NAND Flash
+ */
+struct stm_nand_bank_data {
+	int			csn;
+	int			nr_partitions;
+	struct mtd_partition	*partitions;
+	unsigned int		options;
+	unsigned int		bbt_options;
+
+	struct nand_sdr_timings *timing_spec;
+
+	/*
+	 * No. of IP clk cycles by which to 'relax' the timing configuration.
+	 * Required on some boards to to accommodate board-level limitations.
+	 * Used in conjunction with 'nand_sdr_timings' and ONFI configuration.
+	 */
+	int			timing_relax;
+};
+
+struct stm_nand_bch_ddata {
+	struct nandi_controller *nandi;
+	struct stm_nand_bank_data *bank;
+	enum stm_nand_bch_ecc_config bch_ecc_cfg;
+};
+
+enum nandi_controllers {
+	STM_NANDI_UNCONFIGURED,
+	STM_NANDI_HAMMING,
+	STM_NANDI_BCH
+};
+
+extern int flex_read_raw(struct nandi_controller *nandi,
+			 uint32_t page_addr,
+			 uint32_t col_addr,
+			 uint8_t *buf, uint32_t len);
+extern uint8_t bch_write_page(struct nandi_controller *nandi,
+			      loff_t offs, const uint8_t *buf);
+extern uint8_t bch_erase_block(struct nandi_controller *nandi,
+			       loff_t offs);
+extern int bch_read_page(struct nandi_controller *nandi,
+			 loff_t offs, uint8_t *buf);
+
+#define EMISS_NAND_CONFIG_HAMMING_NOT_BCH 	(0x1 << 6)
+
+static inline void emiss_nandi_select(struct nandi_controller *nandi,
+				      enum nandi_controllers controller)
+{
+	unsigned v;
+
+	v = readl(nandi->emisscfg);
+
+	if (controller == STM_NANDI_HAMMING) {
+		if (v & EMISS_NAND_CONFIG_HAMMING_NOT_BCH)
+			return;
+		v |= EMISS_NAND_CONFIG_HAMMING_NOT_BCH;
+	} else {
+		if (!(v & EMISS_NAND_CONFIG_HAMMING_NOT_BCH))
+			return;
+		v &= ~EMISS_NAND_CONFIG_HAMMING_NOT_BCH;
+	}
+
+	writel(v, nandi->emisscfg);
+	readl(nandi->emisscfg);
+}
+
+#endif /* __LINUX_STM_NAND_H */
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH 8/8] mtd: nand: stm_nand_bch: provide ST's implementation of Back Block Table
  2014-08-13  9:11 ` Lee Jones
  (?)
@ 2014-08-13  9:12   ` Lee Jones
  -1 siblings, 0 replies; 51+ messages in thread
From: Lee Jones @ 2014-08-13  9:12 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: lee.jones, kernel, computersforpeace, pekon, linux-mtd

This is the BBT format ST use internally.  It has to be used on boards
which were flashed with or actively use ST's tooling and boards which
are booted using ST's bootloaders.

Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
 drivers/mtd/nand/Kconfig         |   8 +
 drivers/mtd/nand/Makefile        |   1 +
 drivers/mtd/nand/stm_nand_bbt.c  | 601 +++++++++++++++++++++++++++++++++++++++
 include/linux/mtd/stm_nand_bbt.h |  17 ++
 4 files changed, 627 insertions(+)
 create mode 100644 drivers/mtd/nand/stm_nand_bbt.c
 create mode 100644 include/linux/mtd/stm_nand_bbt.h

diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 2738eec..5c04ec1 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -520,4 +520,12 @@ config MTD_NAND_STM_BCH
 	help
 	  Adds support for the STMicroelectronics NANDi BCH Controller.
 
+config MTD_NAND_STM_BCH_BBT
+	tristate "STMicroelectronics: NANDi BCH Controller Bad Block Table support"
+	default MTD_NAND_STM_BCH
+	help
+	  Adds support for the STMicroelectronics Bad Block Table support.
+	  If you are using a device which has has already been initialised
+	  by ST or using their tooling/bootloaders, leave this enabled.
+
 endif # MTD_NAND
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 60f374b..5ef0462 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -47,6 +47,7 @@ obj-$(CONFIG_MTD_NAND_MPC5121_NFC)	+= mpc5121_nfc.o
 obj-$(CONFIG_MTD_NAND_RICOH)		+= r852.o
 obj-$(CONFIG_MTD_NAND_JZ4740)		+= jz4740_nand.o
 obj-$(CONFIG_MTD_NAND_STM_BCH)		+= stm_nand_bch.o stm_nand_dt.o
+obj-$(CONFIG_MTD_NAND_STM_BCH_BBT)	+= stm_nand_bbt.o
 obj-$(CONFIG_MTD_NAND_GPMI_NAND)	+= gpmi-nand/
 obj-$(CONFIG_MTD_NAND_XWAY)		+= xway_nand.o
 obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH)	+= bcm47xxnflash/
diff --git a/drivers/mtd/nand/stm_nand_bbt.c b/drivers/mtd/nand/stm_nand_bbt.c
new file mode 100644
index 0000000..242bffd
--- /dev/null
+++ b/drivers/mtd/nand/stm_nand_bbt.c
@@ -0,0 +1,601 @@
+/*
+ * Support for STMicroelectronics Bad Block Table (BBT)
+ *
+ * Copyright (c) 2014 STMicroelectronics Limited
+ *
+ * Authors: Angus Clark <Angus.Clark@st.com>
+ *	    Lee Jones <lee.jones@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/mtd/stm_nand.h>
+#include <linux/mtd/stm_nand_bbt.h>
+#include <generated/utsrelease.h>
+
+/*
+ * Inband Bad Block Table (IBBT)
+ */
+#define NAND_IBBT_NBLOCKS	4
+#define NAND_IBBT_SIGLEN	4
+#define NAND_IBBT_PRIMARY	0
+#define NAND_IBBT_MIRROR	1
+#define NAND_IBBT_SCHEMA	0x10
+#define NAND_IBBT_BCH_SCHEMA	0x10
+
+static uint8_t ibbt_sigs[2][NAND_IBBT_SIGLEN] = {
+	{'B', 'b', 't', '0'},
+	{'1', 't', 'b', 'B'},
+};
+
+static char *bbt_strs[] = {
+	"primary",
+	"mirror",
+};
+
+/* IBBT header */
+struct nand_ibbt_header {
+	uint8_t	signature[4];		/* "Bbt0" or "1tbB" signature */
+	uint8_t version;		/* BBT version ("age") */
+	uint8_t reserved[3];		/* padding */
+	uint8_t schema[4];		/* "base" schema (x4) */
+} __packed;
+
+/* Extend IBBT header with some stm-nand-bch niceties */
+struct nand_ibbt_bch_header {
+	struct nand_ibbt_header base;
+	uint8_t schema[4];		/* "private" schema (x4) */
+	uint8_t ecc_size[4];		/* ECC bytes (0, 32, 54) (x4) */
+	char	author[64];		/* Arbitrary string for S/W to use */
+} __packed;
+
+/*
+ * Bad Block Tables/Bad Block Markers
+ */
+#define BBT_MARK_BAD_FACTORY	0x0
+#define BBT_MARK_BAD_WEAR	0x1
+#define BBT_MARK_GOOD		0x3
+
+static void bbt_set_block_mark(uint8_t *bbt, uint32_t block, uint8_t mark)
+{
+	unsigned int byte = block >> 2;
+	unsigned int shift = (block & 0x3) << 1;
+
+	bbt[byte] &= ~(0x3 << shift);
+	bbt[byte] |= ((mark & 0x3) << shift);
+}
+
+static uint8_t bbt_get_block_mark(uint8_t *bbt, uint32_t block)
+{
+	unsigned int byte = block >> 2;
+	unsigned int shift = (block & 0x3) << 1;
+
+	return (bbt[byte] >> shift) & 0x3;
+}
+
+static int bbt_is_block_bad(uint8_t *bbt, uint32_t block)
+{
+	return bbt_get_block_mark(bbt, block) == BBT_MARK_GOOD ? 0 : 1;
+}
+
+/* Scan page for BBM(s), according to specified BBT options */
+static int nandi_scan_bad_block_markers_page(struct nandi_controller *nandi,
+					     uint32_t page)
+{
+	struct mtd_info *mtd = &nandi->info.mtd;
+	struct nand_chip *chip = mtd->priv;
+	uint8_t *oob_buf = nandi->oob_buf;
+	int i, e;
+
+	/* Read the OOB area */
+	flex_read_raw(nandi, page, mtd->writesize, oob_buf, mtd->oobsize);
+
+	if (oob_buf[chip->badblockpos] == 0xff)
+		return 0;
+
+	/* Tolerate 'alien' Hamming Boot Mode ECC */
+	e = 0;
+	for (i = 0; i < mtd->oobsize; i += 16)
+		e += hweight8(oob_buf[i + 3] ^ 'B');
+	if (e <= 1)
+		return 0;
+
+	/* Tolerate 'alien' Hamming AFM ECC */
+	e = 0;
+	for (i = 0; i < mtd->oobsize; i += 16) {
+		e += hweight8(oob_buf[i + 3] ^ 'A');
+		e += hweight8(oob_buf[i + 4] ^ 'F');
+		e += hweight8(oob_buf[i + 5] ^ 'M');
+		if (e <= 1)
+			return 0;
+	}
+
+	return 1;
+}
+
+/* Scan block for BBM(s), according to specified BBT options */
+static int nandi_scan_bad_block_markers_block(struct nandi_controller *nandi,
+					      uint32_t block)
+
+{
+	struct mtd_info *mtd = &nandi->info.mtd;
+	struct nand_chip *chip = mtd->priv;
+	uint32_t pages_per_block = mtd->erasesize >> chip->page_shift;
+	uint32_t page = block << (chip->phys_erase_shift - chip->page_shift);
+
+	if (nandi_scan_bad_block_markers_page(nandi, page))
+		return 1;
+
+	if ((chip->bbt_options & NAND_BBT_SCAN2NDPAGE) &&
+	    nandi_scan_bad_block_markers_page(nandi, page + 1))
+		return 1;
+
+	if ((chip->bbt_options & NAND_BBT_SCANLASTPAGE) &&
+	    nandi_scan_bad_block_markers_page(nandi,
+					      page + pages_per_block - 1))
+		return 1;
+
+	return 0;
+}
+
+/* Scan for BBMs and build memory-resident BBT */
+static int nandi_scan_build_bbt(struct nandi_controller *nandi,
+				struct nandi_bbt_info *bbt_info)
+{
+	struct mtd_info *mtd = &nandi->info.mtd;
+	struct nand_chip *chip = mtd->priv;
+	uint32_t page_size = mtd->writesize;
+	uint8_t *bbt = bbt_info->bbt;
+	uint32_t block;
+
+	dev_dbg(nandi->dev,
+		"scan device for bad-block markers [bbt options = 0x%02x]\n",
+		chip->bbt_options);
+
+	memset(bbt, 0xff, page_size);
+	bbt_info->bbt_vers[0] = 0;
+	bbt_info->bbt_vers[1] = 0;
+	bbt_info->bbt_block[0] = nandi->blocks_per_device - 1;
+	bbt_info->bbt_block[1] = nandi->blocks_per_device - 2;
+
+	for (block = 0; block < nandi->blocks_per_device; block++)
+		if (nandi_scan_bad_block_markers_block(nandi, block))
+			bbt_set_block_mark(bbt, block, BBT_MARK_BAD_FACTORY);
+
+	return 0;
+}
+
+/* Populate IBBT BCH Header */
+static void bch_fill_ibbt_header(struct nandi_controller *nandi,
+				 struct nand_ibbt_bch_header *ibbt_header,
+				 int bak, uint8_t vers)
+{
+	const char author[] = "STLinux " UTS_RELEASE " (stm-nand-bch)";
+
+	memcpy(ibbt_header->base.signature, ibbt_sigs[bak], NAND_IBBT_SIGLEN);
+	ibbt_header->base.version = vers;
+	memset(ibbt_header->base.schema, NAND_IBBT_SCHEMA, 4);
+
+	memset(ibbt_header->schema, NAND_IBBT_SCHEMA, 4);
+	memset(ibbt_header->ecc_size, bch_ecc_sizes[nandi->bch_ecc_mode], 4);
+	memcpy(ibbt_header->author, author, sizeof(author));
+}
+
+/* Write IBBT to Flash */
+static int bch_write_bbt_data(struct nandi_controller *nandi,
+			      struct nandi_bbt_info *bbt_info,
+			      uint32_t block, int bak, uint8_t vers)
+{
+	struct nand_ibbt_bch_header *ibbt_header =
+		(struct nand_ibbt_bch_header *)nandi->page_buf;
+	struct nand_chip *chip = &nandi->info.chip;
+	struct mtd_info *mtd = &nandi->info.mtd;
+	uint32_t page_size = mtd->writesize;
+	uint32_t block_size = mtd->erasesize;
+	loff_t offs;
+	size_t retlen;
+	int ret;
+
+	nandi->cached_page = -1;
+
+	/* Write BBT contents to first page of block */
+	offs = (loff_t)block << chip->phys_erase_shift;
+	ret = mtd_write(mtd, offs, page_size, &retlen, bbt_info->bbt);
+	if (ret)
+		return ret;
+
+	/* Update IBBT header and write to last page of block */
+	memset(ibbt_header, 0xff, mtd->writesize);
+	bch_fill_ibbt_header(nandi, ibbt_header, bak, vers);
+	offs += block_size - page_size;
+	ret = mtd_write(mtd, offs, sizeof(*ibbt_header), &retlen,
+			     (uint8_t *)ibbt_header);
+	return ret;
+}
+
+/*
+ * Update Flash-resident BBT:
+ *   erase/search suitable block, and write table data to Flash
+ */
+static int bch_update_bbt(struct nandi_controller *nandi,
+			 struct nandi_bbt_info *bbt_info,
+			 int bak, uint8_t vers)
+{
+	struct nand_chip *chip = &nandi->info.chip;
+	loff_t offs;
+	uint32_t block;
+	uint32_t block_lower;
+	uint32_t block_other;
+
+	block_other = bbt_info->bbt_block[(bak+1)%2];
+	block_lower = nandi->blocks_per_device - NAND_IBBT_NBLOCKS;
+
+	for (block = bbt_info->bbt_block[bak]; block >= block_lower;  block--) {
+		offs = (loff_t)block << chip->phys_erase_shift;
+
+		/* Skip if block used by other table */
+		if (block == block_other)
+			continue;
+
+		/* Skip if block is marked bad */
+		if (bbt_is_block_bad(bbt_info->bbt, block))
+			continue;
+
+		/* Erase block, mark bad and skip on failure */
+		if (bch_erase_block(nandi, offs) & NAND_STATUS_FAIL) {
+			dev_info(nandi->dev,
+				 "failed to erase block [%u:0x%012llx] while updating BBT\n",
+				 block, offs);
+			vers++;
+			bbt_set_block_mark(bbt_info->bbt, block,
+					   BBT_MARK_BAD_WEAR);
+			continue;
+		}
+
+		/* Write BBT, mark bad and skip on failure */
+		if (bch_write_bbt_data(nandi, bbt_info, block, bak, vers)) {
+			dev_info(nandi->dev,
+				 "failed to write BBT to block [%u:0x%012llx]\n",
+				 block, offs);
+			vers++;
+			bbt_set_block_mark(bbt_info->bbt, block,
+					   BBT_MARK_BAD_WEAR);
+			continue;
+		}
+
+		/* Success */
+		bbt_info->bbt_block[bak] = block;
+		bbt_info->bbt_vers[bak] = vers;
+		break;
+	}
+
+	/* No space in BBT area */
+	if (block < block_lower) {
+		dev_err(nandi->dev, "no space left in BBT area\n");
+		dev_err(nandi->dev, "failed to update %s BBT\n", bbt_strs[bak]);
+		return -ENOSPC;
+	}
+
+	dev_info(nandi->dev, "wrote BBT [%s:%u] at 0x%012llx [%u]\n",
+		 bbt_strs[bak], vers, offs, block);
+
+	return 0;
+}
+
+#define NAND_IBBT_UPDATE_PRIMARY	0x1
+#define NAND_IBBT_UPDATE_MIRROR		0x2
+#define NAND_IBBT_UPDATE_BOTH		(NAND_IBBT_UPDATE_PRIMARY | \
+					 NAND_IBBT_UPDATE_MIRROR)
+static char *bbt_update_strs[] = {
+	"",
+	"primary",
+	"mirror",
+	"both",
+};
+
+/*
+ * Update Flash-resident BBT(s):
+ *   incrementing 'vers' number if required, and ensuring Primary
+ *   and Mirror are kept in sync
+ */
+static int bch_update_bbts(struct nandi_controller *nandi,
+			   struct nandi_bbt_info *bbt_info,
+			   unsigned int update, uint8_t vers)
+{
+	int err;
+
+	dev_info(nandi->dev, "updating %s BBT(s)\n", bbt_update_strs[update]);
+
+	do {
+		/* Update Primary if specified */
+		if (update & NAND_IBBT_UPDATE_PRIMARY) {
+			err = bch_update_bbt(nandi, bbt_info, NAND_IBBT_PRIMARY,
+					     vers);
+			/* Bail out on error (e.g. no space left in BBT area) */
+			if (err)
+				return err;
+
+			/*
+			 * If update resulted in a new BBT version
+			 * (e.g. Erase/Write fail on BBT block) update version
+			 * here, and force update of other table.
+			 */
+			if (bbt_info->bbt_vers[NAND_IBBT_PRIMARY] != vers) {
+				vers = bbt_info->bbt_vers[NAND_IBBT_PRIMARY];
+				update = NAND_IBBT_UPDATE_MIRROR;
+			}
+		}
+
+		/* Update Mirror if specified */
+		if (update & NAND_IBBT_UPDATE_MIRROR) {
+			err = bch_update_bbt(nandi, bbt_info, NAND_IBBT_MIRROR,
+					     vers);
+			/* Bail out on error (e.g. no space left in BBT area) */
+			if (err)
+				return err;
+
+			/*
+			 * If update resulted in a new BBT version
+			 * (e.g. Erase/Write fail on BBT block) update version
+			 * here, and force update of other table.
+			 */
+			if (bbt_info->bbt_vers[NAND_IBBT_MIRROR] != vers) {
+				vers = bbt_info->bbt_vers[NAND_IBBT_MIRROR];
+				update = NAND_IBBT_UPDATE_PRIMARY;
+			}
+		}
+
+		/* Continue, until Primary and Mirror versions are in sync */
+	} while (bbt_info->bbt_vers[NAND_IBBT_PRIMARY] !=
+		 bbt_info->bbt_vers[NAND_IBBT_MIRROR]);
+
+	return 0;
+}
+
+/* Scan block for IBBT signature */
+static int bch_find_ibbt_sig(struct nandi_controller *nandi,
+			     uint32_t block, int *bak, uint8_t *vers,
+			     char *author)
+{
+	struct nand_chip *chip = &nandi->info.chip;
+	struct mtd_info *mtd = &nandi->info.mtd;
+	struct nand_ibbt_bch_header *ibbt_header;
+	loff_t offs;
+	uint8_t *buf = nandi->page_buf;
+	int match_sig;
+	unsigned int b;
+	unsigned int i;
+	size_t retlen;
+	int ret;
+
+	nandi->cached_page = -1;
+
+	/* Load last page of block */
+	offs = (loff_t)block << chip->phys_erase_shift;
+	offs += mtd->erasesize - mtd->writesize;
+	ret = mtd_read(mtd, offs, sizeof(*ibbt_header), &retlen, buf);
+	if (ret < 0) {
+		dev_info(nandi->dev,
+			 "Uncorrectable ECC error while scanning BBT signature at block %u [0x%012llx]\n",
+			 block, offs);
+		return 0;
+	}
+	ibbt_header = (struct nand_ibbt_bch_header *)buf;
+
+	/* Test IBBT signature */
+	match_sig = 0;
+	for (b = 0; b < 2 && !match_sig; b++) {
+		match_sig = 1;
+		for (i = 0; i < NAND_IBBT_SIGLEN; i++) {
+			if (ibbt_header->base.signature[i] != ibbt_sigs[b][i]) {
+				match_sig = 0;
+				break;
+			}
+		}
+
+	}
+
+	if (!match_sig)
+		return 0; /* Failed to match IBBT signature */
+
+	/* Test IBBT schema */
+	for (i = 0; i < 4; i++)
+		if (ibbt_header->base.schema[i] != NAND_IBBT_SCHEMA)
+			return 0;
+
+	/* Test IBBT BCH schema */
+	for (i = 0; i < 4; i++)
+		if (ibbt_header->schema[i] != NAND_IBBT_BCH_SCHEMA)
+			return 0;
+
+	/* We have a match */
+	*vers = ibbt_header->base.version;
+	*bak = b - 1;
+	strncpy(author, ibbt_header->author, 64);
+
+	return 1;
+}
+
+/* Search for and load Flash-resident BBT, updating Primary/Mirror if req'd */
+static int bch_load_bbt(struct nandi_controller *nandi,
+			struct nandi_bbt_info *bbt_info)
+{
+	struct nand_chip *chip = &nandi->info.chip;
+	struct mtd_info *mtd = &nandi->info.mtd;
+	uint32_t page_size = mtd->writesize;
+	unsigned int update = 0;
+	uint32_t block;
+	loff_t offs;
+	uint8_t vers;
+	char author[64];
+	size_t retlen;
+	int ret;
+	int bak;
+
+	dev_dbg(nandi->dev, "looking for Flash-resident BBTs\n");
+
+	bbt_info->bbt_block[0] = 0;
+	bbt_info->bbt_block[1] = 0;
+	bbt_info->bbt_vers[0] = 0;
+	bbt_info->bbt_vers[1] = 0;
+
+	/* Look for IBBT signatures */
+	for (block = nandi->blocks_per_device - NAND_IBBT_NBLOCKS;
+	     block < nandi->blocks_per_device;
+	     block++) {
+		offs = (loff_t)block << chip->phys_erase_shift;
+
+		if (bch_find_ibbt_sig(nandi, block, &bak, &vers, author)) {
+			dev_dbg(nandi->dev,
+				"found BBT [%s:%u] at 0x%012llx [%u] (%s)\n",
+				bbt_strs[bak], vers, offs, block,
+				author);
+
+			if (bbt_info->bbt_block[bak] == 0 ||
+			    ((int8_t)(bbt_info->bbt_vers[bak] - vers)) < 0) {
+				bbt_info->bbt_block[bak] = block;
+				bbt_info->bbt_vers[bak] = vers;
+			}
+		}
+	}
+
+	/* What have we found? */
+	if (bbt_info->bbt_block[0] == 0 && bbt_info->bbt_block[1] == 0) {
+		/* no primary, no mirror: return error */
+		return 1;
+	} else if (bbt_info->bbt_block[0] == 0) {
+		/* no primary: use mirror, update primary */
+		bak = 1;
+		update = NAND_IBBT_UPDATE_PRIMARY;
+		bbt_info->bbt_block[0] = nandi->blocks_per_device - 1;
+	} else if (bbt_info->bbt_block[1] == 0) {
+		/* no mirror: use primary, update mirror */
+		bak = 0;
+		update = NAND_IBBT_UPDATE_MIRROR;
+		bbt_info->bbt_block[1] = nandi->blocks_per_device - 1;
+	} else if (bbt_info->bbt_vers[0] == bbt_info->bbt_vers[1]) {
+		/* primary == mirror: use primary, no update required */
+		bak = 0;
+	} else if ((int8_t)(bbt_info->bbt_vers[1] -
+			    bbt_info->bbt_vers[0]) < 0) {
+		/* primary > mirror: use primary, update mirror */
+		bak = 0;
+		update = NAND_IBBT_UPDATE_MIRROR;
+	} else {
+		/* mirror > primary: use mirror, update primary */
+		bak = 1;
+		update = NAND_IBBT_UPDATE_PRIMARY;
+	}
+
+	vers = bbt_info->bbt_vers[bak];
+	block = bbt_info->bbt_block[bak];
+	offs = (loff_t)block << chip->phys_erase_shift;
+	dev_info(nandi->dev, "using BBT [%s:%u] at 0x%012llx [%u]\n",
+		 bbt_strs[bak], vers, offs, block);
+
+	/* Read BBT data */
+	ret = mtd_read(mtd, offs, page_size, &retlen, bbt_info->bbt);
+	if (ret < 0) {
+		dev_err(nandi->dev,
+			"error while reading BBT %s:%u] at 0x%012llx [%u]\n",
+			bbt_strs[bak], vers, offs, block);
+		return 1;
+	}
+
+	/* Update other BBT if required */
+	if (update)
+		bch_update_bbts(nandi, bbt_info, update, vers);
+
+	return 0;
+}
+
+int bch_scan_bbt(struct mtd_info *mtd)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct nandi_controller *nandi = chip->priv;
+	struct nandi_bbt_info *bbt_info = &nandi->info.bbt_info;
+	int err;
+
+	/* Load Flash-resident BBT */
+	err = bch_load_bbt(nandi, bbt_info);
+	if (err) {
+		dev_warn(nandi->dev,
+			"failed to find BBTs:"
+			"  scanning device for bad-block markers\n");
+
+		/* Scan, build, and write BBT */
+		nandi_scan_build_bbt(nandi, bbt_info);
+		err = bch_update_bbts(nandi, bbt_info, NAND_IBBT_UPDATE_BOTH,
+				      bbt_info->bbt_vers[0] + 1);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+int bch_block_isbad(struct mtd_info *mtd, loff_t offs, int getchip)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct nandi_controller *nandi = chip->priv;
+
+	uint32_t block;
+
+	/* Check for invalid offset */
+	if (offs > mtd->size)
+		return -EINVAL;
+
+	block = offs >> chip->phys_erase_shift;
+
+	/* Protect blocks reserved for BBTs */
+	if (block >= (nandi->blocks_per_device - NAND_IBBT_NBLOCKS))
+		return 1;
+
+	return bbt_is_block_bad(nandi->info.bbt_info.bbt, block);
+}
+
+int bch_block_markbad(struct mtd_info *mtd, loff_t offs)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct nandi_controller *nandi = chip->priv;
+
+	uint32_t block;
+	int ret;
+
+	/* Mark bad */
+	block = offs >> chip->phys_erase_shift;
+	bbt_set_block_mark(nandi->info.bbt_info.bbt, block, BBT_MARK_BAD_WEAR);
+
+	/* Update BBTs, incrementing bbt_vers */
+	ret = bch_update_bbts(nandi, &nandi->info.bbt_info,
+			      NAND_IBBT_UPDATE_BOTH,
+			      nandi->info.bbt_info.bbt_vers[0] + 1);
+
+	return ret;
+}
+
+void nandi_dump_bad_blocks(struct nandi_controller *nandi)
+{
+	struct nand_chip *chip = &nandi->info.chip;
+	int bad_count = 0;
+	uint32_t block;
+	uint8_t *bbt = nandi->info.bbt_info.bbt;
+	uint8_t mark;
+
+	pr_info("BBT:\n");
+	for (block = 0; block < nandi->blocks_per_device; block++) {
+		mark = bbt_get_block_mark(bbt, block);
+		if (mark != BBT_MARK_GOOD) {
+			pr_info("\t\tBlock 0x%08x [%05u] marked bad [%s]\n",
+				block << chip->phys_erase_shift, block,
+				(mark == BBT_MARK_BAD_FACTORY) ?
+				"Factory" : "Wear");
+			bad_count++;
+		}
+	}
+	if (bad_count == 0)
+		pr_info("\t\tNo bad blocks listed in BBT\n");
+}
+EXPORT_SYMBOL(nandi_dump_bad_blocks);
diff --git a/include/linux/mtd/stm_nand_bbt.h b/include/linux/mtd/stm_nand_bbt.h
new file mode 100644
index 0000000..70d3c17
--- /dev/null
+++ b/include/linux/mtd/stm_nand_bbt.h
@@ -0,0 +1,17 @@
+#include <linux/mtd/stm_nand.h>
+
+/* BCH ECC sizes */
+static int bch_ecc_sizes[] = {
+	[BCH_18BIT_ECC] = 32,
+	[BCH_30BIT_ECC] = 54,
+	[BCH_NO_ECC] = 0,
+};
+
+#if defined(MTD_NAND_STM_BBT)
+extern void nandi_dump_bad_blocks(struct nandi_controller *nandi);
+extern int bch_scan_bbt(struct mtd_info *mtd);
+extern int bch_block_isbad(struct mtd_info *mtd, loff_t offs, int getchip);
+extern int bch_block_markbad(struct mtd_info *mtd, loff_t offs);
+#else
+inline void nandi_dump_bad_blocks(struct nandi_controller *nandi) {}
+#endif
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH 8/8] mtd: nand: stm_nand_bch: provide ST's implementation of Back Block Table
@ 2014-08-13  9:12   ` Lee Jones
  0 siblings, 0 replies; 51+ messages in thread
From: Lee Jones @ 2014-08-13  9:12 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: pekon, computersforpeace, lee.jones, kernel, linux-mtd

This is the BBT format ST use internally.  It has to be used on boards
which were flashed with or actively use ST's tooling and boards which
are booted using ST's bootloaders.

Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
 drivers/mtd/nand/Kconfig         |   8 +
 drivers/mtd/nand/Makefile        |   1 +
 drivers/mtd/nand/stm_nand_bbt.c  | 601 +++++++++++++++++++++++++++++++++++++++
 include/linux/mtd/stm_nand_bbt.h |  17 ++
 4 files changed, 627 insertions(+)
 create mode 100644 drivers/mtd/nand/stm_nand_bbt.c
 create mode 100644 include/linux/mtd/stm_nand_bbt.h

diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 2738eec..5c04ec1 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -520,4 +520,12 @@ config MTD_NAND_STM_BCH
 	help
 	  Adds support for the STMicroelectronics NANDi BCH Controller.
 
+config MTD_NAND_STM_BCH_BBT
+	tristate "STMicroelectronics: NANDi BCH Controller Bad Block Table support"
+	default MTD_NAND_STM_BCH
+	help
+	  Adds support for the STMicroelectronics Bad Block Table support.
+	  If you are using a device which has has already been initialised
+	  by ST or using their tooling/bootloaders, leave this enabled.
+
 endif # MTD_NAND
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 60f374b..5ef0462 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -47,6 +47,7 @@ obj-$(CONFIG_MTD_NAND_MPC5121_NFC)	+= mpc5121_nfc.o
 obj-$(CONFIG_MTD_NAND_RICOH)		+= r852.o
 obj-$(CONFIG_MTD_NAND_JZ4740)		+= jz4740_nand.o
 obj-$(CONFIG_MTD_NAND_STM_BCH)		+= stm_nand_bch.o stm_nand_dt.o
+obj-$(CONFIG_MTD_NAND_STM_BCH_BBT)	+= stm_nand_bbt.o
 obj-$(CONFIG_MTD_NAND_GPMI_NAND)	+= gpmi-nand/
 obj-$(CONFIG_MTD_NAND_XWAY)		+= xway_nand.o
 obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH)	+= bcm47xxnflash/
diff --git a/drivers/mtd/nand/stm_nand_bbt.c b/drivers/mtd/nand/stm_nand_bbt.c
new file mode 100644
index 0000000..242bffd
--- /dev/null
+++ b/drivers/mtd/nand/stm_nand_bbt.c
@@ -0,0 +1,601 @@
+/*
+ * Support for STMicroelectronics Bad Block Table (BBT)
+ *
+ * Copyright (c) 2014 STMicroelectronics Limited
+ *
+ * Authors: Angus Clark <Angus.Clark@st.com>
+ *	    Lee Jones <lee.jones@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/mtd/stm_nand.h>
+#include <linux/mtd/stm_nand_bbt.h>
+#include <generated/utsrelease.h>
+
+/*
+ * Inband Bad Block Table (IBBT)
+ */
+#define NAND_IBBT_NBLOCKS	4
+#define NAND_IBBT_SIGLEN	4
+#define NAND_IBBT_PRIMARY	0
+#define NAND_IBBT_MIRROR	1
+#define NAND_IBBT_SCHEMA	0x10
+#define NAND_IBBT_BCH_SCHEMA	0x10
+
+static uint8_t ibbt_sigs[2][NAND_IBBT_SIGLEN] = {
+	{'B', 'b', 't', '0'},
+	{'1', 't', 'b', 'B'},
+};
+
+static char *bbt_strs[] = {
+	"primary",
+	"mirror",
+};
+
+/* IBBT header */
+struct nand_ibbt_header {
+	uint8_t	signature[4];		/* "Bbt0" or "1tbB" signature */
+	uint8_t version;		/* BBT version ("age") */
+	uint8_t reserved[3];		/* padding */
+	uint8_t schema[4];		/* "base" schema (x4) */
+} __packed;
+
+/* Extend IBBT header with some stm-nand-bch niceties */
+struct nand_ibbt_bch_header {
+	struct nand_ibbt_header base;
+	uint8_t schema[4];		/* "private" schema (x4) */
+	uint8_t ecc_size[4];		/* ECC bytes (0, 32, 54) (x4) */
+	char	author[64];		/* Arbitrary string for S/W to use */
+} __packed;
+
+/*
+ * Bad Block Tables/Bad Block Markers
+ */
+#define BBT_MARK_BAD_FACTORY	0x0
+#define BBT_MARK_BAD_WEAR	0x1
+#define BBT_MARK_GOOD		0x3
+
+static void bbt_set_block_mark(uint8_t *bbt, uint32_t block, uint8_t mark)
+{
+	unsigned int byte = block >> 2;
+	unsigned int shift = (block & 0x3) << 1;
+
+	bbt[byte] &= ~(0x3 << shift);
+	bbt[byte] |= ((mark & 0x3) << shift);
+}
+
+static uint8_t bbt_get_block_mark(uint8_t *bbt, uint32_t block)
+{
+	unsigned int byte = block >> 2;
+	unsigned int shift = (block & 0x3) << 1;
+
+	return (bbt[byte] >> shift) & 0x3;
+}
+
+static int bbt_is_block_bad(uint8_t *bbt, uint32_t block)
+{
+	return bbt_get_block_mark(bbt, block) == BBT_MARK_GOOD ? 0 : 1;
+}
+
+/* Scan page for BBM(s), according to specified BBT options */
+static int nandi_scan_bad_block_markers_page(struct nandi_controller *nandi,
+					     uint32_t page)
+{
+	struct mtd_info *mtd = &nandi->info.mtd;
+	struct nand_chip *chip = mtd->priv;
+	uint8_t *oob_buf = nandi->oob_buf;
+	int i, e;
+
+	/* Read the OOB area */
+	flex_read_raw(nandi, page, mtd->writesize, oob_buf, mtd->oobsize);
+
+	if (oob_buf[chip->badblockpos] == 0xff)
+		return 0;
+
+	/* Tolerate 'alien' Hamming Boot Mode ECC */
+	e = 0;
+	for (i = 0; i < mtd->oobsize; i += 16)
+		e += hweight8(oob_buf[i + 3] ^ 'B');
+	if (e <= 1)
+		return 0;
+
+	/* Tolerate 'alien' Hamming AFM ECC */
+	e = 0;
+	for (i = 0; i < mtd->oobsize; i += 16) {
+		e += hweight8(oob_buf[i + 3] ^ 'A');
+		e += hweight8(oob_buf[i + 4] ^ 'F');
+		e += hweight8(oob_buf[i + 5] ^ 'M');
+		if (e <= 1)
+			return 0;
+	}
+
+	return 1;
+}
+
+/* Scan block for BBM(s), according to specified BBT options */
+static int nandi_scan_bad_block_markers_block(struct nandi_controller *nandi,
+					      uint32_t block)
+
+{
+	struct mtd_info *mtd = &nandi->info.mtd;
+	struct nand_chip *chip = mtd->priv;
+	uint32_t pages_per_block = mtd->erasesize >> chip->page_shift;
+	uint32_t page = block << (chip->phys_erase_shift - chip->page_shift);
+
+	if (nandi_scan_bad_block_markers_page(nandi, page))
+		return 1;
+
+	if ((chip->bbt_options & NAND_BBT_SCAN2NDPAGE) &&
+	    nandi_scan_bad_block_markers_page(nandi, page + 1))
+		return 1;
+
+	if ((chip->bbt_options & NAND_BBT_SCANLASTPAGE) &&
+	    nandi_scan_bad_block_markers_page(nandi,
+					      page + pages_per_block - 1))
+		return 1;
+
+	return 0;
+}
+
+/* Scan for BBMs and build memory-resident BBT */
+static int nandi_scan_build_bbt(struct nandi_controller *nandi,
+				struct nandi_bbt_info *bbt_info)
+{
+	struct mtd_info *mtd = &nandi->info.mtd;
+	struct nand_chip *chip = mtd->priv;
+	uint32_t page_size = mtd->writesize;
+	uint8_t *bbt = bbt_info->bbt;
+	uint32_t block;
+
+	dev_dbg(nandi->dev,
+		"scan device for bad-block markers [bbt options = 0x%02x]\n",
+		chip->bbt_options);
+
+	memset(bbt, 0xff, page_size);
+	bbt_info->bbt_vers[0] = 0;
+	bbt_info->bbt_vers[1] = 0;
+	bbt_info->bbt_block[0] = nandi->blocks_per_device - 1;
+	bbt_info->bbt_block[1] = nandi->blocks_per_device - 2;
+
+	for (block = 0; block < nandi->blocks_per_device; block++)
+		if (nandi_scan_bad_block_markers_block(nandi, block))
+			bbt_set_block_mark(bbt, block, BBT_MARK_BAD_FACTORY);
+
+	return 0;
+}
+
+/* Populate IBBT BCH Header */
+static void bch_fill_ibbt_header(struct nandi_controller *nandi,
+				 struct nand_ibbt_bch_header *ibbt_header,
+				 int bak, uint8_t vers)
+{
+	const char author[] = "STLinux " UTS_RELEASE " (stm-nand-bch)";
+
+	memcpy(ibbt_header->base.signature, ibbt_sigs[bak], NAND_IBBT_SIGLEN);
+	ibbt_header->base.version = vers;
+	memset(ibbt_header->base.schema, NAND_IBBT_SCHEMA, 4);
+
+	memset(ibbt_header->schema, NAND_IBBT_SCHEMA, 4);
+	memset(ibbt_header->ecc_size, bch_ecc_sizes[nandi->bch_ecc_mode], 4);
+	memcpy(ibbt_header->author, author, sizeof(author));
+}
+
+/* Write IBBT to Flash */
+static int bch_write_bbt_data(struct nandi_controller *nandi,
+			      struct nandi_bbt_info *bbt_info,
+			      uint32_t block, int bak, uint8_t vers)
+{
+	struct nand_ibbt_bch_header *ibbt_header =
+		(struct nand_ibbt_bch_header *)nandi->page_buf;
+	struct nand_chip *chip = &nandi->info.chip;
+	struct mtd_info *mtd = &nandi->info.mtd;
+	uint32_t page_size = mtd->writesize;
+	uint32_t block_size = mtd->erasesize;
+	loff_t offs;
+	size_t retlen;
+	int ret;
+
+	nandi->cached_page = -1;
+
+	/* Write BBT contents to first page of block */
+	offs = (loff_t)block << chip->phys_erase_shift;
+	ret = mtd_write(mtd, offs, page_size, &retlen, bbt_info->bbt);
+	if (ret)
+		return ret;
+
+	/* Update IBBT header and write to last page of block */
+	memset(ibbt_header, 0xff, mtd->writesize);
+	bch_fill_ibbt_header(nandi, ibbt_header, bak, vers);
+	offs += block_size - page_size;
+	ret = mtd_write(mtd, offs, sizeof(*ibbt_header), &retlen,
+			     (uint8_t *)ibbt_header);
+	return ret;
+}
+
+/*
+ * Update Flash-resident BBT:
+ *   erase/search suitable block, and write table data to Flash
+ */
+static int bch_update_bbt(struct nandi_controller *nandi,
+			 struct nandi_bbt_info *bbt_info,
+			 int bak, uint8_t vers)
+{
+	struct nand_chip *chip = &nandi->info.chip;
+	loff_t offs;
+	uint32_t block;
+	uint32_t block_lower;
+	uint32_t block_other;
+
+	block_other = bbt_info->bbt_block[(bak+1)%2];
+	block_lower = nandi->blocks_per_device - NAND_IBBT_NBLOCKS;
+
+	for (block = bbt_info->bbt_block[bak]; block >= block_lower;  block--) {
+		offs = (loff_t)block << chip->phys_erase_shift;
+
+		/* Skip if block used by other table */
+		if (block == block_other)
+			continue;
+
+		/* Skip if block is marked bad */
+		if (bbt_is_block_bad(bbt_info->bbt, block))
+			continue;
+
+		/* Erase block, mark bad and skip on failure */
+		if (bch_erase_block(nandi, offs) & NAND_STATUS_FAIL) {
+			dev_info(nandi->dev,
+				 "failed to erase block [%u:0x%012llx] while updating BBT\n",
+				 block, offs);
+			vers++;
+			bbt_set_block_mark(bbt_info->bbt, block,
+					   BBT_MARK_BAD_WEAR);
+			continue;
+		}
+
+		/* Write BBT, mark bad and skip on failure */
+		if (bch_write_bbt_data(nandi, bbt_info, block, bak, vers)) {
+			dev_info(nandi->dev,
+				 "failed to write BBT to block [%u:0x%012llx]\n",
+				 block, offs);
+			vers++;
+			bbt_set_block_mark(bbt_info->bbt, block,
+					   BBT_MARK_BAD_WEAR);
+			continue;
+		}
+
+		/* Success */
+		bbt_info->bbt_block[bak] = block;
+		bbt_info->bbt_vers[bak] = vers;
+		break;
+	}
+
+	/* No space in BBT area */
+	if (block < block_lower) {
+		dev_err(nandi->dev, "no space left in BBT area\n");
+		dev_err(nandi->dev, "failed to update %s BBT\n", bbt_strs[bak]);
+		return -ENOSPC;
+	}
+
+	dev_info(nandi->dev, "wrote BBT [%s:%u] at 0x%012llx [%u]\n",
+		 bbt_strs[bak], vers, offs, block);
+
+	return 0;
+}
+
+#define NAND_IBBT_UPDATE_PRIMARY	0x1
+#define NAND_IBBT_UPDATE_MIRROR		0x2
+#define NAND_IBBT_UPDATE_BOTH		(NAND_IBBT_UPDATE_PRIMARY | \
+					 NAND_IBBT_UPDATE_MIRROR)
+static char *bbt_update_strs[] = {
+	"",
+	"primary",
+	"mirror",
+	"both",
+};
+
+/*
+ * Update Flash-resident BBT(s):
+ *   incrementing 'vers' number if required, and ensuring Primary
+ *   and Mirror are kept in sync
+ */
+static int bch_update_bbts(struct nandi_controller *nandi,
+			   struct nandi_bbt_info *bbt_info,
+			   unsigned int update, uint8_t vers)
+{
+	int err;
+
+	dev_info(nandi->dev, "updating %s BBT(s)\n", bbt_update_strs[update]);
+
+	do {
+		/* Update Primary if specified */
+		if (update & NAND_IBBT_UPDATE_PRIMARY) {
+			err = bch_update_bbt(nandi, bbt_info, NAND_IBBT_PRIMARY,
+					     vers);
+			/* Bail out on error (e.g. no space left in BBT area) */
+			if (err)
+				return err;
+
+			/*
+			 * If update resulted in a new BBT version
+			 * (e.g. Erase/Write fail on BBT block) update version
+			 * here, and force update of other table.
+			 */
+			if (bbt_info->bbt_vers[NAND_IBBT_PRIMARY] != vers) {
+				vers = bbt_info->bbt_vers[NAND_IBBT_PRIMARY];
+				update = NAND_IBBT_UPDATE_MIRROR;
+			}
+		}
+
+		/* Update Mirror if specified */
+		if (update & NAND_IBBT_UPDATE_MIRROR) {
+			err = bch_update_bbt(nandi, bbt_info, NAND_IBBT_MIRROR,
+					     vers);
+			/* Bail out on error (e.g. no space left in BBT area) */
+			if (err)
+				return err;
+
+			/*
+			 * If update resulted in a new BBT version
+			 * (e.g. Erase/Write fail on BBT block) update version
+			 * here, and force update of other table.
+			 */
+			if (bbt_info->bbt_vers[NAND_IBBT_MIRROR] != vers) {
+				vers = bbt_info->bbt_vers[NAND_IBBT_MIRROR];
+				update = NAND_IBBT_UPDATE_PRIMARY;
+			}
+		}
+
+		/* Continue, until Primary and Mirror versions are in sync */
+	} while (bbt_info->bbt_vers[NAND_IBBT_PRIMARY] !=
+		 bbt_info->bbt_vers[NAND_IBBT_MIRROR]);
+
+	return 0;
+}
+
+/* Scan block for IBBT signature */
+static int bch_find_ibbt_sig(struct nandi_controller *nandi,
+			     uint32_t block, int *bak, uint8_t *vers,
+			     char *author)
+{
+	struct nand_chip *chip = &nandi->info.chip;
+	struct mtd_info *mtd = &nandi->info.mtd;
+	struct nand_ibbt_bch_header *ibbt_header;
+	loff_t offs;
+	uint8_t *buf = nandi->page_buf;
+	int match_sig;
+	unsigned int b;
+	unsigned int i;
+	size_t retlen;
+	int ret;
+
+	nandi->cached_page = -1;
+
+	/* Load last page of block */
+	offs = (loff_t)block << chip->phys_erase_shift;
+	offs += mtd->erasesize - mtd->writesize;
+	ret = mtd_read(mtd, offs, sizeof(*ibbt_header), &retlen, buf);
+	if (ret < 0) {
+		dev_info(nandi->dev,
+			 "Uncorrectable ECC error while scanning BBT signature at block %u [0x%012llx]\n",
+			 block, offs);
+		return 0;
+	}
+	ibbt_header = (struct nand_ibbt_bch_header *)buf;
+
+	/* Test IBBT signature */
+	match_sig = 0;
+	for (b = 0; b < 2 && !match_sig; b++) {
+		match_sig = 1;
+		for (i = 0; i < NAND_IBBT_SIGLEN; i++) {
+			if (ibbt_header->base.signature[i] != ibbt_sigs[b][i]) {
+				match_sig = 0;
+				break;
+			}
+		}
+
+	}
+
+	if (!match_sig)
+		return 0; /* Failed to match IBBT signature */
+
+	/* Test IBBT schema */
+	for (i = 0; i < 4; i++)
+		if (ibbt_header->base.schema[i] != NAND_IBBT_SCHEMA)
+			return 0;
+
+	/* Test IBBT BCH schema */
+	for (i = 0; i < 4; i++)
+		if (ibbt_header->schema[i] != NAND_IBBT_BCH_SCHEMA)
+			return 0;
+
+	/* We have a match */
+	*vers = ibbt_header->base.version;
+	*bak = b - 1;
+	strncpy(author, ibbt_header->author, 64);
+
+	return 1;
+}
+
+/* Search for and load Flash-resident BBT, updating Primary/Mirror if req'd */
+static int bch_load_bbt(struct nandi_controller *nandi,
+			struct nandi_bbt_info *bbt_info)
+{
+	struct nand_chip *chip = &nandi->info.chip;
+	struct mtd_info *mtd = &nandi->info.mtd;
+	uint32_t page_size = mtd->writesize;
+	unsigned int update = 0;
+	uint32_t block;
+	loff_t offs;
+	uint8_t vers;
+	char author[64];
+	size_t retlen;
+	int ret;
+	int bak;
+
+	dev_dbg(nandi->dev, "looking for Flash-resident BBTs\n");
+
+	bbt_info->bbt_block[0] = 0;
+	bbt_info->bbt_block[1] = 0;
+	bbt_info->bbt_vers[0] = 0;
+	bbt_info->bbt_vers[1] = 0;
+
+	/* Look for IBBT signatures */
+	for (block = nandi->blocks_per_device - NAND_IBBT_NBLOCKS;
+	     block < nandi->blocks_per_device;
+	     block++) {
+		offs = (loff_t)block << chip->phys_erase_shift;
+
+		if (bch_find_ibbt_sig(nandi, block, &bak, &vers, author)) {
+			dev_dbg(nandi->dev,
+				"found BBT [%s:%u] at 0x%012llx [%u] (%s)\n",
+				bbt_strs[bak], vers, offs, block,
+				author);
+
+			if (bbt_info->bbt_block[bak] == 0 ||
+			    ((int8_t)(bbt_info->bbt_vers[bak] - vers)) < 0) {
+				bbt_info->bbt_block[bak] = block;
+				bbt_info->bbt_vers[bak] = vers;
+			}
+		}
+	}
+
+	/* What have we found? */
+	if (bbt_info->bbt_block[0] == 0 && bbt_info->bbt_block[1] == 0) {
+		/* no primary, no mirror: return error */
+		return 1;
+	} else if (bbt_info->bbt_block[0] == 0) {
+		/* no primary: use mirror, update primary */
+		bak = 1;
+		update = NAND_IBBT_UPDATE_PRIMARY;
+		bbt_info->bbt_block[0] = nandi->blocks_per_device - 1;
+	} else if (bbt_info->bbt_block[1] == 0) {
+		/* no mirror: use primary, update mirror */
+		bak = 0;
+		update = NAND_IBBT_UPDATE_MIRROR;
+		bbt_info->bbt_block[1] = nandi->blocks_per_device - 1;
+	} else if (bbt_info->bbt_vers[0] == bbt_info->bbt_vers[1]) {
+		/* primary == mirror: use primary, no update required */
+		bak = 0;
+	} else if ((int8_t)(bbt_info->bbt_vers[1] -
+			    bbt_info->bbt_vers[0]) < 0) {
+		/* primary > mirror: use primary, update mirror */
+		bak = 0;
+		update = NAND_IBBT_UPDATE_MIRROR;
+	} else {
+		/* mirror > primary: use mirror, update primary */
+		bak = 1;
+		update = NAND_IBBT_UPDATE_PRIMARY;
+	}
+
+	vers = bbt_info->bbt_vers[bak];
+	block = bbt_info->bbt_block[bak];
+	offs = (loff_t)block << chip->phys_erase_shift;
+	dev_info(nandi->dev, "using BBT [%s:%u] at 0x%012llx [%u]\n",
+		 bbt_strs[bak], vers, offs, block);
+
+	/* Read BBT data */
+	ret = mtd_read(mtd, offs, page_size, &retlen, bbt_info->bbt);
+	if (ret < 0) {
+		dev_err(nandi->dev,
+			"error while reading BBT %s:%u] at 0x%012llx [%u]\n",
+			bbt_strs[bak], vers, offs, block);
+		return 1;
+	}
+
+	/* Update other BBT if required */
+	if (update)
+		bch_update_bbts(nandi, bbt_info, update, vers);
+
+	return 0;
+}
+
+int bch_scan_bbt(struct mtd_info *mtd)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct nandi_controller *nandi = chip->priv;
+	struct nandi_bbt_info *bbt_info = &nandi->info.bbt_info;
+	int err;
+
+	/* Load Flash-resident BBT */
+	err = bch_load_bbt(nandi, bbt_info);
+	if (err) {
+		dev_warn(nandi->dev,
+			"failed to find BBTs:"
+			"  scanning device for bad-block markers\n");
+
+		/* Scan, build, and write BBT */
+		nandi_scan_build_bbt(nandi, bbt_info);
+		err = bch_update_bbts(nandi, bbt_info, NAND_IBBT_UPDATE_BOTH,
+				      bbt_info->bbt_vers[0] + 1);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+int bch_block_isbad(struct mtd_info *mtd, loff_t offs, int getchip)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct nandi_controller *nandi = chip->priv;
+
+	uint32_t block;
+
+	/* Check for invalid offset */
+	if (offs > mtd->size)
+		return -EINVAL;
+
+	block = offs >> chip->phys_erase_shift;
+
+	/* Protect blocks reserved for BBTs */
+	if (block >= (nandi->blocks_per_device - NAND_IBBT_NBLOCKS))
+		return 1;
+
+	return bbt_is_block_bad(nandi->info.bbt_info.bbt, block);
+}
+
+int bch_block_markbad(struct mtd_info *mtd, loff_t offs)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct nandi_controller *nandi = chip->priv;
+
+	uint32_t block;
+	int ret;
+
+	/* Mark bad */
+	block = offs >> chip->phys_erase_shift;
+	bbt_set_block_mark(nandi->info.bbt_info.bbt, block, BBT_MARK_BAD_WEAR);
+
+	/* Update BBTs, incrementing bbt_vers */
+	ret = bch_update_bbts(nandi, &nandi->info.bbt_info,
+			      NAND_IBBT_UPDATE_BOTH,
+			      nandi->info.bbt_info.bbt_vers[0] + 1);
+
+	return ret;
+}
+
+void nandi_dump_bad_blocks(struct nandi_controller *nandi)
+{
+	struct nand_chip *chip = &nandi->info.chip;
+	int bad_count = 0;
+	uint32_t block;
+	uint8_t *bbt = nandi->info.bbt_info.bbt;
+	uint8_t mark;
+
+	pr_info("BBT:\n");
+	for (block = 0; block < nandi->blocks_per_device; block++) {
+		mark = bbt_get_block_mark(bbt, block);
+		if (mark != BBT_MARK_GOOD) {
+			pr_info("\t\tBlock 0x%08x [%05u] marked bad [%s]\n",
+				block << chip->phys_erase_shift, block,
+				(mark == BBT_MARK_BAD_FACTORY) ?
+				"Factory" : "Wear");
+			bad_count++;
+		}
+	}
+	if (bad_count == 0)
+		pr_info("\t\tNo bad blocks listed in BBT\n");
+}
+EXPORT_SYMBOL(nandi_dump_bad_blocks);
diff --git a/include/linux/mtd/stm_nand_bbt.h b/include/linux/mtd/stm_nand_bbt.h
new file mode 100644
index 0000000..70d3c17
--- /dev/null
+++ b/include/linux/mtd/stm_nand_bbt.h
@@ -0,0 +1,17 @@
+#include <linux/mtd/stm_nand.h>
+
+/* BCH ECC sizes */
+static int bch_ecc_sizes[] = {
+	[BCH_18BIT_ECC] = 32,
+	[BCH_30BIT_ECC] = 54,
+	[BCH_NO_ECC] = 0,
+};
+
+#if defined(MTD_NAND_STM_BBT)
+extern void nandi_dump_bad_blocks(struct nandi_controller *nandi);
+extern int bch_scan_bbt(struct mtd_info *mtd);
+extern int bch_block_isbad(struct mtd_info *mtd, loff_t offs, int getchip);
+extern int bch_block_markbad(struct mtd_info *mtd, loff_t offs);
+#else
+inline void nandi_dump_bad_blocks(struct nandi_controller *nandi) {}
+#endif
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* [PATCH 8/8] mtd: nand: stm_nand_bch: provide ST's implementation of Back Block Table
@ 2014-08-13  9:12   ` Lee Jones
  0 siblings, 0 replies; 51+ messages in thread
From: Lee Jones @ 2014-08-13  9:12 UTC (permalink / raw)
  To: linux-arm-kernel

This is the BBT format ST use internally.  It has to be used on boards
which were flashed with or actively use ST's tooling and boards which
are booted using ST's bootloaders.

Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
 drivers/mtd/nand/Kconfig         |   8 +
 drivers/mtd/nand/Makefile        |   1 +
 drivers/mtd/nand/stm_nand_bbt.c  | 601 +++++++++++++++++++++++++++++++++++++++
 include/linux/mtd/stm_nand_bbt.h |  17 ++
 4 files changed, 627 insertions(+)
 create mode 100644 drivers/mtd/nand/stm_nand_bbt.c
 create mode 100644 include/linux/mtd/stm_nand_bbt.h

diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 2738eec..5c04ec1 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -520,4 +520,12 @@ config MTD_NAND_STM_BCH
 	help
 	  Adds support for the STMicroelectronics NANDi BCH Controller.
 
+config MTD_NAND_STM_BCH_BBT
+	tristate "STMicroelectronics: NANDi BCH Controller Bad Block Table support"
+	default MTD_NAND_STM_BCH
+	help
+	  Adds support for the STMicroelectronics Bad Block Table support.
+	  If you are using a device which has has already been initialised
+	  by ST or using their tooling/bootloaders, leave this enabled.
+
 endif # MTD_NAND
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 60f374b..5ef0462 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -47,6 +47,7 @@ obj-$(CONFIG_MTD_NAND_MPC5121_NFC)	+= mpc5121_nfc.o
 obj-$(CONFIG_MTD_NAND_RICOH)		+= r852.o
 obj-$(CONFIG_MTD_NAND_JZ4740)		+= jz4740_nand.o
 obj-$(CONFIG_MTD_NAND_STM_BCH)		+= stm_nand_bch.o stm_nand_dt.o
+obj-$(CONFIG_MTD_NAND_STM_BCH_BBT)	+= stm_nand_bbt.o
 obj-$(CONFIG_MTD_NAND_GPMI_NAND)	+= gpmi-nand/
 obj-$(CONFIG_MTD_NAND_XWAY)		+= xway_nand.o
 obj-$(CONFIG_MTD_NAND_BCM47XXNFLASH)	+= bcm47xxnflash/
diff --git a/drivers/mtd/nand/stm_nand_bbt.c b/drivers/mtd/nand/stm_nand_bbt.c
new file mode 100644
index 0000000..242bffd
--- /dev/null
+++ b/drivers/mtd/nand/stm_nand_bbt.c
@@ -0,0 +1,601 @@
+/*
+ * Support for STMicroelectronics Bad Block Table (BBT)
+ *
+ * Copyright (c) 2014 STMicroelectronics Limited
+ *
+ * Authors: Angus Clark <Angus.Clark@st.com>
+ *	    Lee Jones <lee.jones@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/mtd/stm_nand.h>
+#include <linux/mtd/stm_nand_bbt.h>
+#include <generated/utsrelease.h>
+
+/*
+ * Inband Bad Block Table (IBBT)
+ */
+#define NAND_IBBT_NBLOCKS	4
+#define NAND_IBBT_SIGLEN	4
+#define NAND_IBBT_PRIMARY	0
+#define NAND_IBBT_MIRROR	1
+#define NAND_IBBT_SCHEMA	0x10
+#define NAND_IBBT_BCH_SCHEMA	0x10
+
+static uint8_t ibbt_sigs[2][NAND_IBBT_SIGLEN] = {
+	{'B', 'b', 't', '0'},
+	{'1', 't', 'b', 'B'},
+};
+
+static char *bbt_strs[] = {
+	"primary",
+	"mirror",
+};
+
+/* IBBT header */
+struct nand_ibbt_header {
+	uint8_t	signature[4];		/* "Bbt0" or "1tbB" signature */
+	uint8_t version;		/* BBT version ("age") */
+	uint8_t reserved[3];		/* padding */
+	uint8_t schema[4];		/* "base" schema (x4) */
+} __packed;
+
+/* Extend IBBT header with some stm-nand-bch niceties */
+struct nand_ibbt_bch_header {
+	struct nand_ibbt_header base;
+	uint8_t schema[4];		/* "private" schema (x4) */
+	uint8_t ecc_size[4];		/* ECC bytes (0, 32, 54) (x4) */
+	char	author[64];		/* Arbitrary string for S/W to use */
+} __packed;
+
+/*
+ * Bad Block Tables/Bad Block Markers
+ */
+#define BBT_MARK_BAD_FACTORY	0x0
+#define BBT_MARK_BAD_WEAR	0x1
+#define BBT_MARK_GOOD		0x3
+
+static void bbt_set_block_mark(uint8_t *bbt, uint32_t block, uint8_t mark)
+{
+	unsigned int byte = block >> 2;
+	unsigned int shift = (block & 0x3) << 1;
+
+	bbt[byte] &= ~(0x3 << shift);
+	bbt[byte] |= ((mark & 0x3) << shift);
+}
+
+static uint8_t bbt_get_block_mark(uint8_t *bbt, uint32_t block)
+{
+	unsigned int byte = block >> 2;
+	unsigned int shift = (block & 0x3) << 1;
+
+	return (bbt[byte] >> shift) & 0x3;
+}
+
+static int bbt_is_block_bad(uint8_t *bbt, uint32_t block)
+{
+	return bbt_get_block_mark(bbt, block) == BBT_MARK_GOOD ? 0 : 1;
+}
+
+/* Scan page for BBM(s), according to specified BBT options */
+static int nandi_scan_bad_block_markers_page(struct nandi_controller *nandi,
+					     uint32_t page)
+{
+	struct mtd_info *mtd = &nandi->info.mtd;
+	struct nand_chip *chip = mtd->priv;
+	uint8_t *oob_buf = nandi->oob_buf;
+	int i, e;
+
+	/* Read the OOB area */
+	flex_read_raw(nandi, page, mtd->writesize, oob_buf, mtd->oobsize);
+
+	if (oob_buf[chip->badblockpos] == 0xff)
+		return 0;
+
+	/* Tolerate 'alien' Hamming Boot Mode ECC */
+	e = 0;
+	for (i = 0; i < mtd->oobsize; i += 16)
+		e += hweight8(oob_buf[i + 3] ^ 'B');
+	if (e <= 1)
+		return 0;
+
+	/* Tolerate 'alien' Hamming AFM ECC */
+	e = 0;
+	for (i = 0; i < mtd->oobsize; i += 16) {
+		e += hweight8(oob_buf[i + 3] ^ 'A');
+		e += hweight8(oob_buf[i + 4] ^ 'F');
+		e += hweight8(oob_buf[i + 5] ^ 'M');
+		if (e <= 1)
+			return 0;
+	}
+
+	return 1;
+}
+
+/* Scan block for BBM(s), according to specified BBT options */
+static int nandi_scan_bad_block_markers_block(struct nandi_controller *nandi,
+					      uint32_t block)
+
+{
+	struct mtd_info *mtd = &nandi->info.mtd;
+	struct nand_chip *chip = mtd->priv;
+	uint32_t pages_per_block = mtd->erasesize >> chip->page_shift;
+	uint32_t page = block << (chip->phys_erase_shift - chip->page_shift);
+
+	if (nandi_scan_bad_block_markers_page(nandi, page))
+		return 1;
+
+	if ((chip->bbt_options & NAND_BBT_SCAN2NDPAGE) &&
+	    nandi_scan_bad_block_markers_page(nandi, page + 1))
+		return 1;
+
+	if ((chip->bbt_options & NAND_BBT_SCANLASTPAGE) &&
+	    nandi_scan_bad_block_markers_page(nandi,
+					      page + pages_per_block - 1))
+		return 1;
+
+	return 0;
+}
+
+/* Scan for BBMs and build memory-resident BBT */
+static int nandi_scan_build_bbt(struct nandi_controller *nandi,
+				struct nandi_bbt_info *bbt_info)
+{
+	struct mtd_info *mtd = &nandi->info.mtd;
+	struct nand_chip *chip = mtd->priv;
+	uint32_t page_size = mtd->writesize;
+	uint8_t *bbt = bbt_info->bbt;
+	uint32_t block;
+
+	dev_dbg(nandi->dev,
+		"scan device for bad-block markers [bbt options = 0x%02x]\n",
+		chip->bbt_options);
+
+	memset(bbt, 0xff, page_size);
+	bbt_info->bbt_vers[0] = 0;
+	bbt_info->bbt_vers[1] = 0;
+	bbt_info->bbt_block[0] = nandi->blocks_per_device - 1;
+	bbt_info->bbt_block[1] = nandi->blocks_per_device - 2;
+
+	for (block = 0; block < nandi->blocks_per_device; block++)
+		if (nandi_scan_bad_block_markers_block(nandi, block))
+			bbt_set_block_mark(bbt, block, BBT_MARK_BAD_FACTORY);
+
+	return 0;
+}
+
+/* Populate IBBT BCH Header */
+static void bch_fill_ibbt_header(struct nandi_controller *nandi,
+				 struct nand_ibbt_bch_header *ibbt_header,
+				 int bak, uint8_t vers)
+{
+	const char author[] = "STLinux " UTS_RELEASE " (stm-nand-bch)";
+
+	memcpy(ibbt_header->base.signature, ibbt_sigs[bak], NAND_IBBT_SIGLEN);
+	ibbt_header->base.version = vers;
+	memset(ibbt_header->base.schema, NAND_IBBT_SCHEMA, 4);
+
+	memset(ibbt_header->schema, NAND_IBBT_SCHEMA, 4);
+	memset(ibbt_header->ecc_size, bch_ecc_sizes[nandi->bch_ecc_mode], 4);
+	memcpy(ibbt_header->author, author, sizeof(author));
+}
+
+/* Write IBBT to Flash */
+static int bch_write_bbt_data(struct nandi_controller *nandi,
+			      struct nandi_bbt_info *bbt_info,
+			      uint32_t block, int bak, uint8_t vers)
+{
+	struct nand_ibbt_bch_header *ibbt_header =
+		(struct nand_ibbt_bch_header *)nandi->page_buf;
+	struct nand_chip *chip = &nandi->info.chip;
+	struct mtd_info *mtd = &nandi->info.mtd;
+	uint32_t page_size = mtd->writesize;
+	uint32_t block_size = mtd->erasesize;
+	loff_t offs;
+	size_t retlen;
+	int ret;
+
+	nandi->cached_page = -1;
+
+	/* Write BBT contents to first page of block */
+	offs = (loff_t)block << chip->phys_erase_shift;
+	ret = mtd_write(mtd, offs, page_size, &retlen, bbt_info->bbt);
+	if (ret)
+		return ret;
+
+	/* Update IBBT header and write to last page of block */
+	memset(ibbt_header, 0xff, mtd->writesize);
+	bch_fill_ibbt_header(nandi, ibbt_header, bak, vers);
+	offs += block_size - page_size;
+	ret = mtd_write(mtd, offs, sizeof(*ibbt_header), &retlen,
+			     (uint8_t *)ibbt_header);
+	return ret;
+}
+
+/*
+ * Update Flash-resident BBT:
+ *   erase/search suitable block, and write table data to Flash
+ */
+static int bch_update_bbt(struct nandi_controller *nandi,
+			 struct nandi_bbt_info *bbt_info,
+			 int bak, uint8_t vers)
+{
+	struct nand_chip *chip = &nandi->info.chip;
+	loff_t offs;
+	uint32_t block;
+	uint32_t block_lower;
+	uint32_t block_other;
+
+	block_other = bbt_info->bbt_block[(bak+1)%2];
+	block_lower = nandi->blocks_per_device - NAND_IBBT_NBLOCKS;
+
+	for (block = bbt_info->bbt_block[bak]; block >= block_lower;  block--) {
+		offs = (loff_t)block << chip->phys_erase_shift;
+
+		/* Skip if block used by other table */
+		if (block == block_other)
+			continue;
+
+		/* Skip if block is marked bad */
+		if (bbt_is_block_bad(bbt_info->bbt, block))
+			continue;
+
+		/* Erase block, mark bad and skip on failure */
+		if (bch_erase_block(nandi, offs) & NAND_STATUS_FAIL) {
+			dev_info(nandi->dev,
+				 "failed to erase block [%u:0x%012llx] while updating BBT\n",
+				 block, offs);
+			vers++;
+			bbt_set_block_mark(bbt_info->bbt, block,
+					   BBT_MARK_BAD_WEAR);
+			continue;
+		}
+
+		/* Write BBT, mark bad and skip on failure */
+		if (bch_write_bbt_data(nandi, bbt_info, block, bak, vers)) {
+			dev_info(nandi->dev,
+				 "failed to write BBT to block [%u:0x%012llx]\n",
+				 block, offs);
+			vers++;
+			bbt_set_block_mark(bbt_info->bbt, block,
+					   BBT_MARK_BAD_WEAR);
+			continue;
+		}
+
+		/* Success */
+		bbt_info->bbt_block[bak] = block;
+		bbt_info->bbt_vers[bak] = vers;
+		break;
+	}
+
+	/* No space in BBT area */
+	if (block < block_lower) {
+		dev_err(nandi->dev, "no space left in BBT area\n");
+		dev_err(nandi->dev, "failed to update %s BBT\n", bbt_strs[bak]);
+		return -ENOSPC;
+	}
+
+	dev_info(nandi->dev, "wrote BBT [%s:%u] at 0x%012llx [%u]\n",
+		 bbt_strs[bak], vers, offs, block);
+
+	return 0;
+}
+
+#define NAND_IBBT_UPDATE_PRIMARY	0x1
+#define NAND_IBBT_UPDATE_MIRROR		0x2
+#define NAND_IBBT_UPDATE_BOTH		(NAND_IBBT_UPDATE_PRIMARY | \
+					 NAND_IBBT_UPDATE_MIRROR)
+static char *bbt_update_strs[] = {
+	"",
+	"primary",
+	"mirror",
+	"both",
+};
+
+/*
+ * Update Flash-resident BBT(s):
+ *   incrementing 'vers' number if required, and ensuring Primary
+ *   and Mirror are kept in sync
+ */
+static int bch_update_bbts(struct nandi_controller *nandi,
+			   struct nandi_bbt_info *bbt_info,
+			   unsigned int update, uint8_t vers)
+{
+	int err;
+
+	dev_info(nandi->dev, "updating %s BBT(s)\n", bbt_update_strs[update]);
+
+	do {
+		/* Update Primary if specified */
+		if (update & NAND_IBBT_UPDATE_PRIMARY) {
+			err = bch_update_bbt(nandi, bbt_info, NAND_IBBT_PRIMARY,
+					     vers);
+			/* Bail out on error (e.g. no space left in BBT area) */
+			if (err)
+				return err;
+
+			/*
+			 * If update resulted in a new BBT version
+			 * (e.g. Erase/Write fail on BBT block) update version
+			 * here, and force update of other table.
+			 */
+			if (bbt_info->bbt_vers[NAND_IBBT_PRIMARY] != vers) {
+				vers = bbt_info->bbt_vers[NAND_IBBT_PRIMARY];
+				update = NAND_IBBT_UPDATE_MIRROR;
+			}
+		}
+
+		/* Update Mirror if specified */
+		if (update & NAND_IBBT_UPDATE_MIRROR) {
+			err = bch_update_bbt(nandi, bbt_info, NAND_IBBT_MIRROR,
+					     vers);
+			/* Bail out on error (e.g. no space left in BBT area) */
+			if (err)
+				return err;
+
+			/*
+			 * If update resulted in a new BBT version
+			 * (e.g. Erase/Write fail on BBT block) update version
+			 * here, and force update of other table.
+			 */
+			if (bbt_info->bbt_vers[NAND_IBBT_MIRROR] != vers) {
+				vers = bbt_info->bbt_vers[NAND_IBBT_MIRROR];
+				update = NAND_IBBT_UPDATE_PRIMARY;
+			}
+		}
+
+		/* Continue, until Primary and Mirror versions are in sync */
+	} while (bbt_info->bbt_vers[NAND_IBBT_PRIMARY] !=
+		 bbt_info->bbt_vers[NAND_IBBT_MIRROR]);
+
+	return 0;
+}
+
+/* Scan block for IBBT signature */
+static int bch_find_ibbt_sig(struct nandi_controller *nandi,
+			     uint32_t block, int *bak, uint8_t *vers,
+			     char *author)
+{
+	struct nand_chip *chip = &nandi->info.chip;
+	struct mtd_info *mtd = &nandi->info.mtd;
+	struct nand_ibbt_bch_header *ibbt_header;
+	loff_t offs;
+	uint8_t *buf = nandi->page_buf;
+	int match_sig;
+	unsigned int b;
+	unsigned int i;
+	size_t retlen;
+	int ret;
+
+	nandi->cached_page = -1;
+
+	/* Load last page of block */
+	offs = (loff_t)block << chip->phys_erase_shift;
+	offs += mtd->erasesize - mtd->writesize;
+	ret = mtd_read(mtd, offs, sizeof(*ibbt_header), &retlen, buf);
+	if (ret < 0) {
+		dev_info(nandi->dev,
+			 "Uncorrectable ECC error while scanning BBT signature at block %u [0x%012llx]\n",
+			 block, offs);
+		return 0;
+	}
+	ibbt_header = (struct nand_ibbt_bch_header *)buf;
+
+	/* Test IBBT signature */
+	match_sig = 0;
+	for (b = 0; b < 2 && !match_sig; b++) {
+		match_sig = 1;
+		for (i = 0; i < NAND_IBBT_SIGLEN; i++) {
+			if (ibbt_header->base.signature[i] != ibbt_sigs[b][i]) {
+				match_sig = 0;
+				break;
+			}
+		}
+
+	}
+
+	if (!match_sig)
+		return 0; /* Failed to match IBBT signature */
+
+	/* Test IBBT schema */
+	for (i = 0; i < 4; i++)
+		if (ibbt_header->base.schema[i] != NAND_IBBT_SCHEMA)
+			return 0;
+
+	/* Test IBBT BCH schema */
+	for (i = 0; i < 4; i++)
+		if (ibbt_header->schema[i] != NAND_IBBT_BCH_SCHEMA)
+			return 0;
+
+	/* We have a match */
+	*vers = ibbt_header->base.version;
+	*bak = b - 1;
+	strncpy(author, ibbt_header->author, 64);
+
+	return 1;
+}
+
+/* Search for and load Flash-resident BBT, updating Primary/Mirror if req'd */
+static int bch_load_bbt(struct nandi_controller *nandi,
+			struct nandi_bbt_info *bbt_info)
+{
+	struct nand_chip *chip = &nandi->info.chip;
+	struct mtd_info *mtd = &nandi->info.mtd;
+	uint32_t page_size = mtd->writesize;
+	unsigned int update = 0;
+	uint32_t block;
+	loff_t offs;
+	uint8_t vers;
+	char author[64];
+	size_t retlen;
+	int ret;
+	int bak;
+
+	dev_dbg(nandi->dev, "looking for Flash-resident BBTs\n");
+
+	bbt_info->bbt_block[0] = 0;
+	bbt_info->bbt_block[1] = 0;
+	bbt_info->bbt_vers[0] = 0;
+	bbt_info->bbt_vers[1] = 0;
+
+	/* Look for IBBT signatures */
+	for (block = nandi->blocks_per_device - NAND_IBBT_NBLOCKS;
+	     block < nandi->blocks_per_device;
+	     block++) {
+		offs = (loff_t)block << chip->phys_erase_shift;
+
+		if (bch_find_ibbt_sig(nandi, block, &bak, &vers, author)) {
+			dev_dbg(nandi->dev,
+				"found BBT [%s:%u] at 0x%012llx [%u] (%s)\n",
+				bbt_strs[bak], vers, offs, block,
+				author);
+
+			if (bbt_info->bbt_block[bak] == 0 ||
+			    ((int8_t)(bbt_info->bbt_vers[bak] - vers)) < 0) {
+				bbt_info->bbt_block[bak] = block;
+				bbt_info->bbt_vers[bak] = vers;
+			}
+		}
+	}
+
+	/* What have we found? */
+	if (bbt_info->bbt_block[0] == 0 && bbt_info->bbt_block[1] == 0) {
+		/* no primary, no mirror: return error */
+		return 1;
+	} else if (bbt_info->bbt_block[0] == 0) {
+		/* no primary: use mirror, update primary */
+		bak = 1;
+		update = NAND_IBBT_UPDATE_PRIMARY;
+		bbt_info->bbt_block[0] = nandi->blocks_per_device - 1;
+	} else if (bbt_info->bbt_block[1] == 0) {
+		/* no mirror: use primary, update mirror */
+		bak = 0;
+		update = NAND_IBBT_UPDATE_MIRROR;
+		bbt_info->bbt_block[1] = nandi->blocks_per_device - 1;
+	} else if (bbt_info->bbt_vers[0] == bbt_info->bbt_vers[1]) {
+		/* primary == mirror: use primary, no update required */
+		bak = 0;
+	} else if ((int8_t)(bbt_info->bbt_vers[1] -
+			    bbt_info->bbt_vers[0]) < 0) {
+		/* primary > mirror: use primary, update mirror */
+		bak = 0;
+		update = NAND_IBBT_UPDATE_MIRROR;
+	} else {
+		/* mirror > primary: use mirror, update primary */
+		bak = 1;
+		update = NAND_IBBT_UPDATE_PRIMARY;
+	}
+
+	vers = bbt_info->bbt_vers[bak];
+	block = bbt_info->bbt_block[bak];
+	offs = (loff_t)block << chip->phys_erase_shift;
+	dev_info(nandi->dev, "using BBT [%s:%u] at 0x%012llx [%u]\n",
+		 bbt_strs[bak], vers, offs, block);
+
+	/* Read BBT data */
+	ret = mtd_read(mtd, offs, page_size, &retlen, bbt_info->bbt);
+	if (ret < 0) {
+		dev_err(nandi->dev,
+			"error while reading BBT %s:%u] at 0x%012llx [%u]\n",
+			bbt_strs[bak], vers, offs, block);
+		return 1;
+	}
+
+	/* Update other BBT if required */
+	if (update)
+		bch_update_bbts(nandi, bbt_info, update, vers);
+
+	return 0;
+}
+
+int bch_scan_bbt(struct mtd_info *mtd)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct nandi_controller *nandi = chip->priv;
+	struct nandi_bbt_info *bbt_info = &nandi->info.bbt_info;
+	int err;
+
+	/* Load Flash-resident BBT */
+	err = bch_load_bbt(nandi, bbt_info);
+	if (err) {
+		dev_warn(nandi->dev,
+			"failed to find BBTs:"
+			"  scanning device for bad-block markers\n");
+
+		/* Scan, build, and write BBT */
+		nandi_scan_build_bbt(nandi, bbt_info);
+		err = bch_update_bbts(nandi, bbt_info, NAND_IBBT_UPDATE_BOTH,
+				      bbt_info->bbt_vers[0] + 1);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+int bch_block_isbad(struct mtd_info *mtd, loff_t offs, int getchip)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct nandi_controller *nandi = chip->priv;
+
+	uint32_t block;
+
+	/* Check for invalid offset */
+	if (offs > mtd->size)
+		return -EINVAL;
+
+	block = offs >> chip->phys_erase_shift;
+
+	/* Protect blocks reserved for BBTs */
+	if (block >= (nandi->blocks_per_device - NAND_IBBT_NBLOCKS))
+		return 1;
+
+	return bbt_is_block_bad(nandi->info.bbt_info.bbt, block);
+}
+
+int bch_block_markbad(struct mtd_info *mtd, loff_t offs)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct nandi_controller *nandi = chip->priv;
+
+	uint32_t block;
+	int ret;
+
+	/* Mark bad */
+	block = offs >> chip->phys_erase_shift;
+	bbt_set_block_mark(nandi->info.bbt_info.bbt, block, BBT_MARK_BAD_WEAR);
+
+	/* Update BBTs, incrementing bbt_vers */
+	ret = bch_update_bbts(nandi, &nandi->info.bbt_info,
+			      NAND_IBBT_UPDATE_BOTH,
+			      nandi->info.bbt_info.bbt_vers[0] + 1);
+
+	return ret;
+}
+
+void nandi_dump_bad_blocks(struct nandi_controller *nandi)
+{
+	struct nand_chip *chip = &nandi->info.chip;
+	int bad_count = 0;
+	uint32_t block;
+	uint8_t *bbt = nandi->info.bbt_info.bbt;
+	uint8_t mark;
+
+	pr_info("BBT:\n");
+	for (block = 0; block < nandi->blocks_per_device; block++) {
+		mark = bbt_get_block_mark(bbt, block);
+		if (mark != BBT_MARK_GOOD) {
+			pr_info("\t\tBlock 0x%08x [%05u] marked bad [%s]\n",
+				block << chip->phys_erase_shift, block,
+				(mark == BBT_MARK_BAD_FACTORY) ?
+				"Factory" : "Wear");
+			bad_count++;
+		}
+	}
+	if (bad_count == 0)
+		pr_info("\t\tNo bad blocks listed in BBT\n");
+}
+EXPORT_SYMBOL(nandi_dump_bad_blocks);
diff --git a/include/linux/mtd/stm_nand_bbt.h b/include/linux/mtd/stm_nand_bbt.h
new file mode 100644
index 0000000..70d3c17
--- /dev/null
+++ b/include/linux/mtd/stm_nand_bbt.h
@@ -0,0 +1,17 @@
+#include <linux/mtd/stm_nand.h>
+
+/* BCH ECC sizes */
+static int bch_ecc_sizes[] = {
+	[BCH_18BIT_ECC] = 32,
+	[BCH_30BIT_ECC] = 54,
+	[BCH_NO_ECC] = 0,
+};
+
+#if defined(MTD_NAND_STM_BBT)
+extern void nandi_dump_bad_blocks(struct nandi_controller *nandi);
+extern int bch_scan_bbt(struct mtd_info *mtd);
+extern int bch_block_isbad(struct mtd_info *mtd, loff_t offs, int getchip);
+extern int bch_block_markbad(struct mtd_info *mtd, loff_t offs);
+#else
+inline void nandi_dump_bad_blocks(struct nandi_controller *nandi) {}
+#endif
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 51+ messages in thread

* Re: [PATCH 0/8] mtd: nand: Support for new DT NAND driver
  2014-08-13  9:11 ` Lee Jones
  (?)
@ 2014-08-13 15:44   ` pekon
  -1 siblings, 0 replies; 51+ messages in thread
From: pekon @ 2014-08-13 15:44 UTC (permalink / raw)
  To: Lee Jones, linux-arm-kernel, linux-kernel
  Cc: computersforpeace, kernel, linux-mtd

Hello Lee,

On Wednesday 13 August 2014 02:41 PM, Lee Jones wrote:
> Hi Brian, Pekon,
>
> I believe all of your queries have either been answered or addressed
> and I am hoping this will be the last submission. :)
>
> /me crosses fingers!
>
> Kind regards,
> Lee
>
> Lee Jones (8):
>    ARM: multi-v7: Enable ST BCH NAND
>    ARM: sti: Add two new clock definitions for use with ST's NAND
>      controllers
>    ARM: sti: Add BCH (NAND Flash) Controller support for STiH41x 
> (Orly)
>      SoCs
>    ARM: sti: Enable BCH NAND for STiH416 B2020-RevE
>    mtd: nand: stm_nand_bch: provide Device Tree documentation
>    mtd: nand: add shared register defines for ST's NAND Controller
>      drivers
>    mtd: nand: stm_nand_bch: add support for ST's BCH NAND controller
>    mtd: nand: stm_nand_bch: provide ST's implementation of Back Block
>      Table
>

Couldn't review the last [Patch 8/8] for ST's BBT implementation as
I'm not familiar with it. But other patches look good. So for them

Reviewed-By: Pekon Gupta <pekon@pek-sem.com>


with regards, pekon


------------------------
Powered by BigRock.com


^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 0/8] mtd: nand: Support for new DT NAND driver
@ 2014-08-13 15:44   ` pekon
  0 siblings, 0 replies; 51+ messages in thread
From: pekon @ 2014-08-13 15:44 UTC (permalink / raw)
  To: Lee Jones, linux-arm-kernel, linux-kernel
  Cc: computersforpeace, linux-mtd, kernel

Hello Lee,

On Wednesday 13 August 2014 02:41 PM, Lee Jones wrote:
> Hi Brian, Pekon,
>
> I believe all of your queries have either been answered or addressed
> and I am hoping this will be the last submission. :)
>
> /me crosses fingers!
>
> Kind regards,
> Lee
>
> Lee Jones (8):
>    ARM: multi-v7: Enable ST BCH NAND
>    ARM: sti: Add two new clock definitions for use with ST's NAND
>      controllers
>    ARM: sti: Add BCH (NAND Flash) Controller support for STiH41x 
> (Orly)
>      SoCs
>    ARM: sti: Enable BCH NAND for STiH416 B2020-RevE
>    mtd: nand: stm_nand_bch: provide Device Tree documentation
>    mtd: nand: add shared register defines for ST's NAND Controller
>      drivers
>    mtd: nand: stm_nand_bch: add support for ST's BCH NAND controller
>    mtd: nand: stm_nand_bch: provide ST's implementation of Back Block
>      Table
>

Couldn't review the last [Patch 8/8] for ST's BBT implementation as
I'm not familiar with it. But other patches look good. So for them

Reviewed-By: Pekon Gupta <pekon@pek-sem.com>


with regards, pekon


------------------------
Powered by BigRock.com

^ permalink raw reply	[flat|nested] 51+ messages in thread

* [PATCH 0/8] mtd: nand: Support for new DT NAND driver
@ 2014-08-13 15:44   ` pekon
  0 siblings, 0 replies; 51+ messages in thread
From: pekon at pek-sem.com @ 2014-08-13 15:44 UTC (permalink / raw)
  To: linux-arm-kernel

Hello Lee,

On Wednesday 13 August 2014 02:41 PM, Lee Jones wrote:
> Hi Brian, Pekon,
>
> I believe all of your queries have either been answered or addressed
> and I am hoping this will be the last submission. :)
>
> /me crosses fingers!
>
> Kind regards,
> Lee
>
> Lee Jones (8):
>    ARM: multi-v7: Enable ST BCH NAND
>    ARM: sti: Add two new clock definitions for use with ST's NAND
>      controllers
>    ARM: sti: Add BCH (NAND Flash) Controller support for STiH41x 
> (Orly)
>      SoCs
>    ARM: sti: Enable BCH NAND for STiH416 B2020-RevE
>    mtd: nand: stm_nand_bch: provide Device Tree documentation
>    mtd: nand: add shared register defines for ST's NAND Controller
>      drivers
>    mtd: nand: stm_nand_bch: add support for ST's BCH NAND controller
>    mtd: nand: stm_nand_bch: provide ST's implementation of Back Block
>      Table
>

Couldn't review the last [Patch 8/8] for ST's BBT implementation as
I'm not familiar with it. But other patches look good. So for them

Reviewed-By: Pekon Gupta <pekon@pek-sem.com>


with regards, pekon


------------------------
Powered by BigRock.com

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 0/8] mtd: nand: Support for new DT NAND driver
  2014-08-13  9:11 ` Lee Jones
  (?)
@ 2014-08-14 22:40   ` Brian Norris
  -1 siblings, 0 replies; 51+ messages in thread
From: Brian Norris @ 2014-08-14 22:40 UTC (permalink / raw)
  To: Lee Jones; +Cc: linux-arm-kernel, linux-kernel, kernel, pekon, linux-mtd

On Wed, Aug 13, 2014 at 10:11:59AM +0100, Lee Jones wrote:
> Hi Brian, Pekon,
> 
> I believe all of your queries have either been answered or addressed
> and I am hoping this will be the last submission. :)
> 
> /me crosses fingers!
> 
> Kind regards,
> Lee

I didn't look through the patches yet, but my build tools tell me you
didn't compile-test this. Please compile, test, and resend your patches.

In file included from drivers/mtd/nand/stm_nand_bch.c:26:0:
include/linux/mtd/stm_nand_bbt.h:16:13: warning: no previous prototype for 'nandi_dump_bad_blocks' [-Wmissing-prototypes]
drivers/mtd/nand/stm_nand_bch.c: In function 'nandi_set_mtd_defaults':
drivers/mtd/nand/stm_nand_bch.c:870:19: error: 'bch_scan_bbt' undeclared (first use in this function)
drivers/mtd/nand/stm_nand_bch.c:870:19: note: each undeclared identifier is reported only once for each function it appears in
drivers/mtd/nand/stm_nand_bch.c:871:20: error: 'bch_block_isbad' undeclared (first use in this function)
drivers/mtd/nand/stm_nand_bch.c:872:24: error: 'bch_block_markbad' undeclared (first use in this function)
drivers/mtd/nand/stm_nand_bch.c: In function 'bch_calc_timing_registers':
drivers/mtd/nand/stm_nand_bch.c:1022:6: warning: variable 'ren_half_off' set but not used [-Wunused-but-set-variable]
drivers/mtd/nand/stm_nand_bch.c:1021:6: warning: variable 'ren_half_on' set but not used [-Wunused-but-set-variable]

Particularly, take a hard look at rewriting
include/linux/mtd/stm_nand_bbt.h.

Thanks,
Brian

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 0/8] mtd: nand: Support for new DT NAND driver
@ 2014-08-14 22:40   ` Brian Norris
  0 siblings, 0 replies; 51+ messages in thread
From: Brian Norris @ 2014-08-14 22:40 UTC (permalink / raw)
  To: Lee Jones; +Cc: pekon, linux-mtd, linux-kernel, linux-arm-kernel, kernel

On Wed, Aug 13, 2014 at 10:11:59AM +0100, Lee Jones wrote:
> Hi Brian, Pekon,
> 
> I believe all of your queries have either been answered or addressed
> and I am hoping this will be the last submission. :)
> 
> /me crosses fingers!
> 
> Kind regards,
> Lee

I didn't look through the patches yet, but my build tools tell me you
didn't compile-test this. Please compile, test, and resend your patches.

In file included from drivers/mtd/nand/stm_nand_bch.c:26:0:
include/linux/mtd/stm_nand_bbt.h:16:13: warning: no previous prototype for 'nandi_dump_bad_blocks' [-Wmissing-prototypes]
drivers/mtd/nand/stm_nand_bch.c: In function 'nandi_set_mtd_defaults':
drivers/mtd/nand/stm_nand_bch.c:870:19: error: 'bch_scan_bbt' undeclared (first use in this function)
drivers/mtd/nand/stm_nand_bch.c:870:19: note: each undeclared identifier is reported only once for each function it appears in
drivers/mtd/nand/stm_nand_bch.c:871:20: error: 'bch_block_isbad' undeclared (first use in this function)
drivers/mtd/nand/stm_nand_bch.c:872:24: error: 'bch_block_markbad' undeclared (first use in this function)
drivers/mtd/nand/stm_nand_bch.c: In function 'bch_calc_timing_registers':
drivers/mtd/nand/stm_nand_bch.c:1022:6: warning: variable 'ren_half_off' set but not used [-Wunused-but-set-variable]
drivers/mtd/nand/stm_nand_bch.c:1021:6: warning: variable 'ren_half_on' set but not used [-Wunused-but-set-variable]

Particularly, take a hard look at rewriting
include/linux/mtd/stm_nand_bbt.h.

Thanks,
Brian

^ permalink raw reply	[flat|nested] 51+ messages in thread

* [PATCH 0/8] mtd: nand: Support for new DT NAND driver
@ 2014-08-14 22:40   ` Brian Norris
  0 siblings, 0 replies; 51+ messages in thread
From: Brian Norris @ 2014-08-14 22:40 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Aug 13, 2014 at 10:11:59AM +0100, Lee Jones wrote:
> Hi Brian, Pekon,
> 
> I believe all of your queries have either been answered or addressed
> and I am hoping this will be the last submission. :)
> 
> /me crosses fingers!
> 
> Kind regards,
> Lee

I didn't look through the patches yet, but my build tools tell me you
didn't compile-test this. Please compile, test, and resend your patches.

In file included from drivers/mtd/nand/stm_nand_bch.c:26:0:
include/linux/mtd/stm_nand_bbt.h:16:13: warning: no previous prototype for 'nandi_dump_bad_blocks' [-Wmissing-prototypes]
drivers/mtd/nand/stm_nand_bch.c: In function 'nandi_set_mtd_defaults':
drivers/mtd/nand/stm_nand_bch.c:870:19: error: 'bch_scan_bbt' undeclared (first use in this function)
drivers/mtd/nand/stm_nand_bch.c:870:19: note: each undeclared identifier is reported only once for each function it appears in
drivers/mtd/nand/stm_nand_bch.c:871:20: error: 'bch_block_isbad' undeclared (first use in this function)
drivers/mtd/nand/stm_nand_bch.c:872:24: error: 'bch_block_markbad' undeclared (first use in this function)
drivers/mtd/nand/stm_nand_bch.c: In function 'bch_calc_timing_registers':
drivers/mtd/nand/stm_nand_bch.c:1022:6: warning: variable 'ren_half_off' set but not used [-Wunused-but-set-variable]
drivers/mtd/nand/stm_nand_bch.c:1021:6: warning: variable 'ren_half_on' set but not used [-Wunused-but-set-variable]

Particularly, take a hard look at rewriting
include/linux/mtd/stm_nand_bbt.h.

Thanks,
Brian

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 0/8] mtd: nand: Support for new DT NAND driver
  2014-08-14 22:40   ` Brian Norris
  (?)
@ 2014-08-18  8:39     ` Lee Jones
  -1 siblings, 0 replies; 51+ messages in thread
From: Lee Jones @ 2014-08-18  8:39 UTC (permalink / raw)
  To: Brian Norris; +Cc: linux-arm-kernel, linux-kernel, kernel, pekon, linux-mtd

On Thu, 14 Aug 2014, Brian Norris wrote:
> On Wed, Aug 13, 2014 at 10:11:59AM +0100, Lee Jones wrote:
> > Hi Brian, Pekon,
> > 
> > I believe all of your queries have either been answered or addressed
> > and I am hoping this will be the last submission. :)
> > 
> > /me crosses fingers!
> > 
> > Kind regards,
> > Lee
> 
> I didn't look through the patches yet, but my build tools tell me you
> didn't compile-test this. Please compile, test, and resend your patches.
> 
> In file included from drivers/mtd/nand/stm_nand_bch.c:26:0:
> include/linux/mtd/stm_nand_bbt.h:16:13: warning: no previous prototype for 'nandi_dump_bad_blocks' [-Wmissing-prototypes]
> drivers/mtd/nand/stm_nand_bch.c: In function 'nandi_set_mtd_defaults':
> drivers/mtd/nand/stm_nand_bch.c:870:19: error: 'bch_scan_bbt' undeclared (first use in this function)
> drivers/mtd/nand/stm_nand_bch.c:870:19: note: each undeclared identifier is reported only once for each function it appears in
> drivers/mtd/nand/stm_nand_bch.c:871:20: error: 'bch_block_isbad' undeclared (first use in this function)
> drivers/mtd/nand/stm_nand_bch.c:872:24: error: 'bch_block_markbad'
> undeclared (first use in this function)

These are due to a CONFIG rename I did lastminute.com.  I fixed it
~2mins after sending the set and will re-send today.

> Particularly, take a hard look at rewriting
> include/linux/mtd/stm_nand_bbt.h.

I don't think there is any need - although this change is required:

-#if defined(MTD_NAND_STM_BBT)
+#if defined(MTD_NAND_STM_BCH_BBT)

> drivers/mtd/nand/stm_nand_bch.c: In function 'bch_calc_timing_registers':
> drivers/mtd/nand/stm_nand_bch.c:1022:6: warning: variable 'ren_half_off' set but not used [-Wunused-but-set-variable]
> drivers/mtd/nand/stm_nand_bch.c:1021:6: warning: variable 'ren_half_on' set but not used [-Wunused-but-set-variable]

This I have never seen.  I guess -Wunused-but-set-variable is normally
off.  This issue has been present since the beginning of this endeavour
and exists in the original (internal) code.

Will fix.

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 0/8] mtd: nand: Support for new DT NAND driver
@ 2014-08-18  8:39     ` Lee Jones
  0 siblings, 0 replies; 51+ messages in thread
From: Lee Jones @ 2014-08-18  8:39 UTC (permalink / raw)
  To: Brian Norris; +Cc: pekon, linux-mtd, linux-kernel, linux-arm-kernel, kernel

On Thu, 14 Aug 2014, Brian Norris wrote:
> On Wed, Aug 13, 2014 at 10:11:59AM +0100, Lee Jones wrote:
> > Hi Brian, Pekon,
> > 
> > I believe all of your queries have either been answered or addressed
> > and I am hoping this will be the last submission. :)
> > 
> > /me crosses fingers!
> > 
> > Kind regards,
> > Lee
> 
> I didn't look through the patches yet, but my build tools tell me you
> didn't compile-test this. Please compile, test, and resend your patches.
> 
> In file included from drivers/mtd/nand/stm_nand_bch.c:26:0:
> include/linux/mtd/stm_nand_bbt.h:16:13: warning: no previous prototype for 'nandi_dump_bad_blocks' [-Wmissing-prototypes]
> drivers/mtd/nand/stm_nand_bch.c: In function 'nandi_set_mtd_defaults':
> drivers/mtd/nand/stm_nand_bch.c:870:19: error: 'bch_scan_bbt' undeclared (first use in this function)
> drivers/mtd/nand/stm_nand_bch.c:870:19: note: each undeclared identifier is reported only once for each function it appears in
> drivers/mtd/nand/stm_nand_bch.c:871:20: error: 'bch_block_isbad' undeclared (first use in this function)
> drivers/mtd/nand/stm_nand_bch.c:872:24: error: 'bch_block_markbad'
> undeclared (first use in this function)

These are due to a CONFIG rename I did lastminute.com.  I fixed it
~2mins after sending the set and will re-send today.

> Particularly, take a hard look at rewriting
> include/linux/mtd/stm_nand_bbt.h.

I don't think there is any need - although this change is required:

-#if defined(MTD_NAND_STM_BBT)
+#if defined(MTD_NAND_STM_BCH_BBT)

> drivers/mtd/nand/stm_nand_bch.c: In function 'bch_calc_timing_registers':
> drivers/mtd/nand/stm_nand_bch.c:1022:6: warning: variable 'ren_half_off' set but not used [-Wunused-but-set-variable]
> drivers/mtd/nand/stm_nand_bch.c:1021:6: warning: variable 'ren_half_on' set but not used [-Wunused-but-set-variable]

This I have never seen.  I guess -Wunused-but-set-variable is normally
off.  This issue has been present since the beginning of this endeavour
and exists in the original (internal) code.

Will fix.

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

^ permalink raw reply	[flat|nested] 51+ messages in thread

* [PATCH 0/8] mtd: nand: Support for new DT NAND driver
@ 2014-08-18  8:39     ` Lee Jones
  0 siblings, 0 replies; 51+ messages in thread
From: Lee Jones @ 2014-08-18  8:39 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, 14 Aug 2014, Brian Norris wrote:
> On Wed, Aug 13, 2014 at 10:11:59AM +0100, Lee Jones wrote:
> > Hi Brian, Pekon,
> > 
> > I believe all of your queries have either been answered or addressed
> > and I am hoping this will be the last submission. :)
> > 
> > /me crosses fingers!
> > 
> > Kind regards,
> > Lee
> 
> I didn't look through the patches yet, but my build tools tell me you
> didn't compile-test this. Please compile, test, and resend your patches.
> 
> In file included from drivers/mtd/nand/stm_nand_bch.c:26:0:
> include/linux/mtd/stm_nand_bbt.h:16:13: warning: no previous prototype for 'nandi_dump_bad_blocks' [-Wmissing-prototypes]
> drivers/mtd/nand/stm_nand_bch.c: In function 'nandi_set_mtd_defaults':
> drivers/mtd/nand/stm_nand_bch.c:870:19: error: 'bch_scan_bbt' undeclared (first use in this function)
> drivers/mtd/nand/stm_nand_bch.c:870:19: note: each undeclared identifier is reported only once for each function it appears in
> drivers/mtd/nand/stm_nand_bch.c:871:20: error: 'bch_block_isbad' undeclared (first use in this function)
> drivers/mtd/nand/stm_nand_bch.c:872:24: error: 'bch_block_markbad'
> undeclared (first use in this function)

These are due to a CONFIG rename I did lastminute.com.  I fixed it
~2mins after sending the set and will re-send today.

> Particularly, take a hard look at rewriting
> include/linux/mtd/stm_nand_bbt.h.

I don't think there is any need - although this change is required:

-#if defined(MTD_NAND_STM_BBT)
+#if defined(MTD_NAND_STM_BCH_BBT)

> drivers/mtd/nand/stm_nand_bch.c: In function 'bch_calc_timing_registers':
> drivers/mtd/nand/stm_nand_bch.c:1022:6: warning: variable 'ren_half_off' set but not used [-Wunused-but-set-variable]
> drivers/mtd/nand/stm_nand_bch.c:1021:6: warning: variable 'ren_half_on' set but not used [-Wunused-but-set-variable]

This I have never seen.  I guess -Wunused-but-set-variable is normally
off.  This issue has been present since the beginning of this endeavour
and exists in the original (internal) code.

Will fix.

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org ? Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 0/8] mtd: nand: Support for new DT NAND driver
  2014-08-18  8:39     ` Lee Jones
  (?)
@ 2014-08-18 10:06       ` Lee Jones
  -1 siblings, 0 replies; 51+ messages in thread
From: Lee Jones @ 2014-08-18 10:06 UTC (permalink / raw)
  To: Brian Norris; +Cc: linux-arm-kernel, linux-kernel, kernel, pekon, linux-mtd

> > Particularly, take a hard look at rewriting
> > include/linux/mtd/stm_nand_bbt.h.
> 
> I don't think there is any need - although this change is required:
> 
> -#if defined(MTD_NAND_STM_BBT)
> +#if defined(MTD_NAND_STM_BCH_BBT)

Sorry, I mean this:

-#if defined(MTD_NAND_STM_BBT)
+#if defined(CONFIG_MTD_NAND_STM_BCH_BBT)

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 0/8] mtd: nand: Support for new DT NAND driver
@ 2014-08-18 10:06       ` Lee Jones
  0 siblings, 0 replies; 51+ messages in thread
From: Lee Jones @ 2014-08-18 10:06 UTC (permalink / raw)
  To: Brian Norris; +Cc: pekon, linux-mtd, linux-kernel, linux-arm-kernel, kernel

> > Particularly, take a hard look at rewriting
> > include/linux/mtd/stm_nand_bbt.h.
> 
> I don't think there is any need - although this change is required:
> 
> -#if defined(MTD_NAND_STM_BBT)
> +#if defined(MTD_NAND_STM_BCH_BBT)

Sorry, I mean this:

-#if defined(MTD_NAND_STM_BBT)
+#if defined(CONFIG_MTD_NAND_STM_BCH_BBT)

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

^ permalink raw reply	[flat|nested] 51+ messages in thread

* [PATCH 0/8] mtd: nand: Support for new DT NAND driver
@ 2014-08-18 10:06       ` Lee Jones
  0 siblings, 0 replies; 51+ messages in thread
From: Lee Jones @ 2014-08-18 10:06 UTC (permalink / raw)
  To: linux-arm-kernel

> > Particularly, take a hard look at rewriting
> > include/linux/mtd/stm_nand_bbt.h.
> 
> I don't think there is any need - although this change is required:
> 
> -#if defined(MTD_NAND_STM_BBT)
> +#if defined(MTD_NAND_STM_BCH_BBT)

Sorry, I mean this:

-#if defined(MTD_NAND_STM_BBT)
+#if defined(CONFIG_MTD_NAND_STM_BCH_BBT)

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org ? Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 0/8] mtd: nand: Support for new DT NAND driver
  2014-08-18  8:39     ` Lee Jones
  (?)
@ 2014-08-19  1:31       ` Brian Norris
  -1 siblings, 0 replies; 51+ messages in thread
From: Brian Norris @ 2014-08-19  1:31 UTC (permalink / raw)
  To: Lee Jones; +Cc: linux-arm-kernel, linux-kernel, kernel, pekon, linux-mtd

On Mon, Aug 18, 2014 at 09:39:53AM +0100, Lee Jones wrote:
> On Thu, 14 Aug 2014, Brian Norris wrote:
> > Particularly, take a hard look at rewriting
> > include/linux/mtd/stm_nand_bbt.h.
> 
> I don't think there is any need - although this change is required:
> 
> -#if defined(MTD_NAND_STM_BBT)
> +#if defined(MTD_NAND_STM_BCH_BBT)

You're still missing a GPL header and #include guards.

Your whole patch series (both v1 and v2) also has a few legit
checkpatch.pl warnings. Please fix these.

Brian

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 0/8] mtd: nand: Support for new DT NAND driver
@ 2014-08-19  1:31       ` Brian Norris
  0 siblings, 0 replies; 51+ messages in thread
From: Brian Norris @ 2014-08-19  1:31 UTC (permalink / raw)
  To: Lee Jones; +Cc: pekon, linux-mtd, linux-kernel, linux-arm-kernel, kernel

On Mon, Aug 18, 2014 at 09:39:53AM +0100, Lee Jones wrote:
> On Thu, 14 Aug 2014, Brian Norris wrote:
> > Particularly, take a hard look at rewriting
> > include/linux/mtd/stm_nand_bbt.h.
> 
> I don't think there is any need - although this change is required:
> 
> -#if defined(MTD_NAND_STM_BBT)
> +#if defined(MTD_NAND_STM_BCH_BBT)

You're still missing a GPL header and #include guards.

Your whole patch series (both v1 and v2) also has a few legit
checkpatch.pl warnings. Please fix these.

Brian

^ permalink raw reply	[flat|nested] 51+ messages in thread

* [PATCH 0/8] mtd: nand: Support for new DT NAND driver
@ 2014-08-19  1:31       ` Brian Norris
  0 siblings, 0 replies; 51+ messages in thread
From: Brian Norris @ 2014-08-19  1:31 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Aug 18, 2014 at 09:39:53AM +0100, Lee Jones wrote:
> On Thu, 14 Aug 2014, Brian Norris wrote:
> > Particularly, take a hard look at rewriting
> > include/linux/mtd/stm_nand_bbt.h.
> 
> I don't think there is any need - although this change is required:
> 
> -#if defined(MTD_NAND_STM_BBT)
> +#if defined(MTD_NAND_STM_BCH_BBT)

You're still missing a GPL header and #include guards.

Your whole patch series (both v1 and v2) also has a few legit
checkpatch.pl warnings. Please fix these.

Brian

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 0/8] mtd: nand: Support for new DT NAND driver
  2014-08-18  8:39     ` Lee Jones
  (?)
@ 2014-08-19 19:06       ` Brian Norris
  -1 siblings, 0 replies; 51+ messages in thread
From: Brian Norris @ 2014-08-19 19:06 UTC (permalink / raw)
  To: Lee Jones; +Cc: linux-arm-kernel, linux-kernel, kernel, pekon, linux-mtd

On Mon, Aug 18, 2014 at 09:39:53AM +0100, Lee Jones wrote:
> On Thu, 14 Aug 2014, Brian Norris wrote:
> > drivers/mtd/nand/stm_nand_bch.c: In function 'bch_calc_timing_registers':
> > drivers/mtd/nand/stm_nand_bch.c:1022:6: warning: variable 'ren_half_off' set but not used [-Wunused-but-set-variable]
> > drivers/mtd/nand/stm_nand_bch.c:1021:6: warning: variable 'ren_half_on' set but not used [-Wunused-but-set-variable]
> 
> This I have never seen.  I guess -Wunused-but-set-variable is normally
> off.  This issue has been present since the beginning of this endeavour
> and exists in the original (internal) code.

BTW, you can get extra warnings by building with the W=[123] option to
the Makefile (e.g., 'make W=1').

$ make help
[...]
  make W=n   [targets] Enable extra gcc checks, n=1,2,3 where
		1: warnings which may be relevant and do not occur too often
		2: warnings which occur quite often but may still be relevant
		3: more obscure warnings, can most likely be ignored
		Multiple levels can be combined with W=12 or W=123

Brian

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 0/8] mtd: nand: Support for new DT NAND driver
@ 2014-08-19 19:06       ` Brian Norris
  0 siblings, 0 replies; 51+ messages in thread
From: Brian Norris @ 2014-08-19 19:06 UTC (permalink / raw)
  To: Lee Jones; +Cc: pekon, linux-mtd, linux-kernel, linux-arm-kernel, kernel

On Mon, Aug 18, 2014 at 09:39:53AM +0100, Lee Jones wrote:
> On Thu, 14 Aug 2014, Brian Norris wrote:
> > drivers/mtd/nand/stm_nand_bch.c: In function 'bch_calc_timing_registers':
> > drivers/mtd/nand/stm_nand_bch.c:1022:6: warning: variable 'ren_half_off' set but not used [-Wunused-but-set-variable]
> > drivers/mtd/nand/stm_nand_bch.c:1021:6: warning: variable 'ren_half_on' set but not used [-Wunused-but-set-variable]
> 
> This I have never seen.  I guess -Wunused-but-set-variable is normally
> off.  This issue has been present since the beginning of this endeavour
> and exists in the original (internal) code.

BTW, you can get extra warnings by building with the W=[123] option to
the Makefile (e.g., 'make W=1').

$ make help
[...]
  make W=n   [targets] Enable extra gcc checks, n=1,2,3 where
		1: warnings which may be relevant and do not occur too often
		2: warnings which occur quite often but may still be relevant
		3: more obscure warnings, can most likely be ignored
		Multiple levels can be combined with W=12 or W=123

Brian

^ permalink raw reply	[flat|nested] 51+ messages in thread

* [PATCH 0/8] mtd: nand: Support for new DT NAND driver
@ 2014-08-19 19:06       ` Brian Norris
  0 siblings, 0 replies; 51+ messages in thread
From: Brian Norris @ 2014-08-19 19:06 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Aug 18, 2014 at 09:39:53AM +0100, Lee Jones wrote:
> On Thu, 14 Aug 2014, Brian Norris wrote:
> > drivers/mtd/nand/stm_nand_bch.c: In function 'bch_calc_timing_registers':
> > drivers/mtd/nand/stm_nand_bch.c:1022:6: warning: variable 'ren_half_off' set but not used [-Wunused-but-set-variable]
> > drivers/mtd/nand/stm_nand_bch.c:1021:6: warning: variable 'ren_half_on' set but not used [-Wunused-but-set-variable]
> 
> This I have never seen.  I guess -Wunused-but-set-variable is normally
> off.  This issue has been present since the beginning of this endeavour
> and exists in the original (internal) code.

BTW, you can get extra warnings by building with the W=[123] option to
the Makefile (e.g., 'make W=1').

$ make help
[...]
  make W=n   [targets] Enable extra gcc checks, n=1,2,3 where
		1: warnings which may be relevant and do not occur too often
		2: warnings which occur quite often but may still be relevant
		3: more obscure warnings, can most likely be ignored
		Multiple levels can be combined with W=12 or W=123

Brian

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 0/8] mtd: nand: Support for new DT NAND driver
  2014-08-19  1:31       ` Brian Norris
  (?)
@ 2014-08-20  6:47         ` Lee Jones
  -1 siblings, 0 replies; 51+ messages in thread
From: Lee Jones @ 2014-08-20  6:47 UTC (permalink / raw)
  To: Brian Norris; +Cc: linux-arm-kernel, linux-kernel, kernel, pekon, linux-mtd

On Mon, 18 Aug 2014, Brian Norris wrote:

> On Mon, Aug 18, 2014 at 09:39:53AM +0100, Lee Jones wrote:
> > On Thu, 14 Aug 2014, Brian Norris wrote:
> > > Particularly, take a hard look at rewriting
> > > include/linux/mtd/stm_nand_bbt.h.
> > 
> > I don't think there is any need - although this change is required:
> > 
> > -#if defined(MTD_NAND_STM_BBT)
> > +#if defined(MTD_NAND_STM_BCH_BBT)
> 
> You're still missing a GPL header and #include guards.
> 
> Your whole patch series (both v1 and v2) also has a few legit
> checkpatch.pl warnings. Please fix these.

Will fix all of the above.

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 0/8] mtd: nand: Support for new DT NAND driver
@ 2014-08-20  6:47         ` Lee Jones
  0 siblings, 0 replies; 51+ messages in thread
From: Lee Jones @ 2014-08-20  6:47 UTC (permalink / raw)
  To: Brian Norris; +Cc: pekon, linux-mtd, linux-kernel, linux-arm-kernel, kernel

On Mon, 18 Aug 2014, Brian Norris wrote:

> On Mon, Aug 18, 2014 at 09:39:53AM +0100, Lee Jones wrote:
> > On Thu, 14 Aug 2014, Brian Norris wrote:
> > > Particularly, take a hard look at rewriting
> > > include/linux/mtd/stm_nand_bbt.h.
> > 
> > I don't think there is any need - although this change is required:
> > 
> > -#if defined(MTD_NAND_STM_BBT)
> > +#if defined(MTD_NAND_STM_BCH_BBT)
> 
> You're still missing a GPL header and #include guards.
> 
> Your whole patch series (both v1 and v2) also has a few legit
> checkpatch.pl warnings. Please fix these.

Will fix all of the above.

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

^ permalink raw reply	[flat|nested] 51+ messages in thread

* [PATCH 0/8] mtd: nand: Support for new DT NAND driver
@ 2014-08-20  6:47         ` Lee Jones
  0 siblings, 0 replies; 51+ messages in thread
From: Lee Jones @ 2014-08-20  6:47 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, 18 Aug 2014, Brian Norris wrote:

> On Mon, Aug 18, 2014 at 09:39:53AM +0100, Lee Jones wrote:
> > On Thu, 14 Aug 2014, Brian Norris wrote:
> > > Particularly, take a hard look at rewriting
> > > include/linux/mtd/stm_nand_bbt.h.
> > 
> > I don't think there is any need - although this change is required:
> > 
> > -#if defined(MTD_NAND_STM_BBT)
> > +#if defined(MTD_NAND_STM_BCH_BBT)
> 
> You're still missing a GPL header and #include guards.
> 
> Your whole patch series (both v1 and v2) also has a few legit
> checkpatch.pl warnings. Please fix these.

Will fix all of the above.

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org ? Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 0/8] mtd: nand: Support for new DT NAND driver
  2014-08-19 19:06       ` Brian Norris
  (?)
@ 2014-08-20  6:48         ` Lee Jones
  -1 siblings, 0 replies; 51+ messages in thread
From: Lee Jones @ 2014-08-20  6:48 UTC (permalink / raw)
  To: Brian Norris; +Cc: linux-arm-kernel, linux-kernel, kernel, pekon, linux-mtd

On Tue, 19 Aug 2014, Brian Norris wrote:

> On Mon, Aug 18, 2014 at 09:39:53AM +0100, Lee Jones wrote:
> > On Thu, 14 Aug 2014, Brian Norris wrote:
> > > drivers/mtd/nand/stm_nand_bch.c: In function 'bch_calc_timing_registers':
> > > drivers/mtd/nand/stm_nand_bch.c:1022:6: warning: variable 'ren_half_off' set but not used [-Wunused-but-set-variable]
> > > drivers/mtd/nand/stm_nand_bch.c:1021:6: warning: variable 'ren_half_on' set but not used [-Wunused-but-set-variable]
> > 
> > This I have never seen.  I guess -Wunused-but-set-variable is normally
> > off.  This issue has been present since the beginning of this endeavour
> > and exists in the original (internal) code.
> 
> BTW, you can get extra warnings by building with the W=[123] option to
> the Makefile (e.g., 'make W=1').
> 
> $ make help
> [...]
>   make W=n   [targets] Enable extra gcc checks, n=1,2,3 where
> 		1: warnings which may be relevant and do not occur too often
> 		2: warnings which occur quite often but may still be relevant
> 		3: more obscure warnings, can most likely be ignored
> 		Multiple levels can be combined with W=12 or W=123

Already taken care of.

I will reply in more detail to your other (build warnings) email.

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

^ permalink raw reply	[flat|nested] 51+ messages in thread

* Re: [PATCH 0/8] mtd: nand: Support for new DT NAND driver
@ 2014-08-20  6:48         ` Lee Jones
  0 siblings, 0 replies; 51+ messages in thread
From: Lee Jones @ 2014-08-20  6:48 UTC (permalink / raw)
  To: Brian Norris; +Cc: pekon, linux-mtd, linux-kernel, linux-arm-kernel, kernel

On Tue, 19 Aug 2014, Brian Norris wrote:

> On Mon, Aug 18, 2014 at 09:39:53AM +0100, Lee Jones wrote:
> > On Thu, 14 Aug 2014, Brian Norris wrote:
> > > drivers/mtd/nand/stm_nand_bch.c: In function 'bch_calc_timing_registers':
> > > drivers/mtd/nand/stm_nand_bch.c:1022:6: warning: variable 'ren_half_off' set but not used [-Wunused-but-set-variable]
> > > drivers/mtd/nand/stm_nand_bch.c:1021:6: warning: variable 'ren_half_on' set but not used [-Wunused-but-set-variable]
> > 
> > This I have never seen.  I guess -Wunused-but-set-variable is normally
> > off.  This issue has been present since the beginning of this endeavour
> > and exists in the original (internal) code.
> 
> BTW, you can get extra warnings by building with the W=[123] option to
> the Makefile (e.g., 'make W=1').
> 
> $ make help
> [...]
>   make W=n   [targets] Enable extra gcc checks, n=1,2,3 where
> 		1: warnings which may be relevant and do not occur too often
> 		2: warnings which occur quite often but may still be relevant
> 		3: more obscure warnings, can most likely be ignored
> 		Multiple levels can be combined with W=12 or W=123

Already taken care of.

I will reply in more detail to your other (build warnings) email.

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

^ permalink raw reply	[flat|nested] 51+ messages in thread

* [PATCH 0/8] mtd: nand: Support for new DT NAND driver
@ 2014-08-20  6:48         ` Lee Jones
  0 siblings, 0 replies; 51+ messages in thread
From: Lee Jones @ 2014-08-20  6:48 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, 19 Aug 2014, Brian Norris wrote:

> On Mon, Aug 18, 2014 at 09:39:53AM +0100, Lee Jones wrote:
> > On Thu, 14 Aug 2014, Brian Norris wrote:
> > > drivers/mtd/nand/stm_nand_bch.c: In function 'bch_calc_timing_registers':
> > > drivers/mtd/nand/stm_nand_bch.c:1022:6: warning: variable 'ren_half_off' set but not used [-Wunused-but-set-variable]
> > > drivers/mtd/nand/stm_nand_bch.c:1021:6: warning: variable 'ren_half_on' set but not used [-Wunused-but-set-variable]
> > 
> > This I have never seen.  I guess -Wunused-but-set-variable is normally
> > off.  This issue has been present since the beginning of this endeavour
> > and exists in the original (internal) code.
> 
> BTW, you can get extra warnings by building with the W=[123] option to
> the Makefile (e.g., 'make W=1').
> 
> $ make help
> [...]
>   make W=n   [targets] Enable extra gcc checks, n=1,2,3 where
> 		1: warnings which may be relevant and do not occur too often
> 		2: warnings which occur quite often but may still be relevant
> 		3: more obscure warnings, can most likely be ignored
> 		Multiple levels can be combined with W=12 or W=123

Already taken care of.

I will reply in more detail to your other (build warnings) email.

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org ? Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

^ permalink raw reply	[flat|nested] 51+ messages in thread

end of thread, other threads:[~2014-08-20  6:48 UTC | newest]

Thread overview: 51+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-08-13  9:11 [PATCH 0/8] mtd: nand: Support for new DT NAND driver Lee Jones
2014-08-13  9:11 ` Lee Jones
2014-08-13  9:11 ` Lee Jones
2014-08-13  9:12 ` [PATCH 1/8] ARM: multi-v7: Enable ST BCH NAND Lee Jones
2014-08-13  9:12   ` Lee Jones
2014-08-13  9:12   ` Lee Jones
2014-08-13  9:12 ` [PATCH 2/8] ARM: sti: Add two new clock definitions for use with ST's NAND controllers Lee Jones
2014-08-13  9:12   ` Lee Jones
2014-08-13  9:12   ` Lee Jones
2014-08-13  9:12 ` [PATCH 3/8] ARM: sti: Add BCH (NAND Flash) Controller support for STiH41x (Orly) SoCs Lee Jones
2014-08-13  9:12   ` Lee Jones
2014-08-13  9:12   ` Lee Jones
2014-08-13  9:12 ` [PATCH 4/8] ARM: sti: Enable BCH NAND for STiH416 B2020-RevE Lee Jones
2014-08-13  9:12   ` Lee Jones
2014-08-13  9:12   ` Lee Jones
2014-08-13  9:12 ` [PATCH 5/8] mtd: nand: stm_nand_bch: provide Device Tree documentation Lee Jones
2014-08-13  9:12   ` Lee Jones
2014-08-13  9:12   ` Lee Jones
2014-08-13  9:12 ` [PATCH 6/8] mtd: nand: add shared register defines for ST's NAND Controller drivers Lee Jones
2014-08-13  9:12   ` Lee Jones
2014-08-13  9:12   ` Lee Jones
2014-08-13  9:12 ` [PATCH 7/8] mtd: nand: stm_nand_bch: add support for ST's BCH NAND controller Lee Jones
2014-08-13  9:12   ` Lee Jones
2014-08-13  9:12   ` Lee Jones
2014-08-13  9:12 ` [PATCH 8/8] mtd: nand: stm_nand_bch: provide ST's implementation of Back Block Table Lee Jones
2014-08-13  9:12   ` Lee Jones
2014-08-13  9:12   ` Lee Jones
2014-08-13 15:44 ` [PATCH 0/8] mtd: nand: Support for new DT NAND driver pekon
2014-08-13 15:44   ` pekon at pek-sem.com
2014-08-13 15:44   ` pekon
2014-08-14 22:40 ` Brian Norris
2014-08-14 22:40   ` Brian Norris
2014-08-14 22:40   ` Brian Norris
2014-08-18  8:39   ` Lee Jones
2014-08-18  8:39     ` Lee Jones
2014-08-18  8:39     ` Lee Jones
2014-08-18 10:06     ` Lee Jones
2014-08-18 10:06       ` Lee Jones
2014-08-18 10:06       ` Lee Jones
2014-08-19  1:31     ` Brian Norris
2014-08-19  1:31       ` Brian Norris
2014-08-19  1:31       ` Brian Norris
2014-08-20  6:47       ` Lee Jones
2014-08-20  6:47         ` Lee Jones
2014-08-20  6:47         ` Lee Jones
2014-08-19 19:06     ` Brian Norris
2014-08-19 19:06       ` Brian Norris
2014-08-19 19:06       ` Brian Norris
2014-08-20  6:48       ` Lee Jones
2014-08-20  6:48         ` Lee Jones
2014-08-20  6:48         ` Lee Jones

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.