From 2472b31997a3343f5be14b7d3c5549010ea83b1a Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Wed, 15 Apr 2026 23:21:36 +0200 Subject: sunxi: use vendor prefix for $fdtfile on RISC-V too Once we complete the support for RISC-V Allwinner D1 (sun20i), we will need to prefix $fdtfile with the vendor prefix to match the Linux device-tree directory structure. Signed-off-by: Heinrich Schuchardt Acked-by: Andre Przywara --- include/configs/sunxi-common.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h index b29a25d5617..49d5617f028 100644 --- a/include/configs/sunxi-common.h +++ b/include/configs/sunxi-common.h @@ -280,7 +280,7 @@ CONSOLE_STDIN_SETTINGS \ CONSOLE_STDOUT_SETTINGS -#ifdef CONFIG_ARM64 +#if defined(CONFIG_ARM64) || defined(CONFIG_RISCV) #define FDTFILE "allwinner/" CONFIG_DEFAULT_DEVICE_TREE ".dtb" #else #define FDTFILE CONFIG_DEFAULT_DEVICE_TREE ".dtb" -- cgit v1.2.3 From dc2427ab20d71258c21379ebc575f05ac9d10141 Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Wed, 8 Apr 2026 00:34:45 +0200 Subject: sunxi: spl: fix SPL_SUNXI_LED active low configuration The newly introduced Allwinner SPL LED "framework" defined a SPL_SUNXI_LED_STATUS_STATE Kconfig symbol, that was supposed to denote the active-low vs. active-high polarity of the LED. However this is a bool symbol, so it will simply vanish if not defined, and we cannot use it directly inside a C statement. Filter the symbol through the IS_ENABLED() macro, which will return 0 if the symbol is not defined, which is the intended value here. Since the STATUS_STATE name is a bit confusing, rename it to ACTIVE_HIGH on the way, because that is its real meaning. Also the LED_STATUS_BIT name for the GPIO number is similarly a remnant of the old status LED code, so rename it to LED_STATUS_GPIO as well. This fixes configuring LEDs with active-low polarity. Fixes: 256557dd9aae ("sunxi: remove usage of legacy LED API") Signed-off-by: Andre Przywara Reviewed-by: Paul Kocialkowski Closes: https://lore.kernel.org/u-boot/adfMQBPdntWy1KIq@shepard/ Acked-by: Quentin Schulz --- arch/arm/mach-sunxi/Kconfig | 10 ++++++---- board/sunxi/board.c | 4 ++-- configs/pinephone_defconfig | 3 +-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig index 89f0e77bcdb..07d7ea2b7ad 100644 --- a/arch/arm/mach-sunxi/Kconfig +++ b/arch/arm/mach-sunxi/Kconfig @@ -1245,15 +1245,17 @@ config SPL_SUNXI_LED_STATUS if SPL_SUNXI_LED_STATUS -config SPL_SUNXI_LED_STATUS_BIT +config SPL_SUNXI_LED_STATUS_GPIO int "GPIO number for GPIO status LED" help GPIO number for the GPIO controlling the GPIO status LED in SPL. -config SPL_SUNXI_LED_STATUS_STATE - bool "GPIO status LED initial state is on" +config SPL_SUNXI_LED_STATUS_ACTIVE_HIGH + bool "GPIO status LED is active high" + default y help - Whether the initial state of the status LED in SPL must be on or off. + Whether the GPIO of the status LED must be set high or low to turn + the LED on. endif # SPL_SUNXI_LED_STATUS diff --git a/board/sunxi/board.c b/board/sunxi/board.c index d7722d1858a..3d1afec7c66 100644 --- a/board/sunxi/board.c +++ b/board/sunxi/board.c @@ -563,8 +563,8 @@ static void sunxi_spl_store_dram_size(phys_addr_t dram_size) static void status_led_init(void) { #if CONFIG_IS_ENABLED(SUNXI_LED_STATUS) - unsigned int state = CONFIG_SPL_SUNXI_LED_STATUS_STATE; - unsigned int gpio = CONFIG_SPL_SUNXI_LED_STATUS_BIT; + unsigned int state = IS_ENABLED(CONFIG_SPL_SUNXI_LED_STATUS_ACTIVE_HIGH); + unsigned int gpio = CONFIG_SPL_SUNXI_LED_STATUS_GPIO; gpio_request(gpio, "gpio_led"); gpio_direction_output(gpio, state); diff --git a/configs/pinephone_defconfig b/configs/pinephone_defconfig index ed7dbc1852b..3a4f4ec51cf 100644 --- a/configs/pinephone_defconfig +++ b/configs/pinephone_defconfig @@ -9,8 +9,7 @@ CONFIG_DRAM_ZQ=3881949 CONFIG_MMC_SUNXI_SLOT_EXTRA=2 CONFIG_PINEPHONE_DT_SELECTION=y CONFIG_SPL_SUNXI_LED_STATUS=y -CONFIG_SPL_SUNXI_LED_STATUS_BIT=114 -CONFIG_SPL_SUNXI_LED_STATUS_STATE=y +CONFIG_SPL_SUNXI_LED_STATUS_GPIO=114 # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_CMD_PSTORE=y CONFIG_CMD_PSTORE_MEM_ADDR=0x61000000 -- cgit v1.2.3 From c3297cb93efb134cabf2138f867a811aaefb4e8a Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Wed, 8 Apr 2026 00:34:46 +0200 Subject: sunxi: configs: enable power LEDs on 32-bit boards We recently gained a simple way to enable a power LED very early in the SPL boot, through simple Kconfig variables. Add those symbols to those boards' defconfigs where the DT indicates a default-on power LED. The number used is * 32 + , an active low setup means CONFIG_SPL_SUNXI_LED_STATUS_ACTIVE_HIGH must be undefined. Since its default is "high", we can skip the symbol in the defconfig in this case. This will light up the power LED very early in the (SPL) boot phase on those 32-bit boards. Signed-off-by: Andre Przywara Reviewed-by: Paul Kocialkowski --- configs/A13-OLinuXinoM_defconfig | 2 ++ configs/Bananapi_M2_Ultra_defconfig | 2 ++ configs/Bananapi_m2m_defconfig | 3 +++ configs/Mele_A1000G_quad_defconfig | 2 ++ configs/Mele_A1000_defconfig | 2 ++ configs/Mele_M9_defconfig | 2 ++ configs/bananapi_m1_plus_defconfig | 2 ++ configs/bananapi_m2_berry_defconfig | 2 ++ configs/bananapi_m2_zero_defconfig | 3 +++ configs/beelink_x2_defconfig | 2 ++ configs/icnova-a20-adb4006_defconfig | 2 ++ configs/nanopi_duo2_defconfig | 2 ++ configs/nanopi_neo_air_defconfig | 2 ++ configs/orangepi_2_defconfig | 2 ++ configs/orangepi_lite_defconfig | 2 ++ configs/orangepi_one_defconfig | 2 ++ configs/orangepi_pc_defconfig | 2 ++ configs/orangepi_zero_defconfig | 2 ++ configs/orangepi_zero_plus2_h3_defconfig | 2 ++ 19 files changed, 40 insertions(+) diff --git a/configs/A13-OLinuXinoM_defconfig b/configs/A13-OLinuXinoM_defconfig index f547635302f..6c5a1b33194 100644 --- a/configs/A13-OLinuXinoM_defconfig +++ b/configs/A13-OLinuXinoM_defconfig @@ -10,6 +10,8 @@ CONFIG_VIDEO_VGA_VIA_LCD=y CONFIG_VIDEO_VGA_VIA_LCD_FORCE_SYNC_ACTIVE_HIGH=y CONFIG_VIDEO_LCD_POWER="PB10" CONFIG_VIDEO_LCD_BL_PWM="PB2" +CONFIG_SPL_SUNXI_LED_STATUS=y +CONFIG_SPL_SUNXI_LED_STATUS_GPIO=201 # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_SYS_I2C_MVTWSI=y CONFIG_SYS_I2C_SLAVE=0x7f diff --git a/configs/Bananapi_M2_Ultra_defconfig b/configs/Bananapi_M2_Ultra_defconfig index 375b0063c1a..750868033be 100644 --- a/configs/Bananapi_M2_Ultra_defconfig +++ b/configs/Bananapi_M2_Ultra_defconfig @@ -5,6 +5,8 @@ CONFIG_DRAM_CLK=576 CONFIG_SPL=y CONFIG_MACH_SUN8I_R40=y CONFIG_MMC_SUNXI_SLOT_EXTRA=2 +CONFIG_SPL_SUNXI_LED_STATUS=y +CONFIG_SPL_SUNXI_LED_STATUS_GPIO=244 # CONFIG_HAS_ARMV7_SECURE_BASE is not set CONFIG_AHCI=y # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set diff --git a/configs/Bananapi_m2m_defconfig b/configs/Bananapi_m2m_defconfig index 0c20cce49ea..f75e3e09d03 100644 --- a/configs/Bananapi_m2m_defconfig +++ b/configs/Bananapi_m2m_defconfig @@ -7,6 +7,9 @@ CONFIG_MACH_SUN8I_A33=y CONFIG_DRAM_ZQ=15291 CONFIG_DRAM_ODT_EN=y CONFIG_MMC_SUNXI_SLOT_EXTRA=2 +CONFIG_SPL_SUNXI_LED_STATUS=y +CONFIG_SPL_SUNXI_LED_STATUS_GPIO=355 +# CONFIG_SPL_SUNXI_LED_STATUS_ACTIVE_HIGH is not set # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_FASTBOOT_CMD_OEM_FORMAT=y CONFIG_USB_EHCI_HCD=y diff --git a/configs/Mele_A1000G_quad_defconfig b/configs/Mele_A1000G_quad_defconfig index 1f4739e0005..ee6c902970a 100644 --- a/configs/Mele_A1000G_quad_defconfig +++ b/configs/Mele_A1000G_quad_defconfig @@ -4,6 +4,8 @@ CONFIG_DEFAULT_DEVICE_TREE="sun6i-a31-mele-a1000g-quad" CONFIG_SPL=y CONFIG_MACH_SUN6I=y CONFIG_DRAM_ZQ=120 +CONFIG_SPL_SUNXI_LED_STATUS=y +CONFIG_SPL_SUNXI_LED_STATUS_GPIO=237 # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_PHY_REALTEK=y CONFIG_ETH_DESIGNWARE=y diff --git a/configs/Mele_A1000_defconfig b/configs/Mele_A1000_defconfig index 93e73ebe3f1..48679265650 100644 --- a/configs/Mele_A1000_defconfig +++ b/configs/Mele_A1000_defconfig @@ -5,6 +5,8 @@ CONFIG_SPL=y CONFIG_MACH_SUN4I=y CONFIG_VIDEO_VGA=y CONFIG_VIDEO_COMPOSITE=y +CONFIG_SPL_SUNXI_LED_STATUS=y +CONFIG_SPL_SUNXI_LED_STATUS_GPIO=244 CONFIG_AHCI=y # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_SPL_I2C=y diff --git a/configs/Mele_M9_defconfig b/configs/Mele_M9_defconfig index 9b1bb97eedd..293e00db1b0 100644 --- a/configs/Mele_M9_defconfig +++ b/configs/Mele_M9_defconfig @@ -4,6 +4,8 @@ CONFIG_DEFAULT_DEVICE_TREE="sun6i-a31-m9" CONFIG_SPL=y CONFIG_MACH_SUN6I=y CONFIG_DRAM_ZQ=120 +CONFIG_SPL_SUNXI_LED_STATUS=y +CONFIG_SPL_SUNXI_LED_STATUS_GPIO=237 # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_PHY_REALTEK=y CONFIG_ETH_DESIGNWARE=y diff --git a/configs/bananapi_m1_plus_defconfig b/configs/bananapi_m1_plus_defconfig index 22b69aa88cb..14ecaf759b5 100644 --- a/configs/bananapi_m1_plus_defconfig +++ b/configs/bananapi_m1_plus_defconfig @@ -6,6 +6,8 @@ CONFIG_SPL=y CONFIG_MACH_SUN7I=y CONFIG_VIDEO_COMPOSITE=y CONFIG_GMAC_TX_DELAY=3 +CONFIG_SPL_SUNXI_LED_STATUS=y +CONFIG_SPL_SUNXI_LED_STATUS_GPIO=249 CONFIG_AHCI=y # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_SPL_I2C=y diff --git a/configs/bananapi_m2_berry_defconfig b/configs/bananapi_m2_berry_defconfig index b406d519684..b9975211362 100644 --- a/configs/bananapi_m2_berry_defconfig +++ b/configs/bananapi_m2_berry_defconfig @@ -4,6 +4,8 @@ CONFIG_DEFAULT_DEVICE_TREE="sun8i-v40-bananapi-m2-berry" CONFIG_DRAM_CLK=576 CONFIG_SPL=y CONFIG_MACH_SUN8I_R40=y +CONFIG_SPL_SUNXI_LED_STATUS=y +CONFIG_SPL_SUNXI_LED_STATUS_GPIO=244 # CONFIG_HAS_ARMV7_SECURE_BASE is not set CONFIG_AHCI=y # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set diff --git a/configs/bananapi_m2_zero_defconfig b/configs/bananapi_m2_zero_defconfig index 337bafecd47..5041802ea8c 100644 --- a/configs/bananapi_m2_zero_defconfig +++ b/configs/bananapi_m2_zero_defconfig @@ -4,4 +4,7 @@ CONFIG_DEFAULT_DEVICE_TREE="sun8i-h2-plus-bananapi-m2-zero" CONFIG_DRAM_CLK=408 CONFIG_SPL=y CONFIG_MACH_SUN8I_H3=y +CONFIG_SPL_SUNXI_LED_STATUS=y +CONFIG_SPL_SUNXI_LED_STATUS_GPIO=362 +# CONFIG_SPL_SUNXI_LED_STATUS_ACTIVE_HIGH is not set # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set diff --git a/configs/beelink_x2_defconfig b/configs/beelink_x2_defconfig index 3b0be26f063..16a37da521d 100644 --- a/configs/beelink_x2_defconfig +++ b/configs/beelink_x2_defconfig @@ -5,6 +5,8 @@ CONFIG_DRAM_CLK=567 CONFIG_SPL=y CONFIG_MACH_SUN8I_H3=y CONFIG_MMC_SUNXI_SLOT_EXTRA=2 +CONFIG_SPL_SUNXI_LED_STATUS=y +CONFIG_SPL_SUNXI_LED_STATUS_GPIO=362 # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_SUN8I_EMAC=y CONFIG_USB_EHCI_HCD=y diff --git a/configs/icnova-a20-adb4006_defconfig b/configs/icnova-a20-adb4006_defconfig index 507e330612e..dcb6457fe71 100644 --- a/configs/icnova-a20-adb4006_defconfig +++ b/configs/icnova-a20-adb4006_defconfig @@ -4,6 +4,8 @@ CONFIG_DEFAULT_DEVICE_TREE="sun7i-a20-icnova-a20-adb4006" CONFIG_DRAM_CLK=384 CONFIG_SPL=y CONFIG_MACH_SUN7I=y +CONFIG_SPL_SUNXI_LED_STATUS=y +CONFIG_SPL_SUNXI_LED_STATUS_GPIO=245 CONFIG_AHCI=y # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_SPL_I2C=y diff --git a/configs/nanopi_duo2_defconfig b/configs/nanopi_duo2_defconfig index 41b5502d24e..9b0fbecff60 100644 --- a/configs/nanopi_duo2_defconfig +++ b/configs/nanopi_duo2_defconfig @@ -5,6 +5,8 @@ CONFIG_DRAM_CLK=408 CONFIG_SPL=y CONFIG_MACH_SUN8I_H3=y # CONFIG_VIDEO_DE2 is not set +CONFIG_SPL_SUNXI_LED_STATUS=y +CONFIG_SPL_SUNXI_LED_STATUS_GPIO=362 # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_CONSOLE_MUX=y CONFIG_USB_EHCI_HCD=y diff --git a/configs/nanopi_neo_air_defconfig b/configs/nanopi_neo_air_defconfig index 3b8cbbfcdba..110bb1e864c 100644 --- a/configs/nanopi_neo_air_defconfig +++ b/configs/nanopi_neo_air_defconfig @@ -5,6 +5,8 @@ CONFIG_DRAM_CLK=408 CONFIG_SPL=y CONFIG_MACH_SUN8I_H3=y # CONFIG_VIDEO_DE2 is not set +CONFIG_SPL_SUNXI_LED_STATUS=y +CONFIG_SPL_SUNXI_LED_STATUS_GPIO=362 # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_CONSOLE_MUX=y CONFIG_USB_EHCI_HCD=y diff --git a/configs/orangepi_2_defconfig b/configs/orangepi_2_defconfig index 88433808538..43f3ab512f1 100644 --- a/configs/orangepi_2_defconfig +++ b/configs/orangepi_2_defconfig @@ -5,6 +5,8 @@ CONFIG_DEFAULT_DEVICE_TREE="sun8i-h3-orangepi-2" CONFIG_DRAM_CLK=672 CONFIG_SPL=y CONFIG_MACH_SUN8I_H3=y +CONFIG_SPL_SUNXI_LED_STATUS=y +CONFIG_SPL_SUNXI_LED_STATUS_GPIO=362 # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_SPL_I2C=y CONFIG_SPL_SYS_I2C_LEGACY=y diff --git a/configs/orangepi_lite_defconfig b/configs/orangepi_lite_defconfig index ea1548f0fcd..499ffd8ace5 100644 --- a/configs/orangepi_lite_defconfig +++ b/configs/orangepi_lite_defconfig @@ -4,6 +4,8 @@ CONFIG_DEFAULT_DEVICE_TREE="sun8i-h3-orangepi-lite" CONFIG_DRAM_CLK=672 CONFIG_SPL=y CONFIG_MACH_SUN8I_H3=y +CONFIG_SPL_SUNXI_LED_STATUS=y +CONFIG_SPL_SUNXI_LED_STATUS_GPIO=362 # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_USB_EHCI_HCD=y CONFIG_USB_OHCI_HCD=y diff --git a/configs/orangepi_one_defconfig b/configs/orangepi_one_defconfig index e4de13de26b..637eb41207e 100644 --- a/configs/orangepi_one_defconfig +++ b/configs/orangepi_one_defconfig @@ -4,6 +4,8 @@ CONFIG_DEFAULT_DEVICE_TREE="sun8i-h3-orangepi-one" CONFIG_DRAM_CLK=672 CONFIG_SPL=y CONFIG_MACH_SUN8I_H3=y +CONFIG_SPL_SUNXI_LED_STATUS=y +CONFIG_SPL_SUNXI_LED_STATUS_GPIO=362 # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_SUN8I_EMAC=y CONFIG_USB_EHCI_HCD=y diff --git a/configs/orangepi_pc_defconfig b/configs/orangepi_pc_defconfig index f857d7fa203..01195b68621 100644 --- a/configs/orangepi_pc_defconfig +++ b/configs/orangepi_pc_defconfig @@ -4,6 +4,8 @@ CONFIG_DEFAULT_DEVICE_TREE="sun8i-h3-orangepi-pc" CONFIG_DRAM_CLK=624 CONFIG_SPL=y CONFIG_MACH_SUN8I_H3=y +CONFIG_SPL_SUNXI_LED_STATUS=y +CONFIG_SPL_SUNXI_LED_STATUS_GPIO=362 # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_SPL_I2C=y CONFIG_SPL_SYS_I2C_LEGACY=y diff --git a/configs/orangepi_zero_defconfig b/configs/orangepi_zero_defconfig index e128f5fcdb3..49a1f4d03da 100644 --- a/configs/orangepi_zero_defconfig +++ b/configs/orangepi_zero_defconfig @@ -6,6 +6,8 @@ CONFIG_SPL=y CONFIG_MACH_SUN8I_H3=y # CONFIG_VIDEO_DE2 is not set CONFIG_SPL_SPI_SUNXI=y +CONFIG_SPL_SUNXI_LED_STATUS=y +CONFIG_SPL_SUNXI_LED_STATUS_GPIO=362 # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_CONSOLE_MUX=y CONFIG_MTD=y diff --git a/configs/orangepi_zero_plus2_h3_defconfig b/configs/orangepi_zero_plus2_h3_defconfig index caf515c7026..63335b404da 100644 --- a/configs/orangepi_zero_plus2_h3_defconfig +++ b/configs/orangepi_zero_plus2_h3_defconfig @@ -6,6 +6,8 @@ CONFIG_SPL=y CONFIG_MACH_SUN8I_H3=y # CONFIG_DRAM_ODT_EN is not set CONFIG_MMC_SUNXI_SLOT_EXTRA=2 +CONFIG_SPL_SUNXI_LED_STATUS=y +CONFIG_SPL_SUNXI_LED_STATUS_GPIO=362 # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_SUN8I_EMAC=y CONFIG_USB_EHCI_HCD=y -- cgit v1.2.3 From 06b3ae910f10c8d13ea733c05ee75cf0a4fec6fd Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Wed, 8 Apr 2026 00:34:47 +0200 Subject: sunxi: configs: enable power LEDs on 64-bit boards We recently gained a simple way to enable a power LED very early in the SPL boot, through simple Kconfig variables. Add those symbols to those boards' defconfigs where the DT indicates a default-on power LED. The number used is * 32 + , an active low setup means CONFIG_SPL_SUNXI_LED_STATUS_ACTIVE_HIGH must be undefined. This will light up the power LED very early in the (SPL) boot phase on those 64-bit boards. Signed-off-by: Andre Przywara Reviewed-by: Paul Kocialkowski --- configs/anbernic_rg35xx_h700_defconfig | 2 ++ configs/bananapi_m64_defconfig | 2 ++ configs/beelink_gs1_defconfig | 2 ++ configs/liontron-h-a133l_defconfig | 3 +++ configs/nanopi_neo2_defconfig | 2 ++ configs/nanopi_neo_plus2_defconfig | 2 ++ configs/orangepi_3_defconfig | 2 ++ configs/orangepi_lite2_defconfig | 2 ++ configs/orangepi_pc2_defconfig | 2 ++ configs/orangepi_prime_defconfig | 2 ++ configs/orangepi_zero2_defconfig | 2 ++ configs/orangepi_zero_plus2_defconfig | 2 ++ configs/orangepi_zero_plus_defconfig | 2 ++ configs/radxa-cubie-a5e_defconfig | 3 +++ configs/tanix_tx1_defconfig | 2 ++ 15 files changed, 32 insertions(+) diff --git a/configs/anbernic_rg35xx_h700_defconfig b/configs/anbernic_rg35xx_h700_defconfig index 2fa57cbecce..2abea94e979 100644 --- a/configs/anbernic_rg35xx_h700_defconfig +++ b/configs/anbernic_rg35xx_h700_defconfig @@ -16,6 +16,8 @@ CONFIG_DRAM_SUNXI_PHY_ADDR_MAP_1=y CONFIG_MACH_SUN50I_H616=y CONFIG_SUNXI_DRAM_H616_LPDDR4=y CONFIG_R_I2C_ENABLE=y +CONFIG_SPL_SUNXI_LED_STATUS=y +CONFIG_SPL_SUNXI_LED_STATUS_GPIO=268 CONFIG_SPL_I2C=y CONFIG_SPL_SYS_I2C_LEGACY=y CONFIG_SYS_I2C_MVTWSI=y diff --git a/configs/bananapi_m64_defconfig b/configs/bananapi_m64_defconfig index d957071cb13..3d1f5d27e5d 100644 --- a/configs/bananapi_m64_defconfig +++ b/configs/bananapi_m64_defconfig @@ -4,6 +4,8 @@ CONFIG_DEFAULT_DEVICE_TREE="sun50i-a64-bananapi-m64" CONFIG_SPL=y CONFIG_MACH_SUN50I=y CONFIG_MMC_SUNXI_SLOT_EXTRA=2 +CONFIG_SPL_SUNXI_LED_STATUS=y +CONFIG_SPL_SUNXI_LED_STATUS_GPIO=120 # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_SUPPORT_EMMC_BOOT=y CONFIG_SUN8I_EMAC=y diff --git a/configs/beelink_gs1_defconfig b/configs/beelink_gs1_defconfig index de46d205453..ebac032915f 100644 --- a/configs/beelink_gs1_defconfig +++ b/configs/beelink_gs1_defconfig @@ -5,6 +5,8 @@ CONFIG_SPL=y CONFIG_MACH_SUN50I_H6=y CONFIG_SUNXI_DRAM_H6_LPDDR3=y CONFIG_MMC_SUNXI_SLOT_EXTRA=2 +CONFIG_SPL_SUNXI_LED_STATUS=y +CONFIG_SPL_SUNXI_LED_STATUS_GPIO=356 # CONFIG_PSCI_RESET is not set # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_LED=y diff --git a/configs/liontron-h-a133l_defconfig b/configs/liontron-h-a133l_defconfig index 831d5b56e3a..836e8d12ff0 100644 --- a/configs/liontron-h-a133l_defconfig +++ b/configs/liontron-h-a133l_defconfig @@ -23,6 +23,9 @@ CONFIG_MACH_SUN50I_A133=y CONFIG_SUNXI_DRAM_A133_LPDDR4=y CONFIG_MMC_SUNXI_SLOT_EXTRA=2 CONFIG_R_I2C_ENABLE=y +CONFIG_SPL_SUNXI_LED_STATUS=y +CONFIG_SPL_SUNXI_LED_STATUS_GPIO=240 +# CONFIG_SPL_SUNXI_LED_STATUS_ACTIVE_HIGH is not set # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_SPL_I2C=y CONFIG_SPL_SYS_I2C_LEGACY=y diff --git a/configs/nanopi_neo2_defconfig b/configs/nanopi_neo2_defconfig index 6704a24a880..63fa6bac9ba 100644 --- a/configs/nanopi_neo2_defconfig +++ b/configs/nanopi_neo2_defconfig @@ -6,6 +6,8 @@ CONFIG_SPL=y CONFIG_MACH_SUN50I_H5=y CONFIG_DRAM_ZQ=3881977 # CONFIG_DRAM_ODT_EN is not set +CONFIG_SPL_SUNXI_LED_STATUS=y +CONFIG_SPL_SUNXI_LED_STATUS_GPIO=362 # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_SUN8I_EMAC=y CONFIG_USB_EHCI_HCD=y diff --git a/configs/nanopi_neo_plus2_defconfig b/configs/nanopi_neo_plus2_defconfig index e80e32a63d4..9d1b783476d 100644 --- a/configs/nanopi_neo_plus2_defconfig +++ b/configs/nanopi_neo_plus2_defconfig @@ -7,6 +7,8 @@ CONFIG_MACH_SUN50I_H5=y CONFIG_DRAM_ZQ=3881977 # CONFIG_DRAM_ODT_EN is not set CONFIG_MMC_SUNXI_SLOT_EXTRA=2 +CONFIG_SPL_SUNXI_LED_STATUS=y +CONFIG_SPL_SUNXI_LED_STATUS_GPIO=362 # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_SUN8I_EMAC=y CONFIG_USB_EHCI_HCD=y diff --git a/configs/orangepi_3_defconfig b/configs/orangepi_3_defconfig index 125137bc321..409fa705a06 100644 --- a/configs/orangepi_3_defconfig +++ b/configs/orangepi_3_defconfig @@ -6,6 +6,8 @@ CONFIG_MACH_SUN50I_H6=y CONFIG_SUNXI_DRAM_H6_LPDDR3=y CONFIG_MMC_SUNXI_SLOT_EXTRA=2 CONFIG_BLUETOOTH_DT_DEVICE_FIXUP="brcm,bcm4345c5" +CONFIG_SPL_SUNXI_LED_STATUS=y +CONFIG_SPL_SUNXI_LED_STATUS_GPIO=356 # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_PHY_SUN50I_USB3=y CONFIG_USB_XHCI_HCD=y diff --git a/configs/orangepi_lite2_defconfig b/configs/orangepi_lite2_defconfig index 577f7436c15..5d72d34eb9d 100644 --- a/configs/orangepi_lite2_defconfig +++ b/configs/orangepi_lite2_defconfig @@ -4,6 +4,8 @@ CONFIG_DEFAULT_DEVICE_TREE="sun50i-h6-orangepi-lite2" CONFIG_SPL=y CONFIG_MACH_SUN50I_H6=y CONFIG_SUNXI_DRAM_H6_LPDDR3=y +CONFIG_SPL_SUNXI_LED_STATUS=y +CONFIG_SPL_SUNXI_LED_STATUS_GPIO=356 # CONFIG_PSCI_RESET is not set # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_USB_EHCI_HCD=y diff --git a/configs/orangepi_pc2_defconfig b/configs/orangepi_pc2_defconfig index d856a25814b..464b34f4ec0 100644 --- a/configs/orangepi_pc2_defconfig +++ b/configs/orangepi_pc2_defconfig @@ -6,6 +6,8 @@ CONFIG_SPL=y CONFIG_MACH_SUN50I_H5=y CONFIG_DRAM_ZQ=3881977 CONFIG_SPL_SPI_SUNXI=y +CONFIG_SPL_SUNXI_LED_STATUS=y +CONFIG_SPL_SUNXI_LED_STATUS_GPIO=362 # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_SPL_I2C=y CONFIG_SPL_SYS_I2C_LEGACY=y diff --git a/configs/orangepi_prime_defconfig b/configs/orangepi_prime_defconfig index e93154cc0e9..e5e9009378c 100644 --- a/configs/orangepi_prime_defconfig +++ b/configs/orangepi_prime_defconfig @@ -6,6 +6,8 @@ CONFIG_SPL=y CONFIG_MACH_SUN50I_H5=y CONFIG_DRAM_ZQ=3881977 # CONFIG_DRAM_ODT_EN is not set +CONFIG_SPL_SUNXI_LED_STATUS=y +CONFIG_SPL_SUNXI_LED_STATUS_GPIO=362 # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_SUN8I_EMAC=y CONFIG_USB_EHCI_HCD=y diff --git a/configs/orangepi_zero2_defconfig b/configs/orangepi_zero2_defconfig index 831bfe66e25..b387b4795ad 100644 --- a/configs/orangepi_zero2_defconfig +++ b/configs/orangepi_zero2_defconfig @@ -10,6 +10,8 @@ CONFIG_MACH_SUN50I_H616=y CONFIG_SUNXI_DRAM_H616_DDR3_1333=y CONFIG_R_I2C_ENABLE=y CONFIG_SPL_SPI_SUNXI=y +CONFIG_SPL_SUNXI_LED_STATUS=y +CONFIG_SPL_SUNXI_LED_STATUS_GPIO=76 # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_SPL_I2C=y CONFIG_SPL_SYS_I2C_LEGACY=y diff --git a/configs/orangepi_zero_plus2_defconfig b/configs/orangepi_zero_plus2_defconfig index c4d9800975c..9758ffd385e 100644 --- a/configs/orangepi_zero_plus2_defconfig +++ b/configs/orangepi_zero_plus2_defconfig @@ -7,6 +7,8 @@ CONFIG_MACH_SUN50I_H5=y CONFIG_DRAM_ZQ=3881977 # CONFIG_DRAM_ODT_EN is not set CONFIG_MMC_SUNXI_SLOT_EXTRA=2 +CONFIG_SPL_SUNXI_LED_STATUS=y +CONFIG_SPL_SUNXI_LED_STATUS_GPIO=362 # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_SUN8I_EMAC=y CONFIG_USB_EHCI_HCD=y diff --git a/configs/orangepi_zero_plus_defconfig b/configs/orangepi_zero_plus_defconfig index 4b6491dc846..bb159428a14 100644 --- a/configs/orangepi_zero_plus_defconfig +++ b/configs/orangepi_zero_plus_defconfig @@ -6,6 +6,8 @@ CONFIG_SPL=y CONFIG_MACH_SUN50I_H5=y CONFIG_DRAM_ZQ=3881977 # CONFIG_DRAM_ODT_EN is not set +CONFIG_SPL_SUNXI_LED_STATUS=y +CONFIG_SPL_SUNXI_LED_STATUS_GPIO=362 # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_SUN8I_EMAC=y CONFIG_USB_EHCI_HCD=y diff --git a/configs/radxa-cubie-a5e_defconfig b/configs/radxa-cubie-a5e_defconfig index 9d204ef5548..84bc4e3d4a9 100644 --- a/configs/radxa-cubie-a5e_defconfig +++ b/configs/radxa-cubie-a5e_defconfig @@ -15,6 +15,9 @@ CONFIG_DRAM_SUNXI_TPR12=0x3533302f CONFIG_MACH_SUN55I_A523=y CONFIG_MMC_SUNXI_SLOT_EXTRA=2 CONFIG_R_I2C_ENABLE=y +CONFIG_SPL_SUNXI_LED_STATUS=y +CONFIG_SPL_SUNXI_LED_STATUS_GPIO=356 +# CONFIG_SPL_SUNXI_LED_STATUS_ACTIVE_HIGH is not set # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_SPL_I2C=y CONFIG_SPL_SYS_I2C_LEGACY=y diff --git a/configs/tanix_tx1_defconfig b/configs/tanix_tx1_defconfig index 1bd167ce0f6..03b18701859 100644 --- a/configs/tanix_tx1_defconfig +++ b/configs/tanix_tx1_defconfig @@ -14,6 +14,8 @@ CONFIG_MACH_SUN50I_H616=y CONFIG_SUNXI_DRAM_H616_LPDDR3=y CONFIG_MMC_SUNXI_SLOT_EXTRA=2 CONFIG_R_I2C_ENABLE=y +CONFIG_SPL_SUNXI_LED_STATUS=y +CONFIG_SPL_SUNXI_LED_STATUS_GPIO=231 # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_SPL_I2C=y CONFIG_SPL_SYS_I2C_LEGACY=y -- cgit v1.2.3 From 9d3bbc99b9038fa64dda92f97e0c29e4808694ec Mon Sep 17 00:00:00 2001 From: Lukas Schmid Date: Wed, 25 Mar 2026 20:26:09 +0100 Subject: pinctrl: sunxi: add I2C3 mux for D1/T113-s3 (PG10/PG11) Boards based on the Allwinner D1/T113-s3, such as the NetCube Systems Nagami, can expose a third I2C controller on PG10/PG11. However, the sun20i_d1 pinctrl function table lacked an entry for this mux. Add the "i2c3" function with mux value 3 on PG10/PG11, allowing device trees to enable the I2C3 controller. Signed-off-by: Lukas Schmid Reviewed-by: Andre Przywara --- drivers/pinctrl/sunxi/pinctrl-sunxi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c index fd357ab0d4e..a8632aeaf8f 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c @@ -603,6 +603,7 @@ static const struct sunxi_pinctrl_function sun20i_d1_pinctrl_functions[] = { { "gpio_in", 0 }, { "gpio_out", 1 }, { "i2c0", 4 }, /* PB10-PB11 */ + { "i2c3", 3 }, /* PG10-PG11 */ { "mmc0", 2 }, /* PF0-PF5 */ { "mmc1", 2 }, /* PG0-PG5 */ { "mmc2", 3 }, /* PC2-PC7 */ -- cgit v1.2.3 From 5a24cc0740e1afcca830dd83d9f1062034fff2d6 Mon Sep 17 00:00:00 2001 From: Jernej Skrabec Date: Sun, 5 Apr 2026 21:59:26 +0200 Subject: sunxi: H616: dram: fix LPDDR3 TPR6 parsing Allwinner's DRAM initialisation code defines a parameter named TPR6, presumably containing some "Vref" parameter, but containing values for *all* DRAM types. The runtime code selects one byte based on the DRAM type used. This selection code was wrong for LPDDR3, the value is encoded in bits [23:16], not [15:8]. Fix that in the code, which also aligns it with the very similar code for the A133 and A523. Signed-off-by: Jernej Skrabec Reported-by: Philippe Simons Reviewed-by: Andre Przywara Reviewed-by: Paul Kocialkowski --- arch/arm/mach-sunxi/dram_sun50i_h616.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-sunxi/dram_sun50i_h616.c b/arch/arm/mach-sunxi/dram_sun50i_h616.c index 3345c9b8e82..42a0550e015 100644 --- a/arch/arm/mach-sunxi/dram_sun50i_h616.c +++ b/arch/arm/mach-sunxi/dram_sun50i_h616.c @@ -975,7 +975,7 @@ static bool mctl_phy_init(const struct dram_para *para, val = para->tpr6 & 0xff; break; case SUNXI_DRAM_TYPE_LPDDR3: - val = para->tpr6 >> 8 & 0xff; + val = para->tpr6 >> 16 & 0xff; break; case SUNXI_DRAM_TYPE_LPDDR4: val = para->tpr6 >> 24 & 0xff; -- cgit v1.2.3 From 7ad8e387d6d56bc07a978a8e888fdf5276325be5 Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Mon, 27 Apr 2026 15:29:50 +0200 Subject: sunxi: H616: dram: drop default TPR6 Kconfig value CONFIG_DRAM_SUNXI_TPR6 is the only DRAM config parameter that has a non-zero default value. Since we need to provide a value for all the other parameters anyway, avoiding TPR6 makes no real difference. To make matters worse, TPR6 is a compound value covering multiple DRAM types, but also spans over three SoCs, which makes it hard to find one good default value. Drop the default from Kconfig, and put some explicit values in the defconfigs for the few boards that were relying on the default so far. The value is taken from one BSP, only the lower byte matters anyway for those boards, all using DDR3 DRAM. Signed-off-by: Andre Przywara Reviewed-by: Jernej Skrabec Reviewed-by: Paul Kocialkowski --- arch/arm/mach-sunxi/Kconfig | 1 - configs/orangepi_zero2_defconfig | 1 + configs/transpeed-8k618-t_defconfig | 1 + configs/x96_mate_defconfig | 1 + configs/x96q_defconfig | 1 + 5 files changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig index 07d7ea2b7ad..ceba96b61a5 100644 --- a/arch/arm/mach-sunxi/Kconfig +++ b/arch/arm/mach-sunxi/Kconfig @@ -151,7 +151,6 @@ config DRAM_SUNXI_TPR3 config DRAM_SUNXI_TPR6 hex "DRAM TPR6 parameter" - default 0x3300c080 help TPR6 value from vendor DRAM settings. diff --git a/configs/orangepi_zero2_defconfig b/configs/orangepi_zero2_defconfig index b387b4795ad..afd1535052d 100644 --- a/configs/orangepi_zero2_defconfig +++ b/configs/orangepi_zero2_defconfig @@ -5,6 +5,7 @@ CONFIG_SPL=y CONFIG_DRAM_SUNXI_DX_ODT=0x08080808 CONFIG_DRAM_SUNXI_DX_DRI=0x0e0e0e0e CONFIG_DRAM_SUNXI_CA_DRI=0x0e0e +CONFIG_DRAM_SUNXI_TPR6=0x33808080 CONFIG_DRAM_SUNXI_TPR10=0xf83438 CONFIG_MACH_SUN50I_H616=y CONFIG_SUNXI_DRAM_H616_DDR3_1333=y diff --git a/configs/transpeed-8k618-t_defconfig b/configs/transpeed-8k618-t_defconfig index e4bf5e8efd3..e672d8f8df9 100644 --- a/configs/transpeed-8k618-t_defconfig +++ b/configs/transpeed-8k618-t_defconfig @@ -7,6 +7,7 @@ CONFIG_DRAM_SUNXI_DX_ODT=0x03030303 CONFIG_DRAM_SUNXI_DX_DRI=0x0e0e0e0e CONFIG_DRAM_SUNXI_CA_DRI=0x1f12 CONFIG_DRAM_SUNXI_TPR0=0xc0001002 +CONFIG_DRAM_SUNXI_TPR6=0x33808080 CONFIG_DRAM_SUNXI_TPR10=0x2f1107 CONFIG_DRAM_SUNXI_TPR11=0xddddcccc CONFIG_DRAM_SUNXI_TPR12=0xeddc7665 diff --git a/configs/x96_mate_defconfig b/configs/x96_mate_defconfig index c0628370df9..1b7d9338c02 100644 --- a/configs/x96_mate_defconfig +++ b/configs/x96_mate_defconfig @@ -6,6 +6,7 @@ CONFIG_DRAM_SUNXI_DX_ODT=0x03030303 CONFIG_DRAM_SUNXI_DX_DRI=0x0e0e0e0e CONFIG_DRAM_SUNXI_CA_DRI=0x1c12 CONFIG_DRAM_SUNXI_TPR0=0xc0000c05 +CONFIG_DRAM_SUNXI_TPR6=0x33808080 CONFIG_DRAM_SUNXI_TPR10=0x2f0007 CONFIG_DRAM_SUNXI_TPR11=0xffffdddd CONFIG_DRAM_SUNXI_TPR12=0xfedf7557 diff --git a/configs/x96q_defconfig b/configs/x96q_defconfig index 59f01aae4eb..068aee3fd1e 100644 --- a/configs/x96q_defconfig +++ b/configs/x96q_defconfig @@ -8,6 +8,7 @@ CONFIG_DRAM_SUNXI_DX_DRI=0x0e0e0e0e CONFIG_DRAM_SUNXI_CA_DRI=0x1f12 CONFIG_DRAM_SUNXI_TPR0=0xc0001002 CONFIG_DRAM_SUNXI_TPR2=0x00000100 +CONFIG_DRAM_SUNXI_TPR6=0x33808080 CONFIG_DRAM_SUNXI_TPR10=0x002f0107 CONFIG_DRAM_SUNXI_TPR11=0xddddcccc CONFIG_DRAM_SUNXI_TPR12=0xeddc7665 -- cgit v1.2.3 From 11b5cd22bae8372447a8089df7a2400ac21ed021 Mon Sep 17 00:00:00 2001 From: Yixun Lan Date: Tue, 21 Apr 2026 04:47:50 +0000 Subject: spi: sunxi: wait for TX/RX fifo reset done Once reset SPI TX or RX fifo, the underlying hardware need to take some time to actually settle down, the two bits will automatically clear to 0, so use a poll mechanism to check status bits to make sure it's done correctly. On Cubie A7A board which using A733 SoC, we encoutered a SPI nor flash timeout issue, it turns out that the SPI fifo reset take a few time to settle down, Add a loop to poll the status. This was the error message shows on A7A board once this issue happened. => sf probe ERROR: sun4i_spi: Timeout transferring data Failed to initialize SPI flash at 0:0 (error -2) Signed-off-by: Yixun Lan Reviewed-by: Jernej Skrabec Acked-by: Andre Przywara --- drivers/spi/spi-sunxi.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-sunxi.c b/drivers/spi/spi-sunxi.c index 0bdc112d249..08b603f04a2 100644 --- a/drivers/spi/spi-sunxi.c +++ b/drivers/spi/spi-sunxi.c @@ -344,7 +344,7 @@ static int sun4i_spi_xfer(struct udevice *dev, unsigned int bitlen, struct sun4i_spi_priv *priv = dev_get_priv(bus); struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev); - u32 len = bitlen / 8; + u32 rst, val, len = bitlen / 8; u8 nbytes; int ret; @@ -360,8 +360,11 @@ static int sun4i_spi_xfer(struct udevice *dev, unsigned int bitlen, sun4i_spi_set_cs(bus, slave_plat->cs[0], true); /* Reset FIFOs */ - setbits_le32(SPI_REG(priv, SPI_FCR), SPI_BIT(priv, SPI_FCR_RF_RST) | - SPI_BIT(priv, SPI_FCR_TF_RST)); + rst = SPI_BIT(priv, SPI_FCR_RF_RST) | SPI_BIT(priv, SPI_FCR_TF_RST); + setbits_le32(SPI_REG(priv, SPI_FCR), rst); + ret = readl_poll_timeout(SPI_REG(priv, SPI_FCR), val, !(rst & val), 20); + if (ret) + return -EBUSY; while (len) { /* Setup the transfer now... */ -- cgit v1.2.3 From 125bba0f61e3643de67d03c95e5cdcf5fded1f5b Mon Sep 17 00:00:00 2001 From: Richard Genoud Date: Fri, 27 Mar 2026 15:05:04 +0100 Subject: mtd: rawnand: sunxi: Replace hard coded value by a define The user data length (4) used all over the code hard coded. And sometimes, it's not that trivial to know that it's the user data length and not something else. Moreover, for the H6/H616 this value is no more fixed by hardware, but could be modified. Using a define here makes the code more readable. Suggested-by: Miquel Raynal Reviewed-by: Michael Trimarchi Signed-off-by: Richard Genoud Acked-by: Andre Przywara --- drivers/mtd/nand/raw/sunxi_nand.c | 58 +++++++++++++++++------------------ drivers/mtd/nand/raw/sunxi_nand.h | 3 ++ drivers/mtd/nand/raw/sunxi_nand_spl.c | 7 ++++- 3 files changed, 38 insertions(+), 30 deletions(-) diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index ef27a4b7a36..91f7da2c5ce 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -772,7 +772,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, return ret; sunxi_nfc_reset_user_data_len(nfc); - sunxi_nfc_set_user_data_len(nfc, 4, 0); + sunxi_nfc_set_user_data_len(nfc, USER_DATA_SZ, 0); sunxi_nfc_randomizer_enable(mtd); writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP, @@ -783,7 +783,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, if (ret) return ret; - *cur_off = oob_off + ecc->bytes + 4; + *cur_off = oob_off + ecc->bytes + USER_DATA_SZ; pattern_found = readl(nfc->regs + nfc->caps->reg_pat_found); pattern_found = field_get(NFC_ECC_PAT_FOUND_MSK(nfc), pattern_found); @@ -794,7 +794,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, pattern = 0x0; memset(data, pattern, ecc->size); - memset(oob, pattern, ecc->bytes + 4); + memset(oob, pattern, ecc->bytes + USER_DATA_SZ); return 1; } @@ -804,7 +804,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ecc->size); nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1); - sunxi_nfc_randomizer_read_buf(mtd, oob, ecc->bytes + 4, true, page); + sunxi_nfc_randomizer_read_buf(mtd, oob, ecc->bytes + USER_DATA_SZ, true, page); status = readl(nfc->regs + NFC_REG_ECC_ST); if (status & NFC_ECC_ERR(0)) { @@ -816,17 +816,17 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, nand->cmdfunc(mtd, NAND_CMD_RNDOUT, data_off, -1); nand->read_buf(mtd, data, ecc->size); nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1); - nand->read_buf(mtd, oob, ecc->bytes + 4); + nand->read_buf(mtd, oob, ecc->bytes + USER_DATA_SZ); } ret = nand_check_erased_ecc_chunk(data, ecc->size, - oob, ecc->bytes + 4, + oob, ecc->bytes + USER_DATA_SZ, NULL, 0, ecc->strength); if (ret >= 0) raw_mode = 1; } else { /* - * The engine protects 4 bytes of OOB data per chunk. + * The engine protects USER_DATA_SZ bytes of OOB data per chunk. * Retrieve the corrected OOB bytes. */ sunxi_nfc_user_data_to_buf(readl(nfc->regs + @@ -854,7 +854,7 @@ static void sunxi_nfc_hw_ecc_read_extra_oob(struct mtd_info *mtd, { struct nand_chip *nand = mtd_to_nand(mtd); struct nand_ecc_ctrl *ecc = &nand->ecc; - int offset = ((ecc->bytes + 4) * ecc->steps); + int offset = ((ecc->bytes + USER_DATA_SZ) * ecc->steps); int len = mtd->oobsize - offset; if (len <= 0) @@ -896,9 +896,9 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd, /* Fill OOB data in */ if ((nand->options & NAND_NEED_SCRAMBLING) && bbm) { - u8 user_data[4]; + u8 user_data[USER_DATA_SZ]; - memcpy(user_data, oob, 4); + memcpy(user_data, oob, USER_DATA_SZ); sunxi_nfc_randomize_bbm(mtd, page, user_data); writel(sunxi_nfc_buf_to_user_data(user_data), nfc->regs + NFC_REG_USER_DATA(nfc, 0)); @@ -915,7 +915,7 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd, return ret; sunxi_nfc_reset_user_data_len(nfc); - sunxi_nfc_set_user_data_len(nfc, 4, 0); + sunxi_nfc_set_user_data_len(nfc, USER_DATA_SZ, 0); sunxi_nfc_randomizer_enable(mtd); writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | @@ -927,7 +927,7 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd, if (ret) return ret; - *cur_off = oob_off + ecc->bytes + 4; + *cur_off = oob_off + ecc->bytes + USER_DATA_SZ; return 0; } @@ -938,7 +938,7 @@ static void sunxi_nfc_hw_ecc_write_extra_oob(struct mtd_info *mtd, { struct nand_chip *nand = mtd_to_nand(mtd); struct nand_ecc_ctrl *ecc = &nand->ecc; - int offset = ((ecc->bytes + 4) * ecc->steps); + int offset = ((ecc->bytes + USER_DATA_SZ) * ecc->steps); int len = mtd->oobsize - offset; if (len <= 0) @@ -966,7 +966,7 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, for (i = 0; i < ecc->steps; i++) { int data_off = i * ecc->size; - int oob_off = i * (ecc->bytes + 4); + int oob_off = i * (ecc->bytes + USER_DATA_SZ); u8 *data = buf + data_off; u8 *oob = chip->oob_poi + oob_off; @@ -1004,7 +1004,7 @@ static int sunxi_nfc_hw_ecc_read_subpage(struct mtd_info *mtd, for (i = data_offs / ecc->size; i < DIV_ROUND_UP(data_offs + readlen, ecc->size); i++) { int data_off = i * ecc->size; - int oob_off = i * (ecc->bytes + 4); + int oob_off = i * (ecc->bytes + USER_DATA_SZ); u8 *data = bufpoi + data_off; u8 *oob = chip->oob_poi + oob_off; @@ -1032,7 +1032,7 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, for (i = 0; i < ecc->steps; i++) { int data_off = i * ecc->size; - int oob_off = i * (ecc->bytes + 4); + int oob_off = i * (ecc->bytes + USER_DATA_SZ); const u8 *data = buf + data_off; const u8 *oob = chip->oob_poi + oob_off; @@ -1066,7 +1066,7 @@ static int sunxi_nfc_hw_ecc_write_subpage(struct mtd_info *mtd, for (i = data_offs / ecc->size; i < DIV_ROUND_UP(data_offs + data_len, ecc->size); i++) { int data_off = i * ecc->size; - int oob_off = i * (ecc->bytes + 4); + int oob_off = i * (ecc->bytes + USER_DATA_SZ); const u8 *data = buf + data_off; const u8 *oob = chip->oob_poi + oob_off; @@ -1095,10 +1095,10 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd, sunxi_nfc_hw_ecc_enable(mtd); for (i = 0; i < ecc->steps; i++) { - int data_off = i * (ecc->size + ecc->bytes + 4); + int data_off = i * (ecc->size + ecc->bytes + USER_DATA_SZ); int oob_off = data_off + ecc->size; u8 *data = buf + (i * ecc->size); - u8 *oob = chip->oob_poi + (i * (ecc->bytes + 4)); + u8 *oob = chip->oob_poi + (i * (ecc->bytes + USER_DATA_SZ)); ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob, oob_off, &cur_off, @@ -1129,10 +1129,10 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd, sunxi_nfc_hw_ecc_enable(mtd); for (i = 0; i < ecc->steps; i++) { - int data_off = i * (ecc->size + ecc->bytes + 4); + int data_off = i * (ecc->size + ecc->bytes + USER_DATA_SZ); int oob_off = data_off + ecc->size; const u8 *data = buf + (i * ecc->size); - const u8 *oob = chip->oob_poi + (i * (ecc->bytes + 4)); + const u8 *oob = chip->oob_poi + (i * (ecc->bytes + USER_DATA_SZ)); ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off, oob, oob_off, &cur_off, @@ -1390,7 +1390,7 @@ static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd, layout = &data->layout; nsectors = mtd->writesize / ecc->size; - if (mtd->oobsize < ((ecc->bytes + 4) * nsectors)) { + if (mtd->oobsize < ((ecc->bytes + USER_DATA_SZ) * nsectors)) { ret = -EINVAL; goto err; } @@ -1440,14 +1440,14 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd, layout->oobfree[i - 1].offset + layout->oobfree[i - 1].length + ecc->bytes; - layout->oobfree[i].length = 4; + layout->oobfree[i].length = USER_DATA_SZ; } else { /* * The first 2 bytes are used for BB markers, hence we - * only have 2 bytes available in the first user data - * section. + * only have USER_DATA_SZ - 2 bytes available in the + * first user data section. */ - layout->oobfree[i].length = 2; + layout->oobfree[i].length = USER_DATA_SZ - 2; layout->oobfree[i].offset = 2; } @@ -1457,13 +1457,13 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd, layout->oobfree[i].length + j; } - if (mtd->oobsize > (ecc->bytes + 4) * nsectors) { + if (mtd->oobsize > (ecc->bytes + USER_DATA_SZ) * nsectors) { layout->oobfree[nsectors].offset = layout->oobfree[nsectors - 1].offset + layout->oobfree[nsectors - 1].length + ecc->bytes; layout->oobfree[nsectors].length = mtd->oobsize - - ((ecc->bytes + 4) * nsectors); + ((ecc->bytes + USER_DATA_SZ) * nsectors); } return 0; @@ -1481,7 +1481,7 @@ static int sunxi_nand_hw_syndrome_ecc_ctrl_init(struct mtd_info *mtd, if (ret) return ret; - ecc->prepad = 4; + ecc->prepad = USER_DATA_SZ; ecc->read_page = sunxi_nfc_hw_syndrome_ecc_read_page; ecc->write_page = sunxi_nfc_hw_syndrome_ecc_write_page; diff --git a/drivers/mtd/nand/raw/sunxi_nand.h b/drivers/mtd/nand/raw/sunxi_nand.h index 6ee3ea14ee1..d7a8b3dd40c 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.h +++ b/drivers/mtd/nand/raw/sunxi_nand.h @@ -181,6 +181,9 @@ #define NFC_MAX_CS 7 +/* On A10, the user data length register is 4 bytes */ +#define USER_DATA_SZ 4 + /* * NAND Controller capabilities structure: stores NAND controller capabilities * for distinction between compatible strings. diff --git a/drivers/mtd/nand/raw/sunxi_nand_spl.c b/drivers/mtd/nand/raw/sunxi_nand_spl.c index 67f7d22ed2c..3a5271b49c3 100644 --- a/drivers/mtd/nand/raw/sunxi_nand_spl.c +++ b/drivers/mtd/nand/raw/sunxi_nand_spl.c @@ -269,6 +269,11 @@ static void sunxi_nfc_set_user_data_len(const struct nfc_config *nfc, writel_nfc(val, NFC_REG_USER_DATA_LEN(nfc, step)); } +/* + * Values in this table are obtained by doing: + * DIV_ROUND_UP(info->ecc_strength * 14, 8) + USER_DATA_SZ + * So it's the number of bytes needed for ECC + user data for one step. + */ #if defined(CONFIG_MACH_SUN50I_H616) || defined(CONFIG_MACH_SUN50I_H6) static const int ecc_bytes[] = { 32, 46, 54, 60, 74, 82, 88, 96, 102, 110, 116, 124, 130, 138, 144 @@ -338,7 +343,7 @@ static int nand_read_page(const struct nfc_config *conf, u32 offs, nand_change_column(oob_off); sunxi_nfc_reset_user_data_len(conf); - sunxi_nfc_set_user_data_len(conf, 4, 0); + sunxi_nfc_set_user_data_len(conf, USER_DATA_SZ, 0); nand_exec_cmd(NFC_DATA_TRANS | NFC_ECC_OP); /* Get the ECC status */ -- cgit v1.2.3 From 6b33232e32828c47ffb9fa024220a7899533f550 Mon Sep 17 00:00:00 2001 From: Richard Genoud Date: Fri, 27 Mar 2026 15:05:05 +0100 Subject: mtd: rawnand: sunxi: make the code mode self-explanatory In sunxi_nfc_hw_ecc_{read,write}_chunk(), the ECC step was force to 0, the reason is not trivial to get when reading the code. The explanation is that, from the NAND flash controller perspective, we are indeed at step 0 for user data length and ECC errors. Just add a const value with an explanation to clarify things. Signed-off-by: Richard Genoud Reviewed-By: Michael Trimarchi Acked-by: Andre Przywara --- drivers/mtd/nand/raw/sunxi_nand.c | 20 ++++++++++++-------- drivers/mtd/nand/raw/sunxi_nand_spl.c | 6 ++++-- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index 91f7da2c5ce..9fc9bc5e019 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -758,6 +758,8 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, u32 status; u32 pattern_found; int ret; + /* From the controller point of view, we are at step 0 */ + const int nfc_step = 0; if (*cur_off != data_off) nand->cmdfunc(mtd, NAND_CMD_RNDOUT, data_off, -1); @@ -772,7 +774,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, return ret; sunxi_nfc_reset_user_data_len(nfc); - sunxi_nfc_set_user_data_len(nfc, USER_DATA_SZ, 0); + sunxi_nfc_set_user_data_len(nfc, USER_DATA_SZ, nfc_step); sunxi_nfc_randomizer_enable(mtd); writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP, @@ -787,7 +789,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, pattern_found = readl(nfc->regs + nfc->caps->reg_pat_found); pattern_found = field_get(NFC_ECC_PAT_FOUND_MSK(nfc), pattern_found); - if (pattern_found & NFC_ECC_PAT_FOUND(0)) { + if (pattern_found & NFC_ECC_PAT_FOUND(nfc_step)) { u8 pattern = 0xff; if (unlikely(!(readl(nfc->regs + NFC_REG_PAT_ID(nfc)) & 0x1))) @@ -799,7 +801,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, return 1; } - ret = NFC_ECC_ERR_CNT(0, readl(nfc->regs + NFC_REG_ECC_ERR_CNT(nfc, 0))); + ret = NFC_ECC_ERR_CNT(nfc_step, readl(nfc->regs + NFC_REG_ECC_ERR_CNT(nfc, nfc_step))); memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ecc->size); @@ -807,7 +809,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, sunxi_nfc_randomizer_read_buf(mtd, oob, ecc->bytes + USER_DATA_SZ, true, page); status = readl(nfc->regs + NFC_REG_ECC_ST); - if (status & NFC_ECC_ERR(0)) { + if (status & NFC_ECC_ERR(nfc_step)) { /* * Re-read the data with the randomizer disabled to identify * bitflips in erased pages. @@ -830,7 +832,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, * Retrieve the corrected OOB bytes. */ sunxi_nfc_user_data_to_buf(readl(nfc->regs + - NFC_REG_USER_DATA(nfc, 0)), + NFC_REG_USER_DATA(nfc, nfc_step)), oob); /* De-randomize the Bad Block Marker. */ @@ -888,6 +890,8 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd, struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); struct nand_ecc_ctrl *ecc = &nand->ecc; int ret; + /* From the controller point of view, we are at step 0 */ + const int nfc_step = 0; if (data_off != *cur_off) nand->cmdfunc(mtd, NAND_CMD_RNDIN, data_off, -1); @@ -901,10 +905,10 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd, memcpy(user_data, oob, USER_DATA_SZ); sunxi_nfc_randomize_bbm(mtd, page, user_data); writel(sunxi_nfc_buf_to_user_data(user_data), - nfc->regs + NFC_REG_USER_DATA(nfc, 0)); + nfc->regs + NFC_REG_USER_DATA(nfc, nfc_step)); } else { writel(sunxi_nfc_buf_to_user_data(oob), - nfc->regs + NFC_REG_USER_DATA(nfc, 0)); + nfc->regs + NFC_REG_USER_DATA(nfc, nfc_step)); } if (data_off + ecc->size != oob_off) @@ -915,7 +919,7 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd, return ret; sunxi_nfc_reset_user_data_len(nfc); - sunxi_nfc_set_user_data_len(nfc, USER_DATA_SZ, 0); + sunxi_nfc_set_user_data_len(nfc, USER_DATA_SZ, nfc_step); sunxi_nfc_randomizer_enable(mtd); writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | diff --git a/drivers/mtd/nand/raw/sunxi_nand_spl.c b/drivers/mtd/nand/raw/sunxi_nand_spl.c index 3a5271b49c3..0d1f060cc42 100644 --- a/drivers/mtd/nand/raw/sunxi_nand_spl.c +++ b/drivers/mtd/nand/raw/sunxi_nand_spl.c @@ -302,6 +302,8 @@ static int nand_read_page(const struct nfc_config *conf, u32 offs, int page = offs / conf->page_size; u32 ecc_st, pattern_found; int i; + /* From the controller point of view, we are at step 0 */ + const int nfc_step = 0; if (offs % conf->page_size || len % conf->ecc_size || len > conf->page_size || len < 0) @@ -343,7 +345,7 @@ static int nand_read_page(const struct nfc_config *conf, u32 offs, nand_change_column(oob_off); sunxi_nfc_reset_user_data_len(conf); - sunxi_nfc_set_user_data_len(conf, USER_DATA_SZ, 0); + sunxi_nfc_set_user_data_len(conf, USER_DATA_SZ, nfc_step); nand_exec_cmd(NFC_DATA_TRANS | NFC_ECC_OP); /* Get the ECC status */ @@ -361,7 +363,7 @@ static int nand_read_page(const struct nfc_config *conf, u32 offs, pattern_found = readl_nfc(conf->caps->reg_pat_found); pattern_found = field_get(NFC_ECC_PAT_FOUND_MSK(conf), pattern_found); - if (pattern_found & NFC_ECC_PAT_FOUND(0)) + if (pattern_found & NFC_ECC_PAT_FOUND(nfc_step)) return 1; } -- cgit v1.2.3 From a776cb833d878dc0e8ea64a0ca28fbcdf166e1f5 Mon Sep 17 00:00:00 2001 From: Richard Genoud Date: Fri, 27 Mar 2026 15:05:06 +0100 Subject: mtd: rawnand: sunxi: clean sunxi_nand_chip_init() In sunxi_nand_chip_init there's quite a lot of kfree/return, it's easy to forget a kfree(), so use a goto/kfree instead. Signed-off-by: Richard Genoud [Andre: rename goto label, keep return 0;] Reviewed-by: Andre Przywara Signed-off-by: Andre Przywara --- drivers/mtd/nand/raw/sunxi_nand.c | 40 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index 9fc9bc5e019..d8ce4a1f35d 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -1600,21 +1600,20 @@ static int sunxi_nand_chip_init(struct udevice *dev, struct sunxi_nfc *nfc, if (ret) { dev_err(dev, "could not retrieve reg property: %d\n", ret); - kfree(chip); - return ret; + goto err_free; } if (tmp > NFC_MAX_CS) { dev_err(dev, "invalid reg value: %u (max CS = 7)\n", tmp); - kfree(chip); - return -EINVAL; + ret = -EINVAL; + goto err_free; } if (test_and_set_bit(tmp, &nfc->assigned_cs)) { dev_err(dev, "CS %d already assigned\n", tmp); - kfree(chip); - return -EINVAL; + ret = -EINVAL; + goto err_free; } chip->sels[i].cs = tmp; @@ -1640,15 +1639,13 @@ static int sunxi_nand_chip_init(struct udevice *dev, struct sunxi_nfc *nfc, dev_err(dev, "could not retrieve timings for ONFI mode 0: %d\n", ret); - kfree(chip); - return ret; + goto err_free; } ret = sunxi_nand_chip_set_timings(nfc, chip, timings); if (ret) { dev_err(dev, "could not configure chip timings: %d\n", ret); - kfree(chip); - return ret; + goto err_free; } nand = &chip->nand; @@ -1669,10 +1666,8 @@ static int sunxi_nand_chip_init(struct udevice *dev, struct sunxi_nfc *nfc, mtd = nand_to_mtd(nand); ret = nand_scan_ident(mtd, nsels, NULL); - if (ret) { - kfree(chip); - return ret; - } + if (ret) + goto err_free; if (nand->bbt_options & NAND_BBT_USE_FLASH) nand->bbt_options |= NAND_BBT_NO_OOB; @@ -1685,34 +1680,35 @@ static int sunxi_nand_chip_init(struct udevice *dev, struct sunxi_nfc *nfc, ret = sunxi_nand_chip_init_timings(nfc, chip); if (ret) { dev_err(dev, "could not configure chip timings: %d\n", ret); - kfree(chip); - return ret; + goto err_free; } ret = sunxi_nand_ecc_init(mtd, &nand->ecc); if (ret) { dev_err(dev, "ECC init failed: %d\n", ret); - kfree(chip); - return ret; + goto err_free; } ret = nand_scan_tail(mtd); if (ret) { dev_err(dev, "nand_scan_tail failed: %d\n", ret); - kfree(chip); - return ret; + goto err_free; } ret = nand_register(devnum, mtd); if (ret) { dev_err(dev, "failed to register mtd device: %d\n", ret); - kfree(chip); - return ret; + goto err_free; } list_add_tail(&chip->node, &nfc->chips); return 0; + +err_free: + kfree(chip); + + return ret; } static int sunxi_nand_chips_init(struct udevice *dev, struct sunxi_nfc *nfc) -- cgit v1.2.3 From dbed35acee32e68d8f26ade5ec16458323454260 Mon Sep 17 00:00:00 2001 From: Richard Genoud Date: Fri, 27 Mar 2026 15:05:07 +0100 Subject: mtd: rawnand: sunxi: introduce variable user data length In Allwinner SoCs, user data can be added in OOB before each ECC data. For older SoCs like A10, the user data size was the size of a register (4 bytes) and was mandatory before each ECC step. So, the A10 OOB Layout is: [4Bytes USER_DATA_STEP0] [ECC_STEP0 bytes] [4bytes USER_DATA_STEP1] [ECC_STEP1 bytes] ... NB: the BBM is stored at the beginning of the USER_DATA_STEP0. Now, for H6/H616 NAND flash controller, this user data can have a different size for each step. So, we are maximizing the user data length to use as many OOB bytes as possible. Fixes: 7d1de9801151 ("mtd: rawnand: sunxi_spl: add support for H6/H616 nand controller") Fixes: f163da5e6d26 ("mtd: rawnand: sunxi: add support for H6/H616 nand controller") Signed-off-by: Richard Genoud --- drivers/mtd/nand/raw/sunxi_nand.c | 235 +++++++++++++++++++++++++++------- drivers/mtd/nand/raw/sunxi_nand_spl.c | 107 ++++++++++++++-- 2 files changed, 287 insertions(+), 55 deletions(-) diff --git a/drivers/mtd/nand/raw/sunxi_nand.c b/drivers/mtd/nand/raw/sunxi_nand.c index d8ce4a1f35d..49748fddf80 100644 --- a/drivers/mtd/nand/raw/sunxi_nand.c +++ b/drivers/mtd/nand/raw/sunxi_nand.c @@ -114,6 +114,7 @@ struct sunxi_nand_hw_ecc { * @clk_rate: clk_rate required for this NAND chip * @timing_cfg TIMING_CFG register value for this NAND chip * @selected: current active CS + * @user_data_bytes array of user data lengths for all ECC steps * @nsels: number of CS lines required by the NAND chip * @sels: array of CS lines descriptions */ @@ -128,6 +129,7 @@ struct sunxi_nand_chip { u32 addr[2]; int cmd_cycles; u8 cmd[2]; + u8 *user_data_bytes; int nsels; struct sunxi_nand_chip_sel sels[0]; }; @@ -744,19 +746,73 @@ static void sunxi_nfc_set_user_data_len(struct sunxi_nfc *nfc, writel(val, nfc->regs + NFC_REG_USER_DATA_LEN(nfc, step)); } +static u8 sunxi_nfc_user_data_sz(const struct sunxi_nand_chip *sunxi_nand, int step) +{ + if (!sunxi_nand->user_data_bytes) + return USER_DATA_SZ; + + return sunxi_nand->user_data_bytes[step]; +} + +static void sunxi_nfc_hw_ecc_get_prot_oob_bytes(struct nand_chip *nand, u8 *oob, + int step, bool bbm, int page, + unsigned int user_data_sz) +{ + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + u32 user_data; + + if (!nfc->caps->reg_user_data_len) { + /* + * For A10, the user data for step n is in the nth + * REG_USER_DATA + */ + user_data = readl(nfc->regs + NFC_REG_USER_DATA(nfc, step)); + sunxi_nfc_user_data_to_buf(user_data, oob); + + } else { + /* + * For H6 NAND controller, the user data for all steps is + * contained in 32 user data registers, but not at a specific + * offset for each step, they are just concatenated. + */ + unsigned int user_data_off = 0; + unsigned int reg_off; + u8 *ptr = oob; + unsigned int i; + + for (i = 0; i < step; i++) + user_data_off += sunxi_nfc_user_data_sz(sunxi_nand, i); + + user_data_off /= 4; + for (i = 0; i < user_data_sz / 4; i++, ptr += 4) { + reg_off = NFC_REG_USER_DATA(nfc, user_data_off + i); + user_data = readl(nfc->regs + reg_off); + sunxi_nfc_user_data_to_buf(user_data, ptr); + } + } + + /* De-randomize the Bad Block Marker. */ + if (bbm && nand->options & NAND_NEED_SCRAMBLING) + sunxi_nfc_randomize_bbm(&nand->mtd, page, oob); +} + static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, u8 *data, int data_off, u8 *oob, int oob_off, int *cur_off, unsigned int *max_bitflips, - bool bbm, int page) + int step, int page) { struct nand_chip *nand = mtd_to_nand(mtd); struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); + unsigned int user_data_sz = sunxi_nfc_user_data_sz(sunxi_nand, step); struct nand_ecc_ctrl *ecc = &nand->ecc; int raw_mode = 0; u32 status; u32 pattern_found; + bool bbm = !step; int ret; /* From the controller point of view, we are at step 0 */ const int nfc_step = 0; @@ -773,8 +829,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, if (ret) return ret; - sunxi_nfc_reset_user_data_len(nfc); - sunxi_nfc_set_user_data_len(nfc, USER_DATA_SZ, nfc_step); + sunxi_nfc_set_user_data_len(nfc, user_data_sz, nfc_step); sunxi_nfc_randomizer_enable(mtd); writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | NFC_ECC_OP, @@ -785,7 +840,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, if (ret) return ret; - *cur_off = oob_off + ecc->bytes + USER_DATA_SZ; + *cur_off = oob_off + ecc->bytes + user_data_sz; pattern_found = readl(nfc->regs + nfc->caps->reg_pat_found); pattern_found = field_get(NFC_ECC_PAT_FOUND_MSK(nfc), pattern_found); @@ -796,7 +851,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, pattern = 0x0; memset(data, pattern, ecc->size); - memset(oob, pattern, ecc->bytes + USER_DATA_SZ); + memset(oob, pattern, ecc->bytes + user_data_sz); return 1; } @@ -806,7 +861,7 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, memcpy_fromio(data, nfc->regs + NFC_RAM0_BASE, ecc->size); nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1); - sunxi_nfc_randomizer_read_buf(mtd, oob, ecc->bytes + USER_DATA_SZ, true, page); + sunxi_nfc_randomizer_read_buf(mtd, oob, ecc->bytes + user_data_sz, true, page); status = readl(nfc->regs + NFC_REG_ECC_ST); if (status & NFC_ECC_ERR(nfc_step)) { @@ -818,26 +873,21 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, nand->cmdfunc(mtd, NAND_CMD_RNDOUT, data_off, -1); nand->read_buf(mtd, data, ecc->size); nand->cmdfunc(mtd, NAND_CMD_RNDOUT, oob_off, -1); - nand->read_buf(mtd, oob, ecc->bytes + USER_DATA_SZ); + nand->read_buf(mtd, oob, ecc->bytes + user_data_sz); } ret = nand_check_erased_ecc_chunk(data, ecc->size, - oob, ecc->bytes + USER_DATA_SZ, + oob, ecc->bytes + user_data_sz, NULL, 0, ecc->strength); if (ret >= 0) raw_mode = 1; } else { /* - * The engine protects USER_DATA_SZ bytes of OOB data per chunk. + * The engine protects user_data_sz bytes of OOB data per chunk. * Retrieve the corrected OOB bytes. */ - sunxi_nfc_user_data_to_buf(readl(nfc->regs + - NFC_REG_USER_DATA(nfc, nfc_step)), - oob); - - /* De-randomize the Bad Block Marker. */ - if (bbm && nand->options & NAND_NEED_SCRAMBLING) - sunxi_nfc_randomize_bbm(mtd, page, oob); + sunxi_nfc_hw_ecc_get_prot_oob_bytes(nand, oob, nfc_step, + bbm, page, user_data_sz); } if (ret < 0) { @@ -850,13 +900,30 @@ static int sunxi_nfc_hw_ecc_read_chunk(struct mtd_info *mtd, return raw_mode; } +/* + * Returns the offset of the OOB for each step. + * (it includes the user data before the ECC data.) + */ +static int sunxi_get_oob_offset(struct sunxi_nand_chip *sunxi_nand, + struct nand_ecc_ctrl *ecc, int step) +{ + int ecc_off = step * ecc->bytes; + int i; + + for (i = 0; i < step; i++) + ecc_off += sunxi_nfc_user_data_sz(sunxi_nand, i); + + return ecc_off; +} + static void sunxi_nfc_hw_ecc_read_extra_oob(struct mtd_info *mtd, u8 *oob, int *cur_off, bool randomize, int page) { struct nand_chip *nand = mtd_to_nand(mtd); struct nand_ecc_ctrl *ecc = &nand->ecc; - int offset = ((ecc->bytes + USER_DATA_SZ) * ecc->steps); + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); + int offset = sunxi_get_oob_offset(sunxi_nand, ecc, ecc->steps); int len = mtd->oobsize - offset; if (len <= 0) @@ -883,12 +950,15 @@ static inline u32 sunxi_nfc_buf_to_user_data(const u8 *buf) static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd, const u8 *data, int data_off, const u8 *oob, int oob_off, - int *cur_off, bool bbm, + int *cur_off, int step, int page) { struct nand_chip *nand = mtd_to_nand(mtd); struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); + unsigned int user_data_sz = sunxi_nfc_user_data_sz(sunxi_nand, step); struct nand_ecc_ctrl *ecc = &nand->ecc; + bool bbm = !step; int ret; /* From the controller point of view, we are at step 0 */ const int nfc_step = 0; @@ -900,12 +970,17 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd, /* Fill OOB data in */ if ((nand->options & NAND_NEED_SCRAMBLING) && bbm) { - u8 user_data[USER_DATA_SZ]; + u8 *user_data; + + user_data = kzalloc(user_data_sz, GFP_KERNEL); + if (!user_data) + return -ENOMEM; - memcpy(user_data, oob, USER_DATA_SZ); + memcpy(user_data, oob, user_data_sz); sunxi_nfc_randomize_bbm(mtd, page, user_data); writel(sunxi_nfc_buf_to_user_data(user_data), nfc->regs + NFC_REG_USER_DATA(nfc, nfc_step)); + kfree(user_data); } else { writel(sunxi_nfc_buf_to_user_data(oob), nfc->regs + NFC_REG_USER_DATA(nfc, nfc_step)); @@ -918,8 +993,7 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd, if (ret) return ret; - sunxi_nfc_reset_user_data_len(nfc); - sunxi_nfc_set_user_data_len(nfc, USER_DATA_SZ, nfc_step); + sunxi_nfc_set_user_data_len(nfc, user_data_sz, nfc_step); sunxi_nfc_randomizer_enable(mtd); writel(NFC_DATA_TRANS | NFC_DATA_SWAP_METHOD | @@ -931,7 +1005,7 @@ static int sunxi_nfc_hw_ecc_write_chunk(struct mtd_info *mtd, if (ret) return ret; - *cur_off = oob_off + ecc->bytes + USER_DATA_SZ; + *cur_off = oob_off + ecc->bytes + user_data_sz; return 0; } @@ -942,7 +1016,8 @@ static void sunxi_nfc_hw_ecc_write_extra_oob(struct mtd_info *mtd, { struct nand_chip *nand = mtd_to_nand(mtd); struct nand_ecc_ctrl *ecc = &nand->ecc; - int offset = ((ecc->bytes + USER_DATA_SZ) * ecc->steps); + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); + int offset = sunxi_get_oob_offset(sunxi_nand, ecc, ecc->steps); int len = mtd->oobsize - offset; if (len <= 0) @@ -961,6 +1036,8 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(chip); + struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); struct nand_ecc_ctrl *ecc = &chip->ecc; unsigned int max_bitflips = 0; int ret, i, cur_off = 0; @@ -968,16 +1045,17 @@ static int sunxi_nfc_hw_ecc_read_page(struct mtd_info *mtd, sunxi_nfc_hw_ecc_enable(mtd); + sunxi_nfc_reset_user_data_len(nfc); for (i = 0; i < ecc->steps; i++) { int data_off = i * ecc->size; - int oob_off = i * (ecc->bytes + USER_DATA_SZ); + int oob_off = sunxi_get_oob_offset(sunxi_nand, ecc, i); u8 *data = buf + data_off; u8 *oob = chip->oob_poi + oob_off; ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob, oob_off + mtd->writesize, &cur_off, &max_bitflips, - !i, page); + i, page); if (ret < 0) return ret; else if (ret) @@ -998,23 +1076,26 @@ static int sunxi_nfc_hw_ecc_read_subpage(struct mtd_info *mtd, uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi, int page) { + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(chip); + struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); struct nand_ecc_ctrl *ecc = &chip->ecc; int ret, i, cur_off = 0; unsigned int max_bitflips = 0; sunxi_nfc_hw_ecc_enable(mtd); + sunxi_nfc_reset_user_data_len(nfc); chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page); for (i = data_offs / ecc->size; i < DIV_ROUND_UP(data_offs + readlen, ecc->size); i++) { int data_off = i * ecc->size; - int oob_off = i * (ecc->bytes + USER_DATA_SZ); + int oob_off = sunxi_get_oob_offset(sunxi_nand, ecc, i); u8 *data = bufpoi + data_off; u8 *oob = chip->oob_poi + oob_off; ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob, oob_off + mtd->writesize, - &cur_off, &max_bitflips, !i, page); + &cur_off, &max_bitflips, i, page); if (ret < 0) return ret; } @@ -1029,20 +1110,23 @@ static int sunxi_nfc_hw_ecc_write_page(struct mtd_info *mtd, const uint8_t *buf, int oob_required, int page) { + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(chip); + struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); struct nand_ecc_ctrl *ecc = &chip->ecc; int ret, i, cur_off = 0; sunxi_nfc_hw_ecc_enable(mtd); + sunxi_nfc_reset_user_data_len(nfc); for (i = 0; i < ecc->steps; i++) { int data_off = i * ecc->size; - int oob_off = i * (ecc->bytes + USER_DATA_SZ); + int oob_off = sunxi_get_oob_offset(sunxi_nand, ecc, i); const u8 *data = buf + data_off; const u8 *oob = chip->oob_poi + oob_off; ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off, oob, oob_off + mtd->writesize, - &cur_off, !i, page); + &cur_off, i, page); if (ret) return ret; } @@ -1062,21 +1146,24 @@ static int sunxi_nfc_hw_ecc_write_subpage(struct mtd_info *mtd, const u8 *buf, int oob_required, int page) { + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(chip); + struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); struct nand_ecc_ctrl *ecc = &chip->ecc; int ret, i, cur_off = 0; sunxi_nfc_hw_ecc_enable(mtd); + sunxi_nfc_reset_user_data_len(nfc); for (i = data_offs / ecc->size; i < DIV_ROUND_UP(data_offs + data_len, ecc->size); i++) { int data_off = i * ecc->size; - int oob_off = i * (ecc->bytes + USER_DATA_SZ); + int oob_off = sunxi_get_oob_offset(sunxi_nand, ecc, i); const u8 *data = buf + data_off; const u8 *oob = chip->oob_poi + oob_off; ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off, oob, oob_off + mtd->writesize, - &cur_off, !i, page); + &cur_off, i, page); if (ret) return ret; } @@ -1091,18 +1178,23 @@ static int sunxi_nfc_hw_syndrome_ecc_read_page(struct mtd_info *mtd, uint8_t *buf, int oob_required, int page) { + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(chip); + struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); struct nand_ecc_ctrl *ecc = &chip->ecc; unsigned int max_bitflips = 0; int ret, i, cur_off = 0; bool raw_mode = false; + /* With hw_syndrome, user data length is fixed */ + unsigned int user_data_sz = sunxi_nfc_user_data_sz(sunxi_nand, 0); sunxi_nfc_hw_ecc_enable(mtd); + sunxi_nfc_reset_user_data_len(nfc); for (i = 0; i < ecc->steps; i++) { - int data_off = i * (ecc->size + ecc->bytes + USER_DATA_SZ); + int data_off = i * (ecc->size + ecc->bytes + user_data_sz); int oob_off = data_off + ecc->size; u8 *data = buf + (i * ecc->size); - u8 *oob = chip->oob_poi + (i * (ecc->bytes + USER_DATA_SZ)); + u8 *oob = chip->oob_poi + (i * (ecc->bytes + user_data_sz)); ret = sunxi_nfc_hw_ecc_read_chunk(mtd, data, data_off, oob, oob_off, &cur_off, @@ -1127,16 +1219,19 @@ static int sunxi_nfc_hw_syndrome_ecc_write_page(struct mtd_info *mtd, const uint8_t *buf, int oob_required, int page) { + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(chip); struct nand_ecc_ctrl *ecc = &chip->ecc; int ret, i, cur_off = 0; + /* With hw_syndrome, user data length is fixed */ + unsigned int user_data_sz = sunxi_nfc_user_data_sz(sunxi_nand, 0); sunxi_nfc_hw_ecc_enable(mtd); for (i = 0; i < ecc->steps; i++) { - int data_off = i * (ecc->size + ecc->bytes + USER_DATA_SZ); + int data_off = i * (ecc->size + ecc->bytes + user_data_sz); int oob_off = data_off + ecc->size; const u8 *data = buf + (i * ecc->size); - const u8 *oob = chip->oob_poi + (i * (ecc->bytes + USER_DATA_SZ)); + const u8 *oob = chip->oob_poi + (i * (ecc->bytes + user_data_sz)); ret = sunxi_nfc_hw_ecc_write_chunk(mtd, data, data_off, oob, oob_off, &cur_off, @@ -1338,6 +1433,34 @@ static int sunxi_nand_chip_init_timings(struct sunxi_nfc *nfc, return sunxi_nand_chip_set_timings(nfc, chip, timings); } +static int sunxi_nfc_maximize_user_data(struct nand_chip *nand, uint32_t oobsize, + int ecc_bytes, int nsectors) +{ + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); + struct sunxi_nfc *nfc = to_sunxi_nfc(nand->controller); + const struct sunxi_nfc_caps *c = nfc->caps; + int remaining_bytes = oobsize - (ecc_bytes * nsectors); + int i, step; + + sunxi_nand->user_data_bytes = devm_kzalloc(nfc->dev, nsectors, + GFP_KERNEL); + if (!sunxi_nand->user_data_bytes) + return -ENOMEM; + + for (step = 0; (step < nsectors) && (remaining_bytes > 0); step++) { + for (i = 0; i < c->nuser_data_tab; i++) { + if (c->user_data_len_tab[i] > remaining_bytes) + break; + sunxi_nand->user_data_bytes[step] = c->user_data_len_tab[i]; + } + remaining_bytes -= sunxi_nand->user_data_bytes[step]; + if (sunxi_nand->user_data_bytes[step] == 0) + break; + } + + return 0; +} + static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc) { @@ -1346,6 +1469,7 @@ static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd, struct sunxi_nfc *nfc = to_sunxi_nfc(sunxi_nand->nand.controller); struct sunxi_nand_hw_ecc *data; struct nand_ecclayout *layout; + unsigned int total_user_data_sz = 0; int nsectors; int ret; int i; @@ -1394,7 +1518,15 @@ static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd, layout = &data->layout; nsectors = mtd->writesize / ecc->size; - if (mtd->oobsize < ((ecc->bytes + USER_DATA_SZ) * nsectors)) { + /* Use the remaining OOB space for user data */ + if (nfc->caps->reg_user_data_len) + sunxi_nfc_maximize_user_data(nand, mtd->oobsize, ecc->bytes, + nsectors); + + for (i = 0; i < nsectors; i++) + total_user_data_sz += sunxi_nfc_user_data_sz(sunxi_nand, i); + + if (mtd->oobsize < ecc->bytes * nsectors + total_user_data_sz) { ret = -EINVAL; goto err; } @@ -1408,6 +1540,8 @@ static int sunxi_nand_hw_common_ecc_ctrl_init(struct mtd_info *mtd, err: kfree(data); + devm_kfree(nfc->dev, sunxi_nand->user_data_bytes); + sunxi_nand->user_data_bytes = NULL; return ret; } @@ -1422,7 +1556,10 @@ static void sunxi_nand_hw_common_ecc_ctrl_cleanup(struct nand_ecc_ctrl *ecc) static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc) { + struct nand_chip *nand = mtd_to_nand(mtd); + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); struct nand_ecclayout *layout; + unsigned int total_user_data_sz = 0; int nsectors; int i, j; int ret; @@ -1444,14 +1581,14 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd, layout->oobfree[i - 1].offset + layout->oobfree[i - 1].length + ecc->bytes; - layout->oobfree[i].length = USER_DATA_SZ; + layout->oobfree[i].length = sunxi_nfc_user_data_sz(sunxi_nand, i); } else { /* * The first 2 bytes are used for BB markers, hence we - * only have USER_DATA_SZ - 2 bytes available in the + * only have user_data_len(0) - 2 bytes available in the * first user data section. */ - layout->oobfree[i].length = USER_DATA_SZ - 2; + layout->oobfree[i].length = sunxi_nfc_user_data_sz(sunxi_nand, i) - 2; layout->oobfree[i].offset = 2; } @@ -1461,13 +1598,16 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd, layout->oobfree[i].length + j; } - if (mtd->oobsize > (ecc->bytes + USER_DATA_SZ) * nsectors) { + for (i = 0; i < nsectors; i++) + total_user_data_sz += sunxi_nfc_user_data_sz(sunxi_nand, i); + + if (mtd->oobsize > ecc->bytes * nsectors + total_user_data_sz) { layout->oobfree[nsectors].offset = layout->oobfree[nsectors - 1].offset + layout->oobfree[nsectors - 1].length + ecc->bytes; layout->oobfree[nsectors].length = mtd->oobsize - - ((ecc->bytes + USER_DATA_SZ) * nsectors); + (ecc->bytes * nsectors + total_user_data_sz); } return 0; @@ -1476,6 +1616,8 @@ static int sunxi_nand_hw_ecc_ctrl_init(struct mtd_info *mtd, static int sunxi_nand_hw_syndrome_ecc_ctrl_init(struct mtd_info *mtd, struct nand_ecc_ctrl *ecc) { + struct nand_chip *nand = mtd_to_nand(mtd); + struct sunxi_nand_chip *sunxi_nand = to_sunxi_nand(nand); struct nand_ecclayout *layout; int nsectors; int i; @@ -1485,7 +1627,13 @@ static int sunxi_nand_hw_syndrome_ecc_ctrl_init(struct mtd_info *mtd, if (ret) return ret; - ecc->prepad = USER_DATA_SZ; + for (i = 0; i < nsectors; i++) + if (sunxi_nfc_user_data_sz(sunxi_nand, i) != + sunxi_nfc_user_data_sz(sunxi_nand, 0)) { + dev_err(mtd->dev, "Variable user data length not upported with NAND_ECC_HW_SYNDROME\n"); + return -EOPNOTSUPP; + } + ecc->prepad = sunxi_nfc_user_data_sz(sunxi_nand, 0); ecc->read_page = sunxi_nfc_hw_syndrome_ecc_read_page; ecc->write_page = sunxi_nfc_hw_syndrome_ecc_write_page; @@ -1736,6 +1884,7 @@ static void sunxi_nand_chips_cleanup(struct sunxi_nfc *nfc) nand_release(&chip->mtd); sunxi_nand_ecc_cleanup(&chip->nand.ecc); list_del(&chip->node); + devm_kfree(nfc->dev, chip->user_data_bytes); kfree(chip); } } diff --git a/drivers/mtd/nand/raw/sunxi_nand_spl.c b/drivers/mtd/nand/raw/sunxi_nand_spl.c index 0d1f060cc42..784ffb00cf7 100644 --- a/drivers/mtd/nand/raw/sunxi_nand_spl.c +++ b/drivers/mtd/nand/raw/sunxi_nand_spl.c @@ -28,6 +28,7 @@ struct nfc_config { bool randomize; bool valid; const struct sunxi_nfc_caps *caps; + u8 *user_data_bytes; }; /* minimal "boot0" style NAND support for Allwinner A20 */ @@ -271,16 +272,17 @@ static void sunxi_nfc_set_user_data_len(const struct nfc_config *nfc, /* * Values in this table are obtained by doing: - * DIV_ROUND_UP(info->ecc_strength * 14, 8) + USER_DATA_SZ - * So it's the number of bytes needed for ECC + user data for one step. + * DIV_ROUND_UP(info->ecc_strength * 14, 8) + * So it's the number of bytes needed for ECC one step + * (not counting the user data length) */ #if defined(CONFIG_MACH_SUN50I_H616) || defined(CONFIG_MACH_SUN50I_H6) static const int ecc_bytes[] = { - 32, 46, 54, 60, 74, 82, 88, 96, 102, 110, 116, 124, 130, 138, 144 + 28, 42, 50, 56, 70, 78, 84, 92, 98, 106, 112, 120, 126, 134, 140 }; #else static const int ecc_bytes[] = { - 32, 46, 54, 60, 74, 88, 102, 110, 116 + 28, 42, 50, 56, 70, 84, 98, 106, 112 }; #endif @@ -293,6 +295,14 @@ static void nand_readlcpy(u32 *dest, u32 * __iomem src, size_t len) *dest++ = readl(src++); } +static u8 nand_user_data_sz(const struct nfc_config *conf, int step) +{ + if (!conf->user_data_bytes) + return USER_DATA_SZ; + + return conf->user_data_bytes[step]; +} + static int nand_read_page(const struct nfc_config *conf, u32 offs, void *dest, int len) { @@ -300,6 +310,7 @@ static int nand_read_page(const struct nfc_config *conf, u32 offs, u16 rand_seed = 0; int oob_chunk_sz = ecc_bytes[conf->ecc_strength]; int page = offs / conf->page_size; + int oob_off = conf->page_size; u32 ecc_st, pattern_found; int i; /* From the controller point of view, we are at step 0 */ @@ -316,9 +327,9 @@ static int nand_read_page(const struct nfc_config *conf, u32 offs, /* Retrieve data from SRAM (PIO) */ for (i = 0; i < nsectors; i++) { int data_off = i * conf->ecc_size; - int oob_off = conf->page_size + (i * oob_chunk_sz); u8 *data = dest + data_off; u32 ecc512_bit = 0; + unsigned int user_data_sz = nand_user_data_sz(conf, i); if (conf->caps->has_ecc_block_512 && conf->ecc_size == 512) ecc512_bit = NFC_ECC_BLOCK_512; @@ -345,7 +356,7 @@ static int nand_read_page(const struct nfc_config *conf, u32 offs, nand_change_column(oob_off); sunxi_nfc_reset_user_data_len(conf); - sunxi_nfc_set_user_data_len(conf, USER_DATA_SZ, nfc_step); + sunxi_nfc_set_user_data_len(conf, user_data_sz, nfc_step); nand_exec_cmd(NFC_DATA_TRANS | NFC_ECC_OP); /* Get the ECC status */ @@ -371,13 +382,61 @@ static int nand_read_page(const struct nfc_config *conf, u32 offs, nand_readlcpy((u32 *)data, (void *)(uintptr_t)SUNXI_NFC_BASE + NFC_RAM0_BASE, conf->ecc_size); - /* Stop the ECC engine */ writel_nfc(readl_nfc(NFC_REG_ECC_CTL) & ~NFC_ECC_EN, NFC_REG_ECC_CTL); if (data_off + conf->ecc_size >= len) break; + + oob_off += oob_chunk_sz + user_data_sz; + } + + return 0; +} + +static int nand_min_user_data_sz(struct nfc_config *conf, int nsectors) +{ + const struct sunxi_nfc_caps *c = conf->caps; + int min_user_data_sz = 0; + int i; + + if (!c->reg_user_data_len) { + for (i = 0; i < nsectors; i++) + min_user_data_sz += nand_user_data_sz(conf, i); + } else { + for (i = 0; i < c->nuser_data_tab; i++) + /* We want at least enough size for the BBM */ + if (c->user_data_len_tab[i] >= 2) + break; + min_user_data_sz = c->user_data_len_tab[i]; + } + + return min_user_data_sz; +} + +static int nand_maximize_user_data(struct nfc_config *conf, uint32_t oobsize, + int ecc_len, int nsectors) +{ + const struct sunxi_nfc_caps *c = conf->caps; + int remaining_bytes = oobsize - (ecc_len * nsectors); + int i, step; + + kfree(conf->user_data_bytes); + + conf->user_data_bytes = kzalloc(nsectors, GFP_KERNEL); + if (!conf->user_data_bytes) + return -ENOMEM; + + for (step = 0; (step < nsectors) && (remaining_bytes > 0); step++) { + for (i = 0; i < c->nuser_data_tab; i++) { + if (c->user_data_len_tab[i] > remaining_bytes) + break; + conf->user_data_bytes[step] = c->user_data_len_tab[i]; + } + remaining_bytes -= conf->user_data_bytes[step]; + if (conf->user_data_bytes[step] == 0) + break; } return 0; @@ -387,7 +446,8 @@ static int nand_max_ecc_strength(struct nfc_config *conf) { int max_oobsize, max_ecc_bytes; int nsectors = conf->page_size / conf->ecc_size; - int i; + unsigned int total_user_data_sz = 0; + int ecc_idx, i; /* * ECC strength is limited by the size of the OOB area which is @@ -412,15 +472,38 @@ static int nand_max_ecc_strength(struct nfc_config *conf) max_ecc_bytes = max_oobsize / nsectors; - for (i = 0; i < ARRAY_SIZE(ecc_bytes); i++) { - if (ecc_bytes[i] > max_ecc_bytes) + /* + * nand_min_user_data_sz() will return the total_user_data_sz in case + * of a fixed user data length, or the minimal usable user data size + * in case of variable data length (with at least enough space for the + * BBM. + */ + total_user_data_sz = nand_min_user_data_sz(conf, nsectors); + + for (ecc_idx = 0; ecc_idx < ARRAY_SIZE(ecc_bytes); ecc_idx++) { + if (ecc_bytes[ecc_idx] + total_user_data_sz > max_ecc_bytes) break; } - if (!i) + if (!ecc_idx) return -EINVAL; - return i - 1; + ecc_idx--; + + /* + * The rationale for variable data length is to prioritize maximum ECC + * strength, and then use the remaining space for user data. + */ + if (conf->caps->reg_user_data_len) { + nand_maximize_user_data(conf, max_oobsize, + ecc_bytes[ecc_idx], nsectors); + + total_user_data_sz = 0; + for (i = 0; i < nsectors; i++) + total_user_data_sz += nand_user_data_sz(conf, i); + } + + return ecc_idx; } static int nand_detect_ecc_config(struct nfc_config *conf, u32 offs, -- cgit v1.2.3 From 4c8d2a633ed22e90c6b1954b10b0f74fd678cb23 Mon Sep 17 00:00:00 2001 From: Richard Genoud Date: Fri, 27 Mar 2026 15:05:08 +0100 Subject: mtd: nand: raw: sunxi_spl: remove user data length reset No need to reset user data length registers in SPL. In SPL, only the first user data length register is used, so we don't need to reset all of them. Signed-off-by: Richard Genoud Acked-by: Andre Przywara --- drivers/mtd/nand/raw/sunxi_nand_spl.c | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/drivers/mtd/nand/raw/sunxi_nand_spl.c b/drivers/mtd/nand/raw/sunxi_nand_spl.c index 784ffb00cf7..cf351de4e8d 100644 --- a/drivers/mtd/nand/raw/sunxi_nand_spl.c +++ b/drivers/mtd/nand/raw/sunxi_nand_spl.c @@ -224,22 +224,6 @@ static int nand_change_column(u16 column) return 0; } -/* - * On H6/H616 the user_data length has to be set in specific registers - * before writing. - */ -static void sunxi_nfc_reset_user_data_len(const struct nfc_config *nfc) -{ - int loop_step = NFC_REG_USER_DATA_LEN_CAPACITY; - - /* not all SoCs have this register */ - if (!NFC_REG_USER_DATA_LEN(nfc, 0)) - return; - - for (int i = 0; i < nfc->caps->max_ecc_steps; i += loop_step) - writel_nfc(0, NFC_REG_USER_DATA_LEN(nfc, i)); -} - static void sunxi_nfc_set_user_data_len(const struct nfc_config *nfc, int len, int step) { @@ -355,7 +339,6 @@ static int nand_read_page(const struct nfc_config *conf, u32 offs, */ nand_change_column(oob_off); - sunxi_nfc_reset_user_data_len(conf); sunxi_nfc_set_user_data_len(conf, user_data_sz, nfc_step); nand_exec_cmd(NFC_DATA_TRANS | NFC_ECC_OP); -- cgit v1.2.3