ZynqMP NAND

Introduction

This page gives an overview of Zynq Ultrascale+ MPSoC nand driver which is located at drivers/mtd/nand/raw/arasan_nand.c. link: nand source file
NAND driver provides basic functions that are required for accessing the nand flash memory including the support for hw ecc, on-die ecc and bad block management.

HW IP Features

Controller Features


  • Complies with the ONFI 3.1 specification
  • Supports interleaving operations
  • Supports BCH error correction code (ECC) data widths of 4, 8, 12, and 24 bits.
  • All ONFI 3.1 commands
  • PIO and MDMA support
  • SDR and NVDDR modes
  • supports only 8-bit bus support
  • Hardware ECC (Hamming code and BCH)
  • Page size up to 16K
  • Programmable timing modes
  • 64-bit dma support.
  • Supports multiple chip selects (up to 2)


Driver Features

  • Supports only the mandatory ONFI 3.1 commands. i.e Reset, Read status, Read ID, Read Parameter Page, Read Page, Program Page, Erase Block, Set/Get Features
  • Supports SDR/DDR modes
  • Supports timing modes 0-5
  • Supports PIO and MDMA support
  • Support for multiple chip selection
  • Support 64-bit dma
  • Support BBT management
  • Hardware ECC (Hamming code and BCH up to 24 bits)
  • Software ECC (Hamming code and BCH up to 24 bits correction and up to 32bit bits detection)

Missing features, Known Issues, limitations

  • No support for interleaved and all optional ONFI 3.1 commands.
  • Hardware ECC configuration limited to 24-bits of error correction and 24-bits of error detection.
  • Performance degradation with Software ECC when compared with Hardware ECC.

Important AR links

  • 2021.1 release on-wards Linux Arasan NAND driver uses the SW-ECC with BCH as the default ECC detection/correction, this results in a degradation of performance as compared to HW-ECC. -  AR-76631
  • 2021.x, 2022.x, 2023.1 BBT of NAND is different between U-boot and Linux kernel AR-Link

Bad Block management

Bad block management implementation is same as Linux MTD bad block management with the exception of reserving number of blocks to store Bad Block Table (BBT) from default 4 blocks to 64 blocks. This is because one of the Micron flash part MT29F32G08ABCDB has last 32 blocks as bad most of the times. Since it was difficult to store bad block table in last 4 blocks, the number of blocks are increased to 64 blocks.

Kernel Configuration Options

The following config options should be enabled in order to build Zynq Ultrascale+ MPSoC

CONFIG_MTD_NAND_ECC_SW_BCH
CONFIG_MTD_NAND_ARASAN
CONFIG_MTD_NAND

config MTD_NAND_ECC_SW_BCH
        bool "Support software BCH ECC"
        select BCH
        default n
        help
          This enables support for software BCH error correction. Binary BCH
          codes are more powerful and cpu intensive than traditional Hamming
          ECC codes. They are used with NAND devices requiring more than 1 bit
          of error correction.

config MTD_NAND_ARASAN
    tristate "Support for Arasan Nand Flash controller"
    depends on HAS_IOMEM && HAS_DMA
	select BCH
    help
      Enables the driver for the Arasan NAND flash controller on Zynq Ultrascale+ MPSoC.

Device Tree Settings

More information on nand device tree node details, refer the link Nand device tree
Example for device tree

nand0: nand@ff100000 {
compatible = "arasan,nfc-v3p10";
reg = <0x0 0xff100000 0x0 0x1000>;
clock-names = "clk_sys", "clk_flash";
interrupt-parent = <&gic>;
interrupts = <0 14 4>;
#address-cells = <2>;
#size-cells = <1>;
};
 
 

Nand Partition details


Below example list the dts properties for operating in Software-ECC mode and its also assumes that controller is using 2 chip select lines

&nand0 {
   status = "okay";
   arasan,has-mdma;
   
   nand@0 {
     reg = <0x0>;
     #address-cells = <0x2>;
     #size-cells = <0x1>;
	 nand-ecc-mode = "soft";
	 nand-ecc-algo = "bch";
	 nand-rb = <0x0>;
	 label = "main-storage-0";
  partition@0 { /* for testing purpose */
    label = "nand-fsbl-uboot";
    reg = <0x0 0x0 0x400000>;
  };
  partition@1 { /* for testing purpose */
    label = "nand-linux";
    reg = <0x0 0x400000 0x1400000>;
  };
  partition@2 { /* for testing purpose */
    label = "nand-device-tree";
    reg = <0x0 0x1800000 0x400000>;
  };
  partition@3 { /* for testing purpose */
    label = "nand-rootfs";
    reg = <0x0 0x1C00000 0x1400000>;
  };
  partition@4 { /* for testing purpose */
    label = "nand-bitstream";
    reg = <0x0 0x3000000 0x400000>;
  };
  partition@5 { /* for testing purpose */
    label = "nand-misc";
    reg = <0x0 0x3400000 0xFCC00000>;
  };
 };
 
nand@1 {
  reg = <0x1>;
  #address-cells = <0x2>;
  #size-cells = <0x1>;
  nand-ecc-mode = "soft";
  nand-ecc-algo = "bch";
  nand-rb = <0x0>;
  label = "main-storage-1";
  partition@0 { /* for testing purpose */
    label = "nand1-fsbl-uboot";
    reg = <0x1 0x0 0x400000>;
  };
  partition@1 { /* for testing purpose */
    label = "nand1-linux";
    reg = <0x1 0x400000 0x1400000>;
  };
  partition@2 { /* for testing purpose */
    label = "nand1-device-tree";
    reg = <0x1 0x1800000 0x400000>;
  };
  partition@3 { /* for testing purpose */
    label = "nand1-rootfs";
    reg = <0x1 0x1C00000 0x1400000>;
  };
  partition@4 { /* for testing purpose */
    label = "nand1-bitstream";
    reg = <0x1 0x3000000 0x400000>;
  };
  partition@5 { /* for testing purpose */
    label = "nand1-misc";
    reg = <0x1 0x3400000 0xFCC00000>;
  };
};
};


Below example list the dts properties for operating in Hardware-ECC mode and its also assumes that controller is using 2 chip select lines

&nand0 {
   status = "okay";
   arasan,has-mdma;
   
   nand@0 {
     reg = <0x0>;
     #address-cells = <0x2>;
     #size-cells = <0x1>;
	 nand-ecc-mode = "hw";
	 nand-rb = <0x0>;
	 label = "main-storage-0";
  partition@0 { /* for testing purpose */
    label = "nand-fsbl-uboot";
    reg = <0x0 0x0 0x400000>;
  };
  partition@1 { /* for testing purpose */
    label = "nand-linux";
    reg = <0x0 0x400000 0x1400000>;
  };
  partition@2 { /* for testing purpose */
    label = "nand-device-tree";
    reg = <0x0 0x1800000 0x400000>;
  };
  partition@3 { /* for testing purpose */
    label = "nand-rootfs";
    reg = <0x0 0x1C00000 0x1400000>;
  };
  partition@4 { /* for testing purpose */
    label = "nand-bitstream";
    reg = <0x0 0x3000000 0x400000>;
  };
  partition@5 { /* for testing purpose */
    label = "nand-misc";
    reg = <0x0 0x3400000 0xFCC00000>;
  };
 };
 
nand@1 {
  reg = <0x1>;
  #address-cells = <0x2>;
  #size-cells = <0x1>;
  nand-ecc-mode = "hw";
  nand-rb = <0x0>;
  label = "main-storage-1";
  partition@0 { /* for testing purpose */
    label = "nand1-fsbl-uboot";
    reg = <0x1 0x0 0x400000>;
  };
  partition@1 { /* for testing purpose */
    label = "nand1-linux";
    reg = <0x1 0x400000 0x1400000>;
  };
  partition@2 { /* for testing purpose */
    label = "nand1-device-tree";
    reg = <0x1 0x1800000 0x400000>;
  };
  partition@3 { /* for testing purpose */
    label = "nand1-rootfs";
    reg = <0x1 0x1C00000 0x1400000>;
  };
  partition@4 { /* for testing purpose */
    label = "nand1-bitstream";
    reg = <