This commit adds support for booting from SPI NAND to SPL SPI code by
mimicing the behavior of boot ROM (use fixed page size and sequentially
try SPI NOR and NAND).

Signed-off-by: Icenowy Zheng <u...@icenowy.me>
---
 arch/arm/mach-sunxi/Kconfig         | 16 +++++++
 arch/arm/mach-sunxi/spl_spi_sunxi.c | 74 +++++++++++++++++++++++++++++
 2 files changed, 90 insertions(+)

diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
index 840bbc19b3..6afbb4acb5 100644
--- a/arch/arm/mach-sunxi/Kconfig
+++ b/arch/arm/mach-sunxi/Kconfig
@@ -1018,6 +1018,22 @@ config SPL_SPI_SUNXI
          sunxi SPI Flash. It uses the same method as the boot ROM, so does
          not need any extra configuration.
 
+config SPL_SPI_SUNXI_NAND
+       bool "Support for SPI NAND Flash on Allwinner SoCs in SPL"
+       depends on SPL_SPI_SUNXI
+       help
+         Enable support for SPI NAND Flash. This option allows SPL to mimic
+         Allwinner boot ROM's behavior to gain support for SPI NAND Flash;
+         a fixed page size needs to be assumed when building the SPL image.
+
+config SPL_SPI_SUNXI_NAND_ASSUMED_PAGESIZE
+       hex "Assumed pagesize for SPI NAND Flash in SPL"
+       depends on SPL_SPI_SUNXI_NAND
+       default 0x400 if MACH_SUNIV
+       help
+         Set the page size assumed by the SPL SPI NAND code, the default
+         value is the same with the boot ROM.
+
 config PINE64_DT_SELECTION
        bool "Enable Pine64 device tree selection code"
        depends on MACH_SUN50I
diff --git a/arch/arm/mach-sunxi/spl_spi_sunxi.c 
b/arch/arm/mach-sunxi/spl_spi_sunxi.c
index 21be33a23f..5178908327 100644
--- a/arch/arm/mach-sunxi/spl_spi_sunxi.c
+++ b/arch/arm/mach-sunxi/spl_spi_sunxi.c
@@ -305,6 +305,49 @@ static void spi0_xfer(const u8 *txbuf, u32 txlen, u8 
*rxbuf, u32 rxlen)
        }
 }
 
+#if defined(CONFIG_SPL_SPI_SUNXI_NAND)
+static int spi0_nand_switch_page(u32 page)
+{
+       unsigned count;
+       u8 buf[4];
+
+       /* Configure the Page Data Read (13h) command header */
+       buf[0] = 0x13;
+       buf[1] = (u8)(page >> 16);
+       buf[2] = (u8)(page >> 8);
+       buf[3] = (u8)(page);
+
+       spi0_xfer(buf, 4, NULL, 0);
+
+       /* Wait for NAND chip to exit busy state */
+       buf[0] = 0x0f;
+       buf[1] = 0xc0;
+
+       /* Load a NAND page can take up to 2-decimal-digit microseconds */
+       for (count = 0; count < 100; count ++) {
+               udelay(1);
+               spi0_xfer(buf, 2, buf+2, 1);
+               if (!(buf[2] & 0x1))
+                       return 0;
+       }
+
+       return -ETIMEDOUT;
+}
+
+static void spi0_nand_reset(void)
+{
+       u8 buf[1];
+
+       /* Configure the Device RESET (ffh) command */
+       buf[0] = 0xff;
+
+       spi0_xfer(buf, 1, NULL, 0);
+
+       /* Wait for the NAND to finish resetting */
+       udelay(10);
+}
+#endif
+
 static void spi0_read_data(void *buf, u32 addr, u32 len, u32 addr_len)
 {
        u8 *buf8 = buf;
@@ -348,6 +391,28 @@ static ulong spi_load_read_nor(struct spl_load_info *load, 
ulong sector,
        return count;
 }
 
+#if defined(CONFIG_SPL_SPI_SUNXI_NAND)
+static ulong spi_load_read_nand(struct spl_load_info *load, ulong sector,
+                              ulong count, void *buf)
+{
+       const ulong pagesize = CONFIG_SPL_SPI_SUNXI_NAND_ASSUMED_PAGESIZE;
+       ulong remain = count;
+
+       while (remain) {
+               ulong count_in_page = min(remain, pagesize - (sector % 
pagesize));
+               ulong current_page = sector / pagesize;
+               if (spi0_nand_switch_page(current_page) != 0)
+                       return 0;
+               spi0_read_data(buf, sector % pagesize, count_in_page, 2);
+               remain -= count_in_page;
+               sector += count_in_page;
+               buf += count_in_page;
+       }
+
+       return count;
+}
+#endif
+
 /*****************************************************************************/
 
 static int spl_spi_try_load(struct spl_image_info *spl_image,
@@ -400,9 +465,18 @@ static int spl_spi_load_image(struct spl_image_info 
*spl_image,
 
        spi0_init();
 
+#if defined(CONFIG_SPL_SPI_SUNXI_NAND)
+       spi0_nand_reset();
+       load.read = spi_load_read_nand;
+       ret = spl_spi_try_load(spl_image, bootdev, &load, load_offset, false);
+       if (!ret)
+               goto out;
+#endif
+
        load.read = spi_load_read_nor;
        ret = spl_spi_try_load(spl_image, bootdev, &load, load_offset, true);
 
+out:
        spi0_deinit();
 
        return ret;
-- 
2.37.1

-- 
You received this message because you are subscribed to the Google Groups 
"linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to linux-sunxi+unsubscr...@googlegroups.com.
To view this discussion on the web, visit 
https://groups.google.com/d/msgid/linux-sunxi/20221014030520.3067228-5-uwu%40icenowy.me.

Reply via email to