[PATCH 0/8] SUNIV SPI NAND support in SPL

This patchset tries to extend SPI-based boot code in sunxi SPL to support SPI NAND, following the same principle with current SPI NOR code (mimicking the behavior of sunxi BROM). In addition, as part of test to this patchset, some patches for Source Parts Inc. PopStick is attached, although marked DO NOT MERGE because the DT should come from Linux after it's ready.
To keep thr code that accesses SPI NAND as simple as possible, it assumes fixed page size, which is also what sunxi BROM does. The SUNIV SPL assumes 0x400 page size, but here to utilize the space better, in the attached example of PopStick, U-Boot main part is assumed to be with 0x800 page size (which is the real situation of the W25N01 flash used by PopStick).
Icenowy Zheng (8): sunxi: SPL SPI: extract code for doing SPI transfer sunxi: SPL SPI: add support for read command with 2 byte address sunxi: SPL SPI: allow multiple boot attempt sunxi: SPL SPI: add initial support for booting from SPI NAND sunxi: enable support for SPI NAND booting on SUNIV [DO NOT MERGE] sunxi: sync DT from my tree for PopStick [DO NOT MERGE, DIRTY HACK] sunxi: use UBI for environement storage [DO NOT MERGE] sunxi: add a defconfig for PopStick
arch/arm/dts/Makefile | 3 +- arch/arm/dts/suniv-f1c100s-licheepi-nano.dts | 16 ++ arch/arm/dts/suniv-f1c100s.dtsi | 26 ++ arch/arm/dts/suniv-f1c200s-popstick-v1.1.dts | 101 ++++++++ arch/arm/mach-sunxi/Kconfig | 16 ++ arch/arm/mach-sunxi/board.c | 4 +- arch/arm/mach-sunxi/spl_spi_sunxi.c | 247 ++++++++++++++----- board/sunxi/board.c | 1 + configs/popstick_defconfig | 35 +++ 9 files changed, 377 insertions(+), 72 deletions(-) create mode 100644 arch/arm/dts/suniv-f1c200s-popstick-v1.1.dts create mode 100644 configs/popstick_defconfig

To support SPI NAND flashes, more commands than Read (03h) are needed.
Extract the code for doing SPI transfer from the reading code for code reuse.
Signed-off-by: Icenowy Zheng uwu@icenowy.me --- arch/arm/mach-sunxi/spl_spi_sunxi.c | 105 ++++++++++++++++------------ 1 file changed, 59 insertions(+), 46 deletions(-)
diff --git a/arch/arm/mach-sunxi/spl_spi_sunxi.c b/arch/arm/mach-sunxi/spl_spi_sunxi.c index 925bf85f2d..7975457758 100644 --- a/arch/arm/mach-sunxi/spl_spi_sunxi.c +++ b/arch/arm/mach-sunxi/spl_spi_sunxi.c @@ -243,77 +243,90 @@ static void spi0_deinit(void)
#define SPI_READ_MAX_SIZE 60 /* FIFO size, minus 4 bytes of the header */
-static void sunxi_spi0_read_data(u8 *buf, u32 addr, u32 bufsize, - ulong spi_ctl_reg, - ulong spi_ctl_xch_bitmask, - ulong spi_fifo_reg, - ulong spi_tx_reg, - ulong spi_rx_reg, - ulong spi_bc_reg, - ulong spi_tc_reg, - ulong spi_bcc_reg) +static void sunxi_spi0_xfer(const u8 *txbuf, u32 txlen, + u8 *rxbuf, u32 rxlen, + ulong spi_ctl_reg, + ulong spi_ctl_xch_bitmask, + ulong spi_fifo_reg, + ulong spi_tx_reg, + ulong spi_rx_reg, + ulong spi_bc_reg, + ulong spi_tc_reg, + ulong spi_bcc_reg) { - writel(4 + bufsize, spi_bc_reg); /* Burst counter (total bytes) */ - writel(4, spi_tc_reg); /* Transfer counter (bytes to send) */ + writel(txlen + rxlen, spi_bc_reg); /* Burst counter (total bytes) */ + writel(txlen, spi_tc_reg); /* Transfer counter (bytes to send) */ if (spi_bcc_reg) - writel(4, spi_bcc_reg); /* SUN6I also needs this */ + writel(txlen, spi_bcc_reg); /* SUN6I also needs this */
- /* Send the Read Data Bytes (03h) command header */ - writeb(0x03, spi_tx_reg); - writeb((u8)(addr >> 16), spi_tx_reg); - writeb((u8)(addr >> 8), spi_tx_reg); - writeb((u8)(addr), spi_tx_reg); + for (u32 i = 0; i < txlen; i++) + writeb(*(txbuf++), spi_tx_reg);
/* Start the data transfer */ setbits_le32(spi_ctl_reg, spi_ctl_xch_bitmask);
/* Wait until everything is received in the RX FIFO */ - while ((readl(spi_fifo_reg) & 0x7F) < 4 + bufsize) + while ((readl(spi_fifo_reg) & 0x7F) < txlen + rxlen) ;
- /* Skip 4 bytes */ - readl(spi_rx_reg); + /* Skip txlen bytes */ + for (u32 i = 0; i < txlen; i++) + readb(spi_rx_reg);
/* Read the data */ - while (bufsize-- > 0) - *buf++ = readb(spi_rx_reg); + while (rxlen-- > 0) + *rxbuf++ = readb(spi_rx_reg); +} + +static void spi0_xfer(const u8 *txbuf, u32 txlen, u8 *rxbuf, u32 rxlen) +{ + uintptr_t base = spi0_base_address();
- /* tSHSL time is up to 100 ns in various SPI flash datasheets */ - udelay(1); + if (is_sun6i_gen_spi()) { + sunxi_spi0_xfer(txbuf, txlen, rxbuf, rxlen, + base + SUN6I_SPI0_TCR, + SUN6I_TCR_XCH, + base + SUN6I_SPI0_FIFO_STA, + base + SUN6I_SPI0_TXD, + base + SUN6I_SPI0_RXD, + base + SUN6I_SPI0_MBC, + base + SUN6I_SPI0_MTC, + base + SUN6I_SPI0_BCC); + } else { + sunxi_spi0_xfer(txbuf, txlen, rxbuf, rxlen, + base + SUN4I_SPI0_CTL, + SUN4I_CTL_XCH, + base + SUN4I_SPI0_FIFO_STA, + base + SUN4I_SPI0_TX, + base + SUN4I_SPI0_RX, + base + SUN4I_SPI0_BC, + base + SUN4I_SPI0_TC, + 0); + } }
static void spi0_read_data(void *buf, u32 addr, u32 len) { u8 *buf8 = buf; u32 chunk_len; - uintptr_t base = spi0_base_address(); + u8 txbuf[4];
while (len > 0) { chunk_len = len; + + /* Configure the Read Data Bytes (03h) command header */ + txbuf[0] = 0x03; + txbuf[1] = (u8)(addr >> 16); + txbuf[2] = (u8)(addr >> 8); + txbuf[3] = (u8)(addr); + if (chunk_len > SPI_READ_MAX_SIZE) chunk_len = SPI_READ_MAX_SIZE;
- if (is_sun6i_gen_spi()) { - sunxi_spi0_read_data(buf8, addr, chunk_len, - base + SUN6I_SPI0_TCR, - SUN6I_TCR_XCH, - base + SUN6I_SPI0_FIFO_STA, - base + SUN6I_SPI0_TXD, - base + SUN6I_SPI0_RXD, - base + SUN6I_SPI0_MBC, - base + SUN6I_SPI0_MTC, - base + SUN6I_SPI0_BCC); - } else { - sunxi_spi0_read_data(buf8, addr, chunk_len, - base + SUN4I_SPI0_CTL, - SUN4I_CTL_XCH, - base + SUN4I_SPI0_FIFO_STA, - base + SUN4I_SPI0_TX, - base + SUN4I_SPI0_RX, - base + SUN4I_SPI0_BC, - base + SUN4I_SPI0_TC, - 0); - } + spi0_xfer(txbuf, 4, buf8, chunk_len); + + /* tSHSL time is up to 100 ns in various SPI flash datasheets */ + udelay(1);
len -= chunk_len; buf8 += chunk_len;

On 10/13/22 22:05, Icenowy Zheng wrote:
To support SPI NAND flashes, more commands than Read (03h) are needed.
Extract the code for doing SPI transfer from the reading code for code reuse.
Signed-off-by: Icenowy Zheng uwu@icenowy.me
One comment below.
Reviewed-by: Samuel Holland samuel@sholland.org Tested-by: Samuel Holland samuel@sholland.org # Orange Pi Zero Plus
arch/arm/mach-sunxi/spl_spi_sunxi.c | 105 ++++++++++++++++------------ 1 file changed, 59 insertions(+), 46 deletions(-)
diff --git a/arch/arm/mach-sunxi/spl_spi_sunxi.c b/arch/arm/mach-sunxi/spl_spi_sunxi.c index 925bf85f2d..7975457758 100644 --- a/arch/arm/mach-sunxi/spl_spi_sunxi.c +++ b/arch/arm/mach-sunxi/spl_spi_sunxi.c @@ -243,77 +243,90 @@ static void spi0_deinit(void)
#define SPI_READ_MAX_SIZE 60 /* FIFO size, minus 4 bytes of the header */
-static void sunxi_spi0_read_data(u8 *buf, u32 addr, u32 bufsize,
ulong spi_ctl_reg,
ulong spi_ctl_xch_bitmask,
ulong spi_fifo_reg,
ulong spi_tx_reg,
ulong spi_rx_reg,
ulong spi_bc_reg,
ulong spi_tc_reg,
ulong spi_bcc_reg)
+static void sunxi_spi0_xfer(const u8 *txbuf, u32 txlen,
u8 *rxbuf, u32 rxlen,
ulong spi_ctl_reg,
ulong spi_ctl_xch_bitmask,
ulong spi_fifo_reg,
ulong spi_tx_reg,
ulong spi_rx_reg,
ulong spi_bc_reg,
ulong spi_tc_reg,
ulong spi_bcc_reg)
{
- writel(4 + bufsize, spi_bc_reg); /* Burst counter (total bytes) */
- writel(4, spi_tc_reg); /* Transfer counter (bytes to send) */
- writel(txlen + rxlen, spi_bc_reg); /* Burst counter (total bytes) */
- writel(txlen, spi_tc_reg); /* Transfer counter (bytes to send) */ if (spi_bcc_reg)
writel(4, spi_bcc_reg); /* SUN6I also needs this */
writel(txlen, spi_bcc_reg); /* SUN6I also needs this */
- /* Send the Read Data Bytes (03h) command header */
- writeb(0x03, spi_tx_reg);
- writeb((u8)(addr >> 16), spi_tx_reg);
- writeb((u8)(addr >> 8), spi_tx_reg);
- writeb((u8)(addr), spi_tx_reg);
- for (u32 i = 0; i < txlen; i++)
writeb(*(txbuf++), spi_tx_reg);
I think txbuf[i] would be a bit clearer here.
Regards, Samuel
/* Start the data transfer */ setbits_le32(spi_ctl_reg, spi_ctl_xch_bitmask);
/* Wait until everything is received in the RX FIFO */
- while ((readl(spi_fifo_reg) & 0x7F) < 4 + bufsize)
- while ((readl(spi_fifo_reg) & 0x7F) < txlen + rxlen) ;
- /* Skip 4 bytes */
- readl(spi_rx_reg);
/* Skip txlen bytes */
for (u32 i = 0; i < txlen; i++)
readb(spi_rx_reg);
/* Read the data */
- while (bufsize-- > 0)
*buf++ = readb(spi_rx_reg);
- while (rxlen-- > 0)
*rxbuf++ = readb(spi_rx_reg);
+}
+static void spi0_xfer(const u8 *txbuf, u32 txlen, u8 *rxbuf, u32 rxlen) +{
- uintptr_t base = spi0_base_address();
- /* tSHSL time is up to 100 ns in various SPI flash datasheets */
- udelay(1);
- if (is_sun6i_gen_spi()) {
sunxi_spi0_xfer(txbuf, txlen, rxbuf, rxlen,
base + SUN6I_SPI0_TCR,
SUN6I_TCR_XCH,
base + SUN6I_SPI0_FIFO_STA,
base + SUN6I_SPI0_TXD,
base + SUN6I_SPI0_RXD,
base + SUN6I_SPI0_MBC,
base + SUN6I_SPI0_MTC,
base + SUN6I_SPI0_BCC);
- } else {
sunxi_spi0_xfer(txbuf, txlen, rxbuf, rxlen,
base + SUN4I_SPI0_CTL,
SUN4I_CTL_XCH,
base + SUN4I_SPI0_FIFO_STA,
base + SUN4I_SPI0_TX,
base + SUN4I_SPI0_RX,
base + SUN4I_SPI0_BC,
base + SUN4I_SPI0_TC,
0);
- }
}
static void spi0_read_data(void *buf, u32 addr, u32 len) { u8 *buf8 = buf; u32 chunk_len;
- uintptr_t base = spi0_base_address();
u8 txbuf[4];
while (len > 0) { chunk_len = len;
/* Configure the Read Data Bytes (03h) command header */
txbuf[0] = 0x03;
txbuf[1] = (u8)(addr >> 16);
txbuf[2] = (u8)(addr >> 8);
txbuf[3] = (u8)(addr);
if (chunk_len > SPI_READ_MAX_SIZE) chunk_len = SPI_READ_MAX_SIZE;
if (is_sun6i_gen_spi()) {
sunxi_spi0_read_data(buf8, addr, chunk_len,
base + SUN6I_SPI0_TCR,
SUN6I_TCR_XCH,
base + SUN6I_SPI0_FIFO_STA,
base + SUN6I_SPI0_TXD,
base + SUN6I_SPI0_RXD,
base + SUN6I_SPI0_MBC,
base + SUN6I_SPI0_MTC,
base + SUN6I_SPI0_BCC);
} else {
sunxi_spi0_read_data(buf8, addr, chunk_len,
base + SUN4I_SPI0_CTL,
SUN4I_CTL_XCH,
base + SUN4I_SPI0_FIFO_STA,
base + SUN4I_SPI0_TX,
base + SUN4I_SPI0_RX,
base + SUN4I_SPI0_BC,
base + SUN4I_SPI0_TC,
0);
}
spi0_xfer(txbuf, 4, buf8, chunk_len);
/* tSHSL time is up to 100 ns in various SPI flash datasheets */
udelay(1);
len -= chunk_len; buf8 += chunk_len;

On Fri, 14 Oct 2022 11:05:13 +0800 Icenowy Zheng uwu@icenowy.me wrote:
Hi,
To support SPI NAND flashes, more commands than Read (03h) are needed.
Extract the code for doing SPI transfer from the reading code for code reuse.
I was looking for a better solution than this inflated function parameter list, but everything I came up with is actually worse. So it's fine like you wrote it here. I think it now even looks a bit better, since that long list is now wrapped completely by the spi0_xfer() function. It increases the code size by 20 (Thumb2) and 28 bytes (AArch64), that's not great, but acceptable.
Signed-off-by: Icenowy Zheng uwu@icenowy.me
With that one array index change that Samuel suggested (I can fix this up myself):
Acked-by: Andre Przywara andre.przywara@arm.com
Cheers, Andre
arch/arm/mach-sunxi/spl_spi_sunxi.c | 105 ++++++++++++++++------------ 1 file changed, 59 insertions(+), 46 deletions(-)
diff --git a/arch/arm/mach-sunxi/spl_spi_sunxi.c b/arch/arm/mach-sunxi/spl_spi_sunxi.c index 925bf85f2d..7975457758 100644 --- a/arch/arm/mach-sunxi/spl_spi_sunxi.c +++ b/arch/arm/mach-sunxi/spl_spi_sunxi.c @@ -243,77 +243,90 @@ static void spi0_deinit(void)
#define SPI_READ_MAX_SIZE 60 /* FIFO size, minus 4 bytes of the header */
-static void sunxi_spi0_read_data(u8 *buf, u32 addr, u32 bufsize,
ulong spi_ctl_reg,
ulong spi_ctl_xch_bitmask,
ulong spi_fifo_reg,
ulong spi_tx_reg,
ulong spi_rx_reg,
ulong spi_bc_reg,
ulong spi_tc_reg,
ulong spi_bcc_reg)
+static void sunxi_spi0_xfer(const u8 *txbuf, u32 txlen,
u8 *rxbuf, u32 rxlen,
ulong spi_ctl_reg,
ulong spi_ctl_xch_bitmask,
ulong spi_fifo_reg,
ulong spi_tx_reg,
ulong spi_rx_reg,
ulong spi_bc_reg,
ulong spi_tc_reg,
ulong spi_bcc_reg)
{
- writel(4 + bufsize, spi_bc_reg); /* Burst counter (total bytes) */
- writel(4, spi_tc_reg); /* Transfer counter (bytes to send) */
- writel(txlen + rxlen, spi_bc_reg); /* Burst counter (total bytes) */
- writel(txlen, spi_tc_reg); /* Transfer counter (bytes to send) */ if (spi_bcc_reg)
writel(4, spi_bcc_reg); /* SUN6I also needs this */
writel(txlen, spi_bcc_reg); /* SUN6I also needs this */
- /* Send the Read Data Bytes (03h) command header */
- writeb(0x03, spi_tx_reg);
- writeb((u8)(addr >> 16), spi_tx_reg);
- writeb((u8)(addr >> 8), spi_tx_reg);
- writeb((u8)(addr), spi_tx_reg);
for (u32 i = 0; i < txlen; i++)
writeb(*(txbuf++), spi_tx_reg);
/* Start the data transfer */ setbits_le32(spi_ctl_reg, spi_ctl_xch_bitmask);
/* Wait until everything is received in the RX FIFO */
- while ((readl(spi_fifo_reg) & 0x7F) < 4 + bufsize)
- while ((readl(spi_fifo_reg) & 0x7F) < txlen + rxlen) ;
- /* Skip 4 bytes */
- readl(spi_rx_reg);
/* Skip txlen bytes */
for (u32 i = 0; i < txlen; i++)
readb(spi_rx_reg);
/* Read the data */
- while (bufsize-- > 0)
*buf++ = readb(spi_rx_reg);
- while (rxlen-- > 0)
*rxbuf++ = readb(spi_rx_reg);
+}
+static void spi0_xfer(const u8 *txbuf, u32 txlen, u8 *rxbuf, u32 rxlen) +{
- uintptr_t base = spi0_base_address();
- /* tSHSL time is up to 100 ns in various SPI flash datasheets */
- udelay(1);
- if (is_sun6i_gen_spi()) {
sunxi_spi0_xfer(txbuf, txlen, rxbuf, rxlen,
base + SUN6I_SPI0_TCR,
SUN6I_TCR_XCH,
base + SUN6I_SPI0_FIFO_STA,
base + SUN6I_SPI0_TXD,
base + SUN6I_SPI0_RXD,
base + SUN6I_SPI0_MBC,
base + SUN6I_SPI0_MTC,
base + SUN6I_SPI0_BCC);
- } else {
sunxi_spi0_xfer(txbuf, txlen, rxbuf, rxlen,
base + SUN4I_SPI0_CTL,
SUN4I_CTL_XCH,
base + SUN4I_SPI0_FIFO_STA,
base + SUN4I_SPI0_TX,
base + SUN4I_SPI0_RX,
base + SUN4I_SPI0_BC,
base + SUN4I_SPI0_TC,
0);
- }
}
static void spi0_read_data(void *buf, u32 addr, u32 len) { u8 *buf8 = buf; u32 chunk_len;
- uintptr_t base = spi0_base_address();
u8 txbuf[4];
while (len > 0) { chunk_len = len;
/* Configure the Read Data Bytes (03h) command header */
txbuf[0] = 0x03;
txbuf[1] = (u8)(addr >> 16);
txbuf[2] = (u8)(addr >> 8);
txbuf[3] = (u8)(addr);
if (chunk_len > SPI_READ_MAX_SIZE) chunk_len = SPI_READ_MAX_SIZE;
if (is_sun6i_gen_spi()) {
sunxi_spi0_read_data(buf8, addr, chunk_len,
base + SUN6I_SPI0_TCR,
SUN6I_TCR_XCH,
base + SUN6I_SPI0_FIFO_STA,
base + SUN6I_SPI0_TXD,
base + SUN6I_SPI0_RXD,
base + SUN6I_SPI0_MBC,
base + SUN6I_SPI0_MTC,
base + SUN6I_SPI0_BCC);
} else {
sunxi_spi0_read_data(buf8, addr, chunk_len,
base + SUN4I_SPI0_CTL,
SUN4I_CTL_XCH,
base + SUN4I_SPI0_FIFO_STA,
base + SUN4I_SPI0_TX,
base + SUN4I_SPI0_RX,
base + SUN4I_SPI0_BC,
base + SUN4I_SPI0_TC,
0);
}
spi0_xfer(txbuf, 4, buf8, chunk_len);
/* tSHSL time is up to 100 ns in various SPI flash datasheets */
udelay(1);
len -= chunk_len; buf8 += chunk_len;

This kind of read command is utilized in SPI NANDs for reading data inside a selected page, which is obviously smaller than how much 2 byte address can address. So 2 bytes are used for the address and one dummy byte is needed after the real address. As the address is sent out in bit endian, this makes it not compatible with usual 3 byte address.
Signed-off-by: Icenowy Zheng uwu@icenowy.me --- arch/arm/mach-sunxi/spl_spi_sunxi.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-)
diff --git a/arch/arm/mach-sunxi/spl_spi_sunxi.c b/arch/arm/mach-sunxi/spl_spi_sunxi.c index 7975457758..88c15a3ee9 100644 --- a/arch/arm/mach-sunxi/spl_spi_sunxi.c +++ b/arch/arm/mach-sunxi/spl_spi_sunxi.c @@ -305,7 +305,7 @@ static void spi0_xfer(const u8 *txbuf, u32 txlen, u8 *rxbuf, u32 rxlen) } }
-static void spi0_read_data(void *buf, u32 addr, u32 len) +static void spi0_read_data(void *buf, u32 addr, u32 len, u32 addr_len) { u8 *buf8 = buf; u32 chunk_len; @@ -316,9 +316,15 @@ static void spi0_read_data(void *buf, u32 addr, u32 len)
/* Configure the Read Data Bytes (03h) command header */ txbuf[0] = 0x03; - txbuf[1] = (u8)(addr >> 16); - txbuf[2] = (u8)(addr >> 8); - txbuf[3] = (u8)(addr); + if (addr_len == 3) { + txbuf[1] = (u8)(addr >> 16); + txbuf[2] = (u8)(addr >> 8); + txbuf[3] = (u8)(addr); + } else if (addr_len == 2) { + txbuf[1] = (u8)(addr >> 8); + txbuf[2] = (u8)(addr); + txbuf[3] = 0; /* dummy */ + }
if (chunk_len > SPI_READ_MAX_SIZE) chunk_len = SPI_READ_MAX_SIZE; @@ -337,7 +343,7 @@ static void spi0_read_data(void *buf, u32 addr, u32 len) static ulong spi_load_read(struct spl_load_info *load, ulong sector, ulong count, void *buf) { - spi0_read_data(buf, sector, count); + spi0_read_data(buf, sector, count, 3);
return count; } @@ -356,7 +362,7 @@ static int spl_spi_load_image(struct spl_image_info *spl_image,
spi0_init();
- spi0_read_data((void *)header, load_offset, 0x40); + spi0_read_data((void *)header, load_offset, 0x40, 3);
if (IS_ENABLED(CONFIG_SPL_LOAD_FIT) && image_get_magic(header) == FDT_MAGIC) { @@ -376,7 +382,7 @@ static int spl_spi_load_image(struct spl_image_info *spl_image, return ret;
spi0_read_data((void *)spl_image->load_addr, - load_offset, spl_image->size); + load_offset, spl_image->size, 3); }
spi0_deinit();

On 10/13/22 22:05, Icenowy Zheng wrote:
This kind of read command is utilized in SPI NANDs for reading data inside a selected page, which is obviously smaller than how much 2 byte address can address. So 2 bytes are used for the address and one dummy byte is needed after the real address. As the address is sent out in bit endian, this makes it not compatible with usual 3 byte address.
typo: big
Signed-off-by: Icenowy Zheng uwu@icenowy.me
arch/arm/mach-sunxi/spl_spi_sunxi.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-)
Reviewed-by: Samuel Holland samuel@sholland.org Tested-by: Samuel Holland samuel@sholland.org # Orange Pi Zero Plus
diff --git a/arch/arm/mach-sunxi/spl_spi_sunxi.c b/arch/arm/mach-sunxi/spl_spi_sunxi.c index 7975457758..88c15a3ee9 100644 --- a/arch/arm/mach-sunxi/spl_spi_sunxi.c +++ b/arch/arm/mach-sunxi/spl_spi_sunxi.c @@ -305,7 +305,7 @@ static void spi0_xfer(const u8 *txbuf, u32 txlen, u8 *rxbuf, u32 rxlen) } }
-static void spi0_read_data(void *buf, u32 addr, u32 len) +static void spi0_read_data(void *buf, u32 addr, u32 len, u32 addr_len) { u8 *buf8 = buf; u32 chunk_len; @@ -316,9 +316,15 @@ static void spi0_read_data(void *buf, u32 addr, u32 len)
/* Configure the Read Data Bytes (03h) command header */ txbuf[0] = 0x03;
txbuf[1] = (u8)(addr >> 16);
txbuf[2] = (u8)(addr >> 8);
txbuf[3] = (u8)(addr);
if (addr_len == 3) {
txbuf[1] = (u8)(addr >> 16);
txbuf[2] = (u8)(addr >> 8);
txbuf[3] = (u8)(addr);
} else if (addr_len == 2) {
txbuf[1] = (u8)(addr >> 8);
txbuf[2] = (u8)(addr);
txbuf[3] = 0; /* dummy */
}
if (chunk_len > SPI_READ_MAX_SIZE) chunk_len = SPI_READ_MAX_SIZE;
@@ -337,7 +343,7 @@ static void spi0_read_data(void *buf, u32 addr, u32 len) static ulong spi_load_read(struct spl_load_info *load, ulong sector, ulong count, void *buf) {
- spi0_read_data(buf, sector, count);
spi0_read_data(buf, sector, count, 3);
return count;
} @@ -356,7 +362,7 @@ static int spl_spi_load_image(struct spl_image_info *spl_image,
spi0_init();
- spi0_read_data((void *)header, load_offset, 0x40);
spi0_read_data((void *)header, load_offset, 0x40, 3);
if (IS_ENABLED(CONFIG_SPL_LOAD_FIT) &&
image_get_magic(header) == FDT_MAGIC) {
@@ -376,7 +382,7 @@ static int spl_spi_load_image(struct spl_image_info *spl_image, return ret;
spi0_read_data((void *)spl_image->load_addr,
load_offset, spl_image->size);
load_offset, spl_image->size, 3);
}
spi0_deinit();

As we're going to add support for SPI NAND to this code, add code that allows multiple boot attempts with different load offsets and functions.
To keep compatibility with loading raw binary on SPI NOR, a bool parameter is used to allow booting without valid magic number when booting with SPI NOR.
Signed-off-by: Icenowy Zheng uwu@icenowy.me --- arch/arm/mach-sunxi/spl_spi_sunxi.c | 58 +++++++++++++++++++---------- 1 file changed, 38 insertions(+), 20 deletions(-)
diff --git a/arch/arm/mach-sunxi/spl_spi_sunxi.c b/arch/arm/mach-sunxi/spl_spi_sunxi.c index 88c15a3ee9..21be33a23f 100644 --- a/arch/arm/mach-sunxi/spl_spi_sunxi.c +++ b/arch/arm/mach-sunxi/spl_spi_sunxi.c @@ -340,8 +340,8 @@ static void spi0_read_data(void *buf, u32 addr, u32 len, u32 addr_len) } }
-static ulong spi_load_read(struct spl_load_info *load, ulong sector, - ulong count, void *buf) +static ulong spi_load_read_nor(struct spl_load_info *load, ulong sector, + ulong count, void *buf) { spi0_read_data(buf, sector, count, 3);
@@ -350,41 +350,59 @@ static ulong spi_load_read(struct spl_load_info *load, ulong sector,
/*****************************************************************************/
-static int spl_spi_load_image(struct spl_image_info *spl_image, - struct spl_boot_device *bootdev) +static int spl_spi_try_load(struct spl_image_info *spl_image, + struct spl_boot_device *bootdev, + struct spl_load_info *load, u32 offset, + bool allow_raw) { int ret = 0; struct legacy_img_hdr *header; - uint32_t load_offset = sunxi_get_spl_size(); - header = (struct legacy_img_hdr *)CONFIG_SYS_TEXT_BASE; - load_offset = max_t(uint32_t, load_offset, CONFIG_SYS_SPI_U_BOOT_OFFS); - - spi0_init();
- spi0_read_data((void *)header, load_offset, 0x40, 3); + if (load->read(load, offset, 0x40, (void *)header) == 0) + return -EINVAL;
if (IS_ENABLED(CONFIG_SPL_LOAD_FIT) && image_get_magic(header) == FDT_MAGIC) { - struct spl_load_info load;
debug("Found FIT image\n"); - load.dev = NULL; - load.priv = NULL; - load.filename = NULL; - load.bl_len = 1; - load.read = spi_load_read; - ret = spl_load_simple_fit(spl_image, &load, - load_offset, header); + ret = spl_load_simple_fit(spl_image, load, + offset, header); } else { + if (!allow_raw && image_get_magic(header) != IH_MAGIC) + return -EINVAL; + ret = spl_parse_image_header(spl_image, bootdev, header); if (ret) return ret;
- spi0_read_data((void *)spl_image->load_addr, - load_offset, spl_image->size, 3); + if (load->read(load, offset, spl_image->size, + (void *)spl_image->load_addr) == 0) + ret = -EINVAL; }
+ return ret; +} + +static int spl_spi_load_image(struct spl_image_info *spl_image, + struct spl_boot_device *bootdev) +{ + int ret = 0; + uint32_t load_offset = sunxi_get_spl_size(); + struct spl_load_info load; + + load_offset = max_t(uint32_t, load_offset, CONFIG_SYS_SPI_U_BOOT_OFFS); + + load.dev = NULL; + load.priv = NULL; + load.filename = NULL; + load.bl_len = 1; + + spi0_init(); + + load.read = spi_load_read_nor; + ret = spl_spi_try_load(spl_image, bootdev, &load, load_offset, true); + spi0_deinit();
return ret;

On 10/13/22 22:05, Icenowy Zheng wrote:
As we're going to add support for SPI NAND to this code, add code that allows multiple boot attempts with different load offsets and functions.
To keep compatibility with loading raw binary on SPI NOR, a bool parameter is used to allow booting without valid magic number when booting with SPI NOR.
So the issue is that when CONFIG_SPL_RAW_IMAGE_SUPPORT=y, then spl_parse_image_header() will return 0 even when using the wrong NAND parameters? I don't see a better solution, so:
Reviewed-by: Samuel Holland samuel@sholland.org Tested-by: Samuel Holland samuel@sholland.org # Orange Pi Zero Plus
Signed-off-by: Icenowy Zheng uwu@icenowy.me
arch/arm/mach-sunxi/spl_spi_sunxi.c | 58 +++++++++++++++++++---------- 1 file changed, 38 insertions(+), 20 deletions(-)
diff --git a/arch/arm/mach-sunxi/spl_spi_sunxi.c b/arch/arm/mach-sunxi/spl_spi_sunxi.c index 88c15a3ee9..21be33a23f 100644 --- a/arch/arm/mach-sunxi/spl_spi_sunxi.c +++ b/arch/arm/mach-sunxi/spl_spi_sunxi.c @@ -340,8 +340,8 @@ static void spi0_read_data(void *buf, u32 addr, u32 len, u32 addr_len) } }
-static ulong spi_load_read(struct spl_load_info *load, ulong sector,
ulong count, void *buf)
+static ulong spi_load_read_nor(struct spl_load_info *load, ulong sector,
ulong count, void *buf)
{ spi0_read_data(buf, sector, count, 3);
@@ -350,41 +350,59 @@ static ulong spi_load_read(struct spl_load_info *load, ulong sector,
/*****************************************************************************/
-static int spl_spi_load_image(struct spl_image_info *spl_image,
struct spl_boot_device *bootdev)
+static int spl_spi_try_load(struct spl_image_info *spl_image,
struct spl_boot_device *bootdev,
struct spl_load_info *load, u32 offset,
bool allow_raw)
{ int ret = 0; struct legacy_img_hdr *header;
- uint32_t load_offset = sunxi_get_spl_size();
nit: keep this blank line
header = (struct legacy_img_hdr *)CONFIG_SYS_TEXT_BASE;
load_offset = max_t(uint32_t, load_offset, CONFIG_SYS_SPI_U_BOOT_OFFS);
spi0_init();
spi0_read_data((void *)header, load_offset, 0x40, 3);
if (load->read(load, offset, 0x40, (void *)header) == 0)
return -EINVAL; if (IS_ENABLED(CONFIG_SPL_LOAD_FIT) &&
image_get_magic(header) == FDT_MAGIC) {
struct spl_load_info load;
debug("Found FIT image\n");
load.dev = NULL;
load.priv = NULL;
load.filename = NULL;
load.bl_len = 1;
load.read = spi_load_read;
ret = spl_load_simple_fit(spl_image, &load,
load_offset, header);
ret = spl_load_simple_fit(spl_image, load,
} else {offset, header);
if (!allow_raw && image_get_magic(header) != IH_MAGIC)
return -EINVAL;
- ret = spl_parse_image_header(spl_image, bootdev, header); if (ret) return ret;
spi0_read_data((void *)spl_image->load_addr,
load_offset, spl_image->size, 3);
if (load->read(load, offset, spl_image->size,
(void *)spl_image->load_addr) == 0)
ret = -EINVAL;
}
return ret;
+}
+static int spl_spi_load_image(struct spl_image_info *spl_image,
struct spl_boot_device *bootdev)
+{
int ret = 0;
uint32_t load_offset = sunxi_get_spl_size();
struct spl_load_info load;
load_offset = max_t(uint32_t, load_offset, CONFIG_SYS_SPI_U_BOOT_OFFS);
load.dev = NULL;
load.priv = NULL;
load.filename = NULL;
load.bl_len = 1;
spi0_init();
load.read = spi_load_read_nor;
ret = spl_spi_try_load(spl_image, bootdev, &load, load_offset, true);
spi0_deinit();
return ret;

于 2023年1月15日 GMT+08:00 上午3:56:08, Samuel Holland samuel@sholland.org 写到:
On 10/13/22 22:05, Icenowy Zheng wrote:
As we're going to add support for SPI NAND to this code, add code that allows multiple boot attempts with different load offsets and functions.
To keep compatibility with loading raw binary on SPI NOR, a bool parameter is used to allow booting without valid magic number when booting with SPI NOR.
So the issue is that when CONFIG_SPL_RAW_IMAGE_SUPPORT=y, then spl_parse_image_header() will return 0 even when using the wrong NAND parameters? I don't see a better solution, so:
Good point, maybe the next patch needs to add some dependency of raw image support to SPI NAND support option.
Reviewed-by: Samuel Holland samuel@sholland.org Tested-by: Samuel Holland samuel@sholland.org # Orange Pi Zero Plus
Signed-off-by: Icenowy Zheng uwu@icenowy.me
arch/arm/mach-sunxi/spl_spi_sunxi.c | 58 +++++++++++++++++++---------- 1 file changed, 38 insertions(+), 20 deletions(-)
diff --git a/arch/arm/mach-sunxi/spl_spi_sunxi.c b/arch/arm/mach-sunxi/spl_spi_sunxi.c index 88c15a3ee9..21be33a23f 100644 --- a/arch/arm/mach-sunxi/spl_spi_sunxi.c +++ b/arch/arm/mach-sunxi/spl_spi_sunxi.c @@ -340,8 +340,8 @@ static void spi0_read_data(void *buf, u32 addr, u32 len, u32 addr_len) } }
-static ulong spi_load_read(struct spl_load_info *load, ulong sector,
ulong count, void *buf)
+static ulong spi_load_read_nor(struct spl_load_info *load, ulong sector,
ulong count, void *buf)
{ spi0_read_data(buf, sector, count, 3);
@@ -350,41 +350,59 @@ static ulong spi_load_read(struct spl_load_info *load, ulong sector,
/*****************************************************************************/
-static int spl_spi_load_image(struct spl_image_info *spl_image,
struct spl_boot_device *bootdev)
+static int spl_spi_try_load(struct spl_image_info *spl_image,
struct spl_boot_device *bootdev,
struct spl_load_info *load, u32 offset,
bool allow_raw)
{ int ret = 0; struct legacy_img_hdr *header;
- uint32_t load_offset = sunxi_get_spl_size();
nit: keep this blank line
header = (struct legacy_img_hdr *)CONFIG_SYS_TEXT_BASE;
load_offset = max_t(uint32_t, load_offset, CONFIG_SYS_SPI_U_BOOT_OFFS);
spi0_init();
spi0_read_data((void *)header, load_offset, 0x40, 3);
if (load->read(load, offset, 0x40, (void *)header) == 0)
return -EINVAL; if (IS_ENABLED(CONFIG_SPL_LOAD_FIT) &&
image_get_magic(header) == FDT_MAGIC) {
struct spl_load_info load;
debug("Found FIT image\n");
load.dev = NULL;
load.priv = NULL;
load.filename = NULL;
load.bl_len = 1;
load.read = spi_load_read;
ret = spl_load_simple_fit(spl_image, &load,
load_offset, header);
ret = spl_load_simple_fit(spl_image, load,
} else {offset, header);
if (!allow_raw && image_get_magic(header) != IH_MAGIC)
return -EINVAL;
- ret = spl_parse_image_header(spl_image, bootdev, header); if (ret) return ret;
spi0_read_data((void *)spl_image->load_addr,
load_offset, spl_image->size, 3);
if (load->read(load, offset, spl_image->size,
(void *)spl_image->load_addr) == 0)
ret = -EINVAL;
}
return ret;
+}
+static int spl_spi_load_image(struct spl_image_info *spl_image,
struct spl_boot_device *bootdev)
+{
int ret = 0;
uint32_t load_offset = sunxi_get_spl_size();
struct spl_load_info load;
load_offset = max_t(uint32_t, load_offset, CONFIG_SYS_SPI_U_BOOT_OFFS);
load.dev = NULL;
load.priv = NULL;
load.filename = NULL;
load.bl_len = 1;
spi0_init();
load.read = spi_load_read_nor;
ret = spl_spi_try_load(spl_image, bootdev, &load, load_offset, true);
spi0_deinit();
return ret;

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 uwu@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;

On 10/13/22 22:05, Icenowy Zheng wrote:
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).
One warning generated when SPL_SPI_SUNXI_NAND is disabled. Otherwise, it looks fine to me. I verified that NOR booting still works when SPL_SPI_SUNXI_NAND is enabled, so:
Tested-by: Samuel Holland samuel@sholland.org # Orange Pi Zero Plus
Signed-off-by: Icenowy Zheng uwu@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:
arch/arm/mach-sunxi/spl_spi_sunxi.c: In function 'spl_spi_load_image': arch/arm/mach-sunxi/spl_spi_sunxi.c:482:1: warning: label 'out' defined but not used [-Wunused-label] 482 | out: | ^~~
spi0_deinit();
return ret;

As we added support for SPI NAND to the existing SPL SPI codepath, route the boot code to it when it detects the BROM loads SPL from SPI NAND, as for SoCs with both SPI NAND and boot media indicator support, the boot media indicator is the same for SPI NOR and NAND.
Signed-off-by: Icenowy Zheng uwu@icenowy.me --- arch/arm/mach-sunxi/board.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/arch/arm/mach-sunxi/board.c b/arch/arm/mach-sunxi/board.c index 220ed80ba7..3a81743e8f 100644 --- a/arch/arm/mach-sunxi/board.c +++ b/arch/arm/mach-sunxi/board.c @@ -210,12 +210,10 @@ static int suniv_get_boot_source(void) case SUNIV_BOOTED_FROM_MMC0: return SUNXI_BOOTED_FROM_MMC0; case SUNIV_BOOTED_FROM_SPI: + case SUNIV_BOOTED_FROM_NAND: return SUNXI_BOOTED_FROM_SPI; case SUNIV_BOOTED_FROM_MMC1: return SUNXI_BOOTED_FROM_MMC2; - /* SPI NAND is not supported yet. */ - case SUNIV_BOOTED_FROM_NAND: - return SUNXI_INVALID_BOOT_SOURCE; } /* If we get here something went wrong try to boot from FEL.*/ printf("Unknown boot source from BROM: 0x%x\n", brom_call);

On 10/13/22 22:05, Icenowy Zheng wrote:
As we added support for SPI NAND to the existing SPL SPI codepath, route the boot code to it when it detects the BROM loads SPL from SPI NAND, as for SoCs with both SPI NAND and boot media indicator support, the boot media indicator is the same for SPI NOR and NAND.
Signed-off-by: Icenowy Zheng uwu@icenowy.me
arch/arm/mach-sunxi/board.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-)
Reviewed-by: Samuel Holland samuel@sholland.org
diff --git a/arch/arm/mach-sunxi/board.c b/arch/arm/mach-sunxi/board.c index 220ed80ba7..3a81743e8f 100644 --- a/arch/arm/mach-sunxi/board.c +++ b/arch/arm/mach-sunxi/board.c @@ -210,12 +210,10 @@ static int suniv_get_boot_source(void) case SUNIV_BOOTED_FROM_MMC0: return SUNXI_BOOTED_FROM_MMC0; case SUNIV_BOOTED_FROM_SPI:
- case SUNIV_BOOTED_FROM_NAND: return SUNXI_BOOTED_FROM_SPI; case SUNIV_BOOTED_FROM_MMC1: return SUNXI_BOOTED_FROM_MMC2;
- /* SPI NAND is not supported yet. */
- case SUNIV_BOOTED_FROM_NAND:
} /* If we get here something went wrong try to boot from FEL.*/ printf("Unknown boot source from BROM: 0x%x\n", brom_call);return SUNXI_INVALID_BOOT_SOURCE;

Signed-off-by: Icenowy Zheng uwu@icenowy.me --- arch/arm/dts/Makefile | 3 +- arch/arm/dts/suniv-f1c100s-licheepi-nano.dts | 16 +++ arch/arm/dts/suniv-f1c100s.dtsi | 26 +++++ arch/arm/dts/suniv-f1c200s-popstick-v1.1.dts | 101 +++++++++++++++++++ 4 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 arch/arm/dts/suniv-f1c200s-popstick-v1.1.dts
diff --git a/arch/arm/dts/Makefile b/arch/arm/dts/Makefile index 9b00b64509..ef7fff3559 100644 --- a/arch/arm/dts/Makefile +++ b/arch/arm/dts/Makefile @@ -529,7 +529,8 @@ dtb-$(CONFIG_STM32H7) += stm32h743i-disco.dtb \ stm32h750i-art-pi.dtb
dtb-$(CONFIG_MACH_SUNIV) += \ - suniv-f1c100s-licheepi-nano.dtb + suniv-f1c100s-licheepi-nano.dtb \ + suniv-f1c200s-popstick-v1.1.dtb dtb-$(CONFIG_MACH_SUN4I) += \ sun4i-a10-a1000.dtb \ sun4i-a10-ba10-tvbox.dtb \ diff --git a/arch/arm/dts/suniv-f1c100s-licheepi-nano.dts b/arch/arm/dts/suniv-f1c100s-licheepi-nano.dts index 04e59b8381..1935d8c885 100644 --- a/arch/arm/dts/suniv-f1c100s-licheepi-nano.dts +++ b/arch/arm/dts/suniv-f1c100s-licheepi-nano.dts @@ -6,6 +6,8 @@ /dts-v1/; #include "suniv-f1c100s.dtsi"
+#include <dt-bindings/gpio/gpio.h> + / { model = "Lichee Pi Nano"; compatible = "licheepi,licheepi-nano", "allwinner,suniv-f1c100s"; @@ -50,8 +52,22 @@ }; };
+&otg_sram { + status = "okay"; +}; + &uart0 { pinctrl-names = "default"; pinctrl-0 = <&uart0_pe_pins>; status = "okay"; }; + +&usb_otg { + dr_mode = "otg"; + status = "okay"; +}; + +&usbphy { + usb0_id_det-gpio = <&pio 4 2 GPIO_ACTIVE_HIGH>; /* PE2 */ + status = "okay"; +}; diff --git a/arch/arm/dts/suniv-f1c100s.dtsi b/arch/arm/dts/suniv-f1c100s.dtsi index bc563c12e9..6d7b120da2 100644 --- a/arch/arm/dts/suniv-f1c100s.dtsi +++ b/arch/arm/dts/suniv-f1c100s.dtsi @@ -133,6 +133,32 @@ #size-cells = <0>; };
+ usb_otg: usb@1c13000 { + compatible = "allwinner,suniv-f1c100s-musb"; + reg = <0x01c13000 0x0400>; + clocks = <&ccu CLK_BUS_OTG>; + resets = <&ccu RST_BUS_OTG>; + interrupts = <26>; + interrupt-names = "mc"; + phys = <&usbphy 0>; + phy-names = "usb"; + extcon = <&usbphy 0>; + allwinner,sram = <&otg_sram 1>; + status = "disabled"; + }; + + usbphy: phy@1c13400 { + compatible = "allwinner,suniv-f1c100s-usb-phy"; + reg = <0x01c13400 0x10>; + reg-names = "phy_ctrl"; + clocks = <&ccu CLK_USB_PHY0>; + clock-names = "usb0_phy"; + resets = <&ccu RST_USB_PHY0>; + reset-names = "usb0_reset"; + #phy-cells = <1>; + status = "disabled"; + }; + ccu: clock@1c20000 { compatible = "allwinner,suniv-f1c100s-ccu"; reg = <0x01c20000 0x400>; diff --git a/arch/arm/dts/suniv-f1c200s-popstick-v1.1.dts b/arch/arm/dts/suniv-f1c200s-popstick-v1.1.dts new file mode 100644 index 0000000000..121dfc6f60 --- /dev/null +++ b/arch/arm/dts/suniv-f1c200s-popstick-v1.1.dts @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2022 Icenowy Zheng uwu@icenowy.me + */ + +/dts-v1/; +#include "suniv-f1c100s.dtsi" + +#include <dt-bindings/gpio/gpio.h> +#include <dt-bindings/leds/common.h> + +/ { + model = "Popcorn Computer PopStick v1.1"; + compatible = "sourceparts,popstick-v1.1", "sourceparts,popstick", + "allwinner,suniv-f1c200s", "allwinner,suniv-f1c100s"; + + aliases { + mmc0 = &mmc0; + serial0 = &uart0; + spi0 = &spi0; + }; + + chosen { + stdout-path = "serial0:115200n8"; + }; + + leds { + compatible = "gpio-leds"; + + led { + function = LED_FUNCTION_STATUS; + color = <LED_COLOR_ID_GREEN>; + gpios = <&pio 4 6 GPIO_ACTIVE_HIGH>; /* PE6 */ + linux,default-trigger = "heartbeat"; + }; + }; + + reg_vcc3v3: vcc3v3 { + compatible = "regulator-fixed"; + regulator-name = "vcc3v3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; +}; + +&mmc0 { + cd-gpios = <&pio 4 3 GPIO_ACTIVE_LOW>; /* PE3 */ + bus-width = <4>; + disable-wp; + status = "okay"; + vmmc-supply = <®_vcc3v3>; +}; + +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi0_pc_pins>; + status = "okay"; + + flash@0 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "spi-nand"; + reg = <0>; + spi-max-frequency = <40000000>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "u-boot-with-spl"; + reg = <0x0 0x100000>; + }; + + ubi@100000 { + label = "ubi"; + reg = <0x100000 0x7f00000>; + }; + }; + }; +}; + +&otg_sram { + status = "okay"; +}; + +&uart0 { + pinctrl-names = "default"; + pinctrl-0 = <&uart0_pe_pins>; + status = "okay"; +}; + +&usb_otg { + dr_mode = "peripheral"; + status = "okay"; +}; + +&usbphy { + status = "okay"; +};

Signed-off-by: Icenowy Zheng uwu@icenowy.me --- board/sunxi/board.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/board/sunxi/board.c b/board/sunxi/board.c index 21a2407e06..f4138573d4 100644 --- a/board/sunxi/board.c +++ b/board/sunxi/board.c @@ -133,6 +133,7 @@ void i2c_init_board(void) */ enum env_location env_get_location(enum env_operation op, int prio) { + return prio ? ENVL_UNKNOWN : ENVL_UBI; if (prio > 1) return ENVL_UNKNOWN;

在 2022-10-14星期五的 11:05 +0800,Icenowy Zheng写道:
Signed-off-by: Icenowy Zheng uwu@icenowy.me
By the way should we have some better way to handle the placement of environments?
board/sunxi/board.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/board/sunxi/board.c b/board/sunxi/board.c index 21a2407e06..f4138573d4 100644 --- a/board/sunxi/board.c +++ b/board/sunxi/board.c @@ -133,6 +133,7 @@ void i2c_init_board(void) */ enum env_location env_get_location(enum env_operation op, int prio) { + return prio ? ENVL_UNKNOWN : ENVL_UBI; if (prio > 1) return ENVL_UNKNOWN;

--- configs/popstick_defconfig | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 configs/popstick_defconfig
diff --git a/configs/popstick_defconfig b/configs/popstick_defconfig new file mode 100644 index 0000000000..6dc21695b7 --- /dev/null +++ b/configs/popstick_defconfig @@ -0,0 +1,35 @@ +CONFIG_ARM=y +CONFIG_ARCH_SUNXI=y +CONFIG_SYS_MALLOC_LEN=0x120000 +CONFIG_ENV_SIZE=0x1f000 +CONFIG_DEFAULT_DEVICE_TREE="suniv-f1c200s-popstick-v1.1" +CONFIG_SPL=y +CONFIG_MACH_SUNIV=y +CONFIG_DRAM_CLK=156 +CONFIG_DRAM_ZQ=0 +CONFIG_SUNXI_MINIMUM_DRAM_MB=64 +CONFIG_MMC0_CD_PIN="PE3" +# CONFIG_VIDEO_SUNXI is not set +CONFIG_SPL_SPI_SUNXI=y +CONFIG_SPL_SPI_SUNXI_NAND=y +CONFIG_SPL_SPI_SUNXI_NAND_ASSUMED_PAGESIZE=0x800 +CONFIG_SPL_STACK=0x8000 +CONFIG_SYS_SPI_U_BOOT_OFFS=0x20000 +CONFIG_CMD_MTD=y +# CONFIG_CMD_SF is not set +CONFIG_CMD_MTDPARTS=y +CONFIG_CMD_UBI=y +# CONFIG_ENV_IS_IN_FAT is not set +# CONFIG_ENV_IS_IN_SPI_FLASH is not set +CONFIG_ENV_IS_IN_UBI=y +CONFIG_ENV_UBI_PART="ubi" +CONFIG_ENV_UBI_VOLUME="env" +# CONFIG_NET is not set +CONFIG_MTD=y +CONFIG_DM_MTD=y +CONFIG_MTD_SPI_NAND=y +# CONFIG_SPI_FLASH is not set +CONFIG_SF_DEFAULT_SPEED=25000000 +# CONFIG_UBI_SILENCE_MSG is not set +CONFIG_SPI=y +# CONFIG_UBIFS_SILENCE_MSG is not set

Hey hey Icenowy,
On 10/13/22 21:05, Icenowy Zheng wrote:
This patchset tries to extend SPI-based boot code in sunxi SPL to support SPI NAND, following the same principle with current SPI NOR code (mimicking the behavior of sunxi BROM).
Thank you for your work on this patchset; it works great on T113 as well! Note that I have had to apply an extra patch to resolve a bootloop of "Unknown boot source 4" (find the patch below).
Also, since on both SUNXI and SUNIV the SPL knows whether it came from NOR or NAND, perhaps it would be better to break apart BOOT_DEVICE_SPI/spl_spi_load_image into specific NOR and NAND variants, as opposed to the current try-NAND-then-NOR approach?
Cheers (and thanks again), Sam
--
diff --git a/arch/arm/include/asm/arch-sunxi/spl.h b/arch/arm/include/asm/arch-sunxi/spl.h index 14944a20ea..703aa70a49 100644 --- a/arch/arm/include/asm/arch-sunxi/spl.h +++ b/arch/arm/include/asm/arch-sunxi/spl.h @@ -12,10 +12,11 @@ #define SPL_ADDR CONFIG_SUNXI_SRAM_ADDRESS
/* The low 8-bits of the 'boot_media' field in the SPL header */ -#define SUNXI_BOOTED_FROM_MMC0 0 -#define SUNXI_BOOTED_FROM_NAND 1 -#define SUNXI_BOOTED_FROM_MMC2 2 -#define SUNXI_BOOTED_FROM_SPI 3 +#define SUNXI_BOOTED_FROM_MMC0 0 +#define SUNXI_BOOTED_FROM_NAND 1 +#define SUNXI_BOOTED_FROM_MMC2 2 +#define SUNXI_BOOTED_FROM_SPI 3 +#define SUNXI_BOOTED_FROM_SPI_NAND 4 #define SUNXI_BOOTED_FROM_MMC0_HIGH 0x10 #define SUNXI_BOOTED_FROM_MMC2_HIGH 0x12
diff --git a/arch/arm/mach-sunxi/board.c b/arch/arm/mach-sunxi/board.c index a48f5aaa05..9bbd0753e6 100644 --- a/arch/arm/mach-sunxi/board.c +++ b/arch/arm/mach-sunxi/board.c @@ -298,6 +298,7 @@ uint32_t sunxi_get_boot_device(void) case SUNXI_BOOTED_FROM_MMC2_HIGH: return BOOT_DEVICE_MMC2; case SUNXI_BOOTED_FROM_SPI: + case SUNXI_BOOTED_FROM_SPI_NAND: return BOOT_DEVICE_SPI; }

在 2023-06-05星期一的 15:03 -0600,Sam Edwards写道:
Hey hey Icenowy,
On 10/13/22 21:05, Icenowy Zheng wrote:
This patchset tries to extend SPI-based boot code in sunxi SPL to support SPI NAND, following the same principle with current SPI NOR code (mimicking the behavior of sunxi BROM).
Thank you for your work on this patchset; it works great on T113 as well! Note that I have had to apply an extra patch to resolve a bootloop of "Unknown boot source 4" (find the patch below).
Also, since on both SUNXI and SUNIV the SPL knows whether it came from NOR or NAND, perhaps it would be better to break apart BOOT_DEVICE_SPI/spl_spi_load_image into specific NOR and NAND variants, as opposed to the current try-NAND-then-NOR approach?
Well it depends on whether all SoCs differienate between SPI NOR and SPI NAND.
Do you have any suggestion on as many chips models as possible boards with SPI NAND?
Cheers (and thanks again), Sam
--
diff --git a/arch/arm/include/asm/arch-sunxi/spl.h b/arch/arm/include/asm/arch-sunxi/spl.h index 14944a20ea..703aa70a49 100644 --- a/arch/arm/include/asm/arch-sunxi/spl.h +++ b/arch/arm/include/asm/arch-sunxi/spl.h @@ -12,10 +12,11 @@ #define SPL_ADDR CONFIG_SUNXI_SRAM_ADDRESS
/* The low 8-bits of the 'boot_media' field in the SPL header */ -#define SUNXI_BOOTED_FROM_MMC0 0 -#define SUNXI_BOOTED_FROM_NAND 1 -#define SUNXI_BOOTED_FROM_MMC2 2 -#define SUNXI_BOOTED_FROM_SPI 3 +#define SUNXI_BOOTED_FROM_MMC0 0 +#define SUNXI_BOOTED_FROM_NAND 1 +#define SUNXI_BOOTED_FROM_MMC2 2 +#define SUNXI_BOOTED_FROM_SPI 3 +#define SUNXI_BOOTED_FROM_SPI_NAND 4 #define SUNXI_BOOTED_FROM_MMC0_HIGH 0x10 #define SUNXI_BOOTED_FROM_MMC2_HIGH 0x12
diff --git a/arch/arm/mach-sunxi/board.c b/arch/arm/mach- sunxi/board.c index a48f5aaa05..9bbd0753e6 100644 --- a/arch/arm/mach-sunxi/board.c +++ b/arch/arm/mach-sunxi/board.c @@ -298,6 +298,7 @@ uint32_t sunxi_get_boot_device(void) case SUNXI_BOOTED_FROM_MMC2_HIGH: return BOOT_DEVICE_MMC2; case SUNXI_BOOTED_FROM_SPI: + case SUNXI_BOOTED_FROM_SPI_NAND: return BOOT_DEVICE_SPI; }

On 6/6/23 00:39, Icenowy Zheng wrote:
Well it depends on whether all SoCs differienate between SPI NOR and SPI NAND.
Allwinner chips that (have datasheets saying they) support boot from SPI-NAND seem quite few and far between, but I've learned that the answer is "not all": the V3s, at least, appears to use 0x03 in the boot field when booting from SPI-NAND. (Note: I do not have a V3s that I tested with; I studied a dumped BROM.)
Still, I believe it's sensible that, when we know for sure we're coming from SPI-NAND (because it's a newer sunxi that reports 0x04, or we know from the suniv stack-checking hack), we should call that its own SPL load method, which does not fall back to SPI-NOR. The SPI(-NOR) load method naturally has to implement the try-NAND-first logic for some of these SoCs, but perhaps we could call it a "quirk" and only do that for chips that aren't known to report SPI-NAND directly?
Do you have any suggestion on as many chips models as possible boards with SPI NAND?
I know of only one: the Turing Pi 2, which is the only target I'm trying to support. I'm guessing you're looking for some such boards so you can identify which of them report 0x03 and which report 0x04? I'd be happy to analyze some BROMs from chips known to support SPI-NAND boot if you know where I can find them.
Thanks, Sam

Hi again Icenowy,
On 6/6/23 17:09, Sam Edwards wrote:
Still, I believe it's sensible that, when we know for sure we're coming from SPI-NAND (because it's a newer sunxi that reports 0x04, or we know from the suniv stack-checking hack), we should call that its own SPL load method, which does not fall back to SPI-NOR. The SPI(-NOR) load method naturally has to implement the try-NAND-first logic for some of these SoCs, but perhaps we could call it a "quirk" and only do that for chips that aren't known to report SPI-NAND directly?
Since other methods that care about flash type (e.g. env_get_location) don't understand BOOT_DEVICE_SPI to mean "SPI, but the actual flash type is ambiguous," I'm starting to think we need to resolve the ambiguity *before* the load method runs.
What are your thoughts on this: 1. Introduce SUNXI_BOOTED_FROM_SPI_NAND, value 4 (matching the value used by Allwinner's newer BROMs). 2. Map SUNIV_BOOTED_FROM_NAND to SUNXI_BOOTED_FROM_SPI_NAND. 3. Map SUNXI_BOOTED_FROM_SPI_NAND to BOOT_DEVICE_NAND (and not _SPI). 4. Implement the SPI-NAND stuff to provide `nand_spl_load_image` (so that the common SPL framework's NAND loader can take care of it). Possibly go even further and implement the methods needed by the UBI loader too. 5. On sunxis (e.g. V3s) which are known to report SUNXI_BOOTED_FROM_SPI for SPI-NAND, add a small check to sunxi_get_boot_device() which disambiguates this situation by probing for SPI-NAND.
If #4 isn't really feasible, we might instead want to introduce a BOOT_DEVICE_SPI_NAND, but I don't know if adding sunxi to the list of platforms that have their own BOOT_DEVICE_ names is a great call.
In any case, I'd hope also to see some kind of bad block handling in here -- a simple check for a bad block each time we get to page 0 (which skips to the next block) should be pretty easy, don't you think?
Any thoughts? Any other way I can be of service? (Feel free to delegate some of these ideas to me if you think they're good but don't have the time to execute them!)
Warm regards, Sam

Hello,
I've used this code extensively, incorporated it in to an RFC branch of mine during development and reviewed it in the process.
John.
Reviewed-by: John Watts contact@jookia.org Tested-by: John Watts contact@jookia.org
On Fri, Oct 14, 2022 at 11:05:12AM +0800, Icenowy Zheng wrote:
This patchset tries to extend SPI-based boot code in sunxi SPL to support SPI NAND, following the same principle with current SPI NOR code (mimicking the behavior of sunxi BROM). In addition, as part of test to this patchset, some patches for Source Parts Inc. PopStick is attached, although marked DO NOT MERGE because the DT should come from Linux after it's ready.
To keep thr code that accesses SPI NAND as simple as possible, it assumes fixed page size, which is also what sunxi BROM does. The SUNIV SPL assumes 0x400 page size, but here to utilize the space better, in the attached example of PopStick, U-Boot main part is assumed to be with 0x800 page size (which is the real situation of the W25N01 flash used by PopStick).
Icenowy Zheng (8): sunxi: SPL SPI: extract code for doing SPI transfer sunxi: SPL SPI: add support for read command with 2 byte address sunxi: SPL SPI: allow multiple boot attempt sunxi: SPL SPI: add initial support for booting from SPI NAND sunxi: enable support for SPI NAND booting on SUNIV [DO NOT MERGE] sunxi: sync DT from my tree for PopStick [DO NOT MERGE, DIRTY HACK] sunxi: use UBI for environement storage [DO NOT MERGE] sunxi: add a defconfig for PopStick
arch/arm/dts/Makefile | 3 +- arch/arm/dts/suniv-f1c100s-licheepi-nano.dts | 16 ++ arch/arm/dts/suniv-f1c100s.dtsi | 26 ++ arch/arm/dts/suniv-f1c200s-popstick-v1.1.dts | 101 ++++++++ arch/arm/mach-sunxi/Kconfig | 16 ++ arch/arm/mach-sunxi/board.c | 4 +- arch/arm/mach-sunxi/spl_spi_sunxi.c | 247 ++++++++++++++----- board/sunxi/board.c | 1 + configs/popstick_defconfig | 35 +++ 9 files changed, 377 insertions(+), 72 deletions(-) create mode 100644 arch/arm/dts/suniv-f1c200s-popstick-v1.1.dts create mode 100644 configs/popstick_defconfig
-- 2.37.1
participants (5)
-
Andre Przywara
-
Icenowy Zheng
-
John Watts
-
Sam Edwards
-
Samuel Holland