aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS2
-rw-r--r--arch/arm/Kconfig1
-rw-r--r--arch/arm/dts/sun8i-h3-nanopi-neo.dts4
-rw-r--r--arch/arm/include/asm/arch-sunxi/gpio.h18
-rw-r--r--arch/arm/include/asm/arch-sunxi/i2c.h11
-rw-r--r--arch/arm/include/asm/arch-sunxi/prcm_sun50i.h10
-rw-r--r--arch/arm/include/asm/arch-sunxi/spl.h3
-rw-r--r--arch/arm/mach-imx/spl.c2
-rw-r--r--arch/arm/mach-k3/am642_init.c2
-rw-r--r--arch/arm/mach-k3/am6_init.c2
-rw-r--r--arch/arm/mach-k3/j721e_init.c2
-rw-r--r--arch/arm/mach-k3/j721s2_init.c2
-rw-r--r--arch/arm/mach-mvebu/spl.c2
-rw-r--r--arch/arm/mach-omap2/boot-common.c2
-rw-r--r--arch/arm/mach-rockchip/spl.c2
-rw-r--r--arch/arm/mach-socfpga/spl_a10.c2
-rw-r--r--arch/arm/mach-socfpga/spl_gen5.c2
-rw-r--r--arch/arm/mach-stm32mp/spl.c2
-rw-r--r--arch/arm/mach-sunxi/Kconfig24
-rw-r--r--arch/arm/mach-sunxi/board.c126
-rw-r--r--arch/arm/mach-sunxi/clock_sun50i_h6.c20
-rw-r--r--arch/arm/mach-sunxi/dram_sun50i_h6.c8
-rw-r--r--arch/arm/mach-sunxi/dram_sun50i_h616.c7
-rw-r--r--arch/arm/mach-sunxi/spl_spi_sunxi.c4
-rw-r--r--arch/arm/mach-uniphier/mmc-boot-mode.c5
-rw-r--r--board/sunxi/Kconfig24
-rw-r--r--board/sunxi/board.c59
-rw-r--r--board/sunxi/gmac.c55
-rw-r--r--boot/image.c1
-rw-r--r--common/spl/spl_mmc.c4
-rw-r--r--configs/bananapi_m64_defconfig1
-rw-r--r--configs/emlid_neutis_n5_devboard_defconfig1
-rw-r--r--configs/pine64-lts_defconfig1
-rw-r--r--configs/pine_h64_defconfig1
-rw-r--r--configs/sopine_baseboard_defconfig1
-rw-r--r--drivers/gpio/sunxi_gpio.c130
-rw-r--r--drivers/i2c/sun6i_p2wi.c27
-rw-r--r--drivers/i2c/sun8i_rsb.c61
-rw-r--r--drivers/net/sun8i_emac.c90
-rw-r--r--drivers/net/sunxi_emac.c7
-rw-r--r--drivers/pinctrl/Kconfig1
-rw-r--r--drivers/pinctrl/Makefile2
-rw-r--r--drivers/pinctrl/sunxi/Kconfig127
-rw-r--r--drivers/pinctrl/sunxi/Makefile3
-rw-r--r--drivers/pinctrl/sunxi/pinctrl-sunxi.c897
-rw-r--r--drivers/pwm/sunxi_pwm.c11
-rw-r--r--drivers/spi/spi-sunxi.c84
-rw-r--r--include/image.h1
-rw-r--r--include/spl.h3
-rw-r--r--include/sunxi_image.h37
-rw-r--r--scripts/Makefile.spl5
-rw-r--r--tools/Makefile7
-rw-r--r--tools/imagetool.h1
-rw-r--r--tools/mkimage.c1
-rw-r--r--tools/sunxi_egon.c73
-rw-r--r--tools/sunxi_toc0.c907
56 files changed, 2325 insertions, 563 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index aca97cd..f986530 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -521,7 +521,9 @@ F: arch/arm/mach-sunxi/
F: board/sunxi/
F: drivers/clk/sunxi/
F: drivers/phy/allwinner/
+F: drivers/pinctrl/sunxi/
F: drivers/video/sunxi/
+F: tools/sunxi*
ARM TEGRA
M: Tom Warren <twarren@nvidia.com>
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index f277929..6771f14 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1124,6 +1124,7 @@ config ARCH_SUNXI
select OF_BOARD_SETUP
select OF_CONTROL
select OF_SEPARATE
+ select PINCTRL
select SPECIFY_CONSOLE_INDEX
select SPL_SEPARATE_BSS if SPL
select SPL_STACK_R if SPL
diff --git a/arch/arm/dts/sun8i-h3-nanopi-neo.dts b/arch/arm/dts/sun8i-h3-nanopi-neo.dts
index 9f33f6f..df71fab 100644
--- a/arch/arm/dts/sun8i-h3-nanopi-neo.dts
+++ b/arch/arm/dts/sun8i-h3-nanopi-neo.dts
@@ -45,6 +45,10 @@
/ {
model = "FriendlyARM NanoPi NEO";
compatible = "friendlyarm,nanopi-neo", "allwinner,sun8i-h3";
+
+ aliases {
+ ethernet0 = &emac;
+ };
};
&ehci0 {
diff --git a/arch/arm/include/asm/arch-sunxi/gpio.h b/arch/arm/include/asm/arch-sunxi/gpio.h
index edd0fbf..437e864 100644
--- a/arch/arm/include/asm/arch-sunxi/gpio.h
+++ b/arch/arm/include/asm/arch-sunxi/gpio.h
@@ -135,17 +135,13 @@ enum sunxi_gpio_number {
#define SUNXI_GPIO_OUTPUT 1
#define SUNXI_GPIO_DISABLE 7
-#define SUNXI_GPA_EMAC 2
-#define SUN6I_GPA_GMAC 2
-#define SUN7I_GPA_GMAC 5
#define SUN8I_H3_GPA_UART0 2
+#define SUN8I_H3_GPA_UART2 2
#define SUN4I_GPB_PWM 2
#define SUN4I_GPB_TWI0 2
#define SUN4I_GPB_TWI1 2
#define SUN5I_GPB_TWI1 2
-#define SUN4I_GPB_TWI2 2
-#define SUN5I_GPB_TWI2 2
#define SUN8I_V3S_GPB_TWI0 2
#define SUN4I_GPB_UART0 2
#define SUN5I_GPB_UART0 2
@@ -164,11 +160,8 @@ enum sunxi_gpio_number {
#define SUNXI_GPD_LCD0 2
#define SUNXI_GPD_LVDS0 3
-#define SUNXI_GPD_PWM 2
#define SUNIV_GPE_UART0 5
-#define SUN8I_GPE_TWI2 3
-#define SUN50I_GPE_TWI2 3
#define SUNXI_GPF_SDC0 2
#define SUNXI_GPF_UART0 4
@@ -179,7 +172,6 @@ enum sunxi_gpio_number {
#define SUN6I_GPG_SDC1 2
#define SUN8I_GPG_SDC1 2
#define SUN8I_GPG_UART1 2
-#define SUN6I_GPG_TWI3 2
#define SUN5I_GPG_UART1 4
#define SUN6I_GPH_PWM 2
@@ -191,15 +183,12 @@ enum sunxi_gpio_number {
#define SUN6I_GPH_TWI1 2
#define SUN8I_GPH_TWI1 2
#define SUN50I_GPH_TWI1 2
-#define SUN6I_GPH_TWI2 2
#define SUN6I_GPH_UART0 2
#define SUN9I_GPH_UART0 2
#define SUN50I_H6_GPH_UART0 2
#define SUN50I_H616_GPH_UART0 2
#define SUNXI_GPI_SDC3 2
-#define SUN7I_GPI_TWI3 3
-#define SUN7I_GPI_TWI4 3
#define SUN6I_GPL0_R_P2WI_SCK 3
#define SUN6I_GPL1_R_P2WI_SDA 3
@@ -224,6 +213,11 @@ enum sunxi_gpio_number {
#define SUNXI_GPIO_AXP0_VBUS_ENABLE 5
#define SUNXI_GPIO_AXP0_GPIO_COUNT 6
+struct sunxi_gpio_plat {
+ struct sunxi_gpio *regs;
+ char bank_name[3];
+};
+
void sunxi_gpio_set_cfgbank(struct sunxi_gpio *pio, int bank_offset, u32 val);
void sunxi_gpio_set_cfgpin(u32 pin, u32 val);
int sunxi_gpio_get_cfgbank(struct sunxi_gpio *pio, int bank_offset);
diff --git a/arch/arm/include/asm/arch-sunxi/i2c.h b/arch/arm/include/asm/arch-sunxi/i2c.h
index 1cb2ba6..3525f22 100644
--- a/arch/arm/include/asm/arch-sunxi/i2c.h
+++ b/arch/arm/include/asm/arch-sunxi/i2c.h
@@ -13,17 +13,8 @@
#ifdef CONFIG_I2C1_ENABLE
#define CONFIG_I2C_MVTWSI_BASE1 SUNXI_TWI1_BASE
#endif
-#ifdef CONFIG_I2C2_ENABLE
-#define CONFIG_I2C_MVTWSI_BASE2 SUNXI_TWI2_BASE
-#endif
-#ifdef CONFIG_I2C3_ENABLE
-#define CONFIG_I2C_MVTWSI_BASE3 SUNXI_TWI3_BASE
-#endif
-#ifdef CONFIG_I2C4_ENABLE
-#define CONFIG_I2C_MVTWSI_BASE4 SUNXI_TWI4_BASE
-#endif
#ifdef CONFIG_R_I2C_ENABLE
-#define CONFIG_I2C_MVTWSI_BASE5 SUNXI_R_TWI_BASE
+#define CONFIG_I2C_MVTWSI_BASE2 SUNXI_R_TWI_BASE
#endif
/* This is abp0-clk on sun4i/5i/7i / abp1-clk on sun6i/sun8i which is 24MHz */
diff --git a/arch/arm/include/asm/arch-sunxi/prcm_sun50i.h b/arch/arm/include/asm/arch-sunxi/prcm_sun50i.h
index 5f636e8..fd63d3a 100644
--- a/arch/arm/include/asm/arch-sunxi/prcm_sun50i.h
+++ b/arch/arm/include/asm/arch-sunxi/prcm_sun50i.h
@@ -37,8 +37,18 @@ struct sunxi_prcm_reg {
u32 w1_gate_reset; /* 0x1ec */
u8 res10[0x1c]; /* 0x1f0 */
u32 rtc_gate_reset; /* 0x20c */
+ u8 res11[0x34]; /* 0x210 */
+ u32 pll_ldo_cfg; /* 0x244 */
+ u8 res12[0x8]; /* 0x248 */
+ u32 sys_pwroff_gating; /* 0x250 */
+ u8 res13[0xbc]; /* 0x254 */
+ u32 res_cal_ctrl; /* 0x310 */
+ u32 ohms200; /* 0x314 */
+ u32 ohms240; /* 0x318 */
+ u32 res_cal_status; /* 0x31c */
};
check_member(sunxi_prcm_reg, rtc_gate_reset, 0x20c);
+check_member(sunxi_prcm_reg, res_cal_status, 0x31c);
#define PRCM_TWI_GATE (1 << 0)
#define PRCM_TWI_RESET (1 << 16)
diff --git a/arch/arm/include/asm/arch-sunxi/spl.h b/arch/arm/include/asm/arch-sunxi/spl.h
index b543d24..14944a2 100644
--- a/arch/arm/include/asm/arch-sunxi/spl.h
+++ b/arch/arm/include/asm/arch-sunxi/spl.h
@@ -28,8 +28,7 @@
#define SUNIV_BOOTED_FROM_SPI 0xffff4130
#define SUNIV_BOOTED_FROM_MMC1 0xffff4150
-#define is_boot0_magic(addr) (memcmp((void *)(addr), BOOT0_MAGIC, 8) == 0)
-
uint32_t sunxi_get_boot_device(void);
+uint32_t sunxi_get_spl_size(void);
#endif
diff --git a/arch/arm/mach-imx/spl.c b/arch/arm/mach-imx/spl.c
index 2832b73..64ca296 100644
--- a/arch/arm/mach-imx/spl.c
+++ b/arch/arm/mach-imx/spl.c
@@ -201,7 +201,7 @@ int g_dnl_get_board_bcd_device_number(int gcnum)
#if defined(CONFIG_SPL_MMC)
/* called from spl_mmc to see type of boot mode for storage (RAW or FAT) */
-u32 spl_mmc_boot_mode(const u32 boot_device)
+u32 spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device)
{
#if defined(CONFIG_MX7) || defined(CONFIG_IMX8M) || defined(CONFIG_IMX8)
switch (get_boot_device()) {
diff --git a/arch/arm/mach-k3/am642_init.c b/arch/arm/mach-k3/am642_init.c
index 543dea0..eabfd57 100644
--- a/arch/arm/mach-k3/am642_init.c
+++ b/arch/arm/mach-k3/am642_init.c
@@ -208,7 +208,7 @@ void board_init_f(ulong dummy)
}
}
-u32 spl_mmc_boot_mode(const u32 boot_device)
+u32 spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device)
{
switch (boot_device) {
case BOOT_DEVICE_MMC1:
diff --git a/arch/arm/mach-k3/am6_init.c b/arch/arm/mach-k3/am6_init.c
index 8a6b1de..86c1a34 100644
--- a/arch/arm/mach-k3/am6_init.c
+++ b/arch/arm/mach-k3/am6_init.c
@@ -269,7 +269,7 @@ void board_init_f(ulong dummy)
spl_enable_dcache();
}
-u32 spl_mmc_boot_mode(const u32 boot_device)
+u32 spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device)
{
#if defined(CONFIG_SUPPORT_EMMC_BOOT)
u32 devstat = readl(CTRLMMR_MAIN_DEVSTAT);
diff --git a/arch/arm/mach-k3/j721e_init.c b/arch/arm/mach-k3/j721e_init.c
index c4b6b18..f503f15 100644
--- a/arch/arm/mach-k3/j721e_init.c
+++ b/arch/arm/mach-k3/j721e_init.c
@@ -291,7 +291,7 @@ void board_init_f(ulong dummy)
spl_enable_dcache();
}
-u32 spl_mmc_boot_mode(const u32 boot_device)
+u32 spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device)
{
switch (boot_device) {
case BOOT_DEVICE_MMC1:
diff --git a/arch/arm/mach-k3/j721s2_init.c b/arch/arm/mach-k3/j721s2_init.c
index 58a8654..2e64e44 100644
--- a/arch/arm/mach-k3/j721s2_init.c
+++ b/arch/arm/mach-k3/j721s2_init.c
@@ -173,7 +173,7 @@ void board_init_f(ulong dummy)
spl_enable_dcache();
}
-u32 spl_mmc_boot_mode(const u32 boot_device)
+u32 spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device)
{
switch (boot_device) {
case BOOT_DEVICE_MMC1:
diff --git a/arch/arm/mach-mvebu/spl.c b/arch/arm/mach-mvebu/spl.c
index 5ad323f..fa9a1d7 100644
--- a/arch/arm/mach-mvebu/spl.c
+++ b/arch/arm/mach-mvebu/spl.c
@@ -96,7 +96,7 @@ struct kwbimage_main_hdr_v1 {
} __packed;
#ifdef CONFIG_SPL_MMC
-u32 spl_mmc_boot_mode(const u32 boot_device)
+u32 spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device)
{
return MMCSD_MODE_RAW;
}
diff --git a/arch/arm/mach-omap2/boot-common.c b/arch/arm/mach-omap2/boot-common.c
index afc3585..c463c96 100644
--- a/arch/arm/mach-omap2/boot-common.c
+++ b/arch/arm/mach-omap2/boot-common.c
@@ -196,7 +196,7 @@ u32 spl_boot_device(void)
return gd->arch.omap_boot_device;
}
-u32 spl_mmc_boot_mode(const u32 boot_device)
+u32 spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device)
{
return gd->arch.omap_boot_mode;
}
diff --git a/arch/arm/mach-rockchip/spl.c b/arch/arm/mach-rockchip/spl.c
index 7a8db63..d51a072 100644
--- a/arch/arm/mach-rockchip/spl.c
+++ b/arch/arm/mach-rockchip/spl.c
@@ -66,7 +66,7 @@ u32 spl_boot_device(void)
return boot_device;
}
-u32 spl_mmc_boot_mode(const u32 boot_device)
+u32 spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device)
{
return MMCSD_MODE_RAW;
}
diff --git a/arch/arm/mach-socfpga/spl_a10.c b/arch/arm/mach-socfpga/spl_a10.c
index d2f454c..ec67a5b 100644
--- a/arch/arm/mach-socfpga/spl_a10.c
+++ b/arch/arm/mach-socfpga/spl_a10.c
@@ -99,7 +99,7 @@ u32 spl_boot_device(void)
}
#ifdef CONFIG_SPL_MMC
-u32 spl_mmc_boot_mode(const u32 boot_device)
+u32 spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device)
{
#if defined(CONFIG_SPL_FS_FAT) || defined(CONFIG_SPL_FS_EXT4)
return MMCSD_MODE_FS;
diff --git a/arch/arm/mach-socfpga/spl_gen5.c b/arch/arm/mach-socfpga/spl_gen5.c
index 441d893..287fbd1 100644
--- a/arch/arm/mach-socfpga/spl_gen5.c
+++ b/arch/arm/mach-socfpga/spl_gen5.c
@@ -53,7 +53,7 @@ u32 spl_boot_device(void)
}
#ifdef CONFIG_SPL_MMC
-u32 spl_mmc_boot_mode(const u32 boot_device)
+u32 spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device)
{
#if defined(CONFIG_SPL_FS_FAT) || defined(CONFIG_SPL_FS_EXT4)
return MMCSD_MODE_FS;
diff --git a/arch/arm/mach-stm32mp/spl.c b/arch/arm/mach-stm32mp/spl.c
index 51fe069..78fa9d7 100644
--- a/arch/arm/mach-stm32mp/spl.c
+++ b/arch/arm/mach-stm32mp/spl.c
@@ -55,7 +55,7 @@ u32 spl_boot_device(void)
return BOOT_DEVICE_MMC1;
}
-u32 spl_mmc_boot_mode(const u32 boot_device)
+u32 spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device)
{
return MMCSD_MODE_RAW;
}
diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
index 73da6b8..1f43b25 100644
--- a/arch/arm/mach-sunxi/Kconfig
+++ b/arch/arm/mach-sunxi/Kconfig
@@ -755,20 +755,6 @@ config I2C1_ENABLE
---help---
See I2C0_ENABLE help text.
-config I2C2_ENABLE
- bool "Enable I2C/TWI controller 2"
- select CMD_I2C
- ---help---
- See I2C0_ENABLE help text.
-
-if MACH_SUN6I || MACH_SUN7I
-config I2C3_ENABLE
- bool "Enable I2C/TWI controller 3"
- select CMD_I2C
- ---help---
- See I2C0_ENABLE help text.
-endif
-
if SUNXI_GEN_SUN6I || SUN50I_GEN_H6
config R_I2C_ENABLE
bool "Enable the PRCM I2C/TWI controller"
@@ -779,14 +765,6 @@ config R_I2C_ENABLE
Set this to y to enable the I2C controller which is part of the PRCM.
endif
-if MACH_SUN7I
-config I2C4_ENABLE
- bool "Enable I2C/TWI controller 4"
- select CMD_I2C
- ---help---
- See I2C0_ENABLE help text.
-endif
-
config AXP_GPIO
bool "Enable support for gpio-s on axp PMICs"
depends on AXP_PMIC_BUS
@@ -1069,6 +1047,8 @@ config BLUETOOTH_DT_DEVICE_FIXUP
The used address is "bdaddr" if set, and "ethaddr" with the LSB
flipped elsewise.
+source "board/sunxi/Kconfig"
+
endif
config CHIP_DIP_SCAN
diff --git a/arch/arm/mach-sunxi/board.c b/arch/arm/mach-sunxi/board.c
index 9a7673d..173e946 100644
--- a/arch/arm/mach-sunxi/board.c
+++ b/arch/arm/mach-sunxi/board.c
@@ -150,6 +150,10 @@ static int gpio_init(void)
sunxi_gpio_set_cfgpin(SUNXI_GPG(3), SUN5I_GPG_UART1);
sunxi_gpio_set_cfgpin(SUNXI_GPG(4), SUN5I_GPG_UART1);
sunxi_gpio_set_pull(SUNXI_GPG(4), SUNXI_GPIO_PULL_UP);
+#elif CONFIG_CONS_INDEX == 3 && defined(CONFIG_MACH_SUN8I_H3)
+ sunxi_gpio_set_cfgpin(SUNXI_GPA(0), SUN8I_H3_GPA_UART2);
+ sunxi_gpio_set_cfgpin(SUNXI_GPA(1), SUN8I_H3_GPA_UART2);
+ sunxi_gpio_set_pull(SUNXI_GPA(1), SUNXI_GPIO_PULL_UP);
#elif CONFIG_CONS_INDEX == 3 && defined(CONFIG_MACH_SUN8I)
sunxi_gpio_set_cfgpin(SUNXI_GPB(0), SUN8I_GPB_UART2);
sunxi_gpio_set_cfgpin(SUNXI_GPB(1), SUN8I_GPB_UART2);
@@ -213,8 +217,21 @@ static int suniv_get_boot_source(void)
return SUNXI_INVALID_BOOT_SOURCE;
}
+static int sunxi_egon_valid(struct boot_file_head *egon_head)
+{
+ return !memcmp(egon_head->magic, BOOT0_MAGIC, 8); /* eGON.BT0 */
+}
+
+static int sunxi_toc0_valid(struct toc0_main_info *toc0_info)
+{
+ return !memcmp(toc0_info->name, TOC0_MAIN_INFO_NAME, 8); /* TOC0.GLH */
+}
+
static int sunxi_get_boot_source(void)
{
+ struct boot_file_head *egon_head = (void *)SPL_ADDR;
+ struct toc0_main_info *toc0_info = (void *)SPL_ADDR;
+
/*
* On the ARMv5 SoCs, the SPL header in SRAM is overwritten by the
* exception vectors in U-Boot proper, so we won't find any
@@ -226,13 +243,15 @@ static int sunxi_get_boot_source(void)
!IS_ENABLED(CONFIG_SPL_BUILD))
return SUNXI_BOOTED_FROM_MMC0;
- if (!is_boot0_magic(SPL_ADDR + 4)) /* eGON.BT0 */
- return SUNXI_INVALID_BOOT_SOURCE;
-
if (IS_ENABLED(CONFIG_MACH_SUNIV))
return suniv_get_boot_source();
- else
- return readb(SPL_ADDR + 0x28);
+ if (sunxi_egon_valid(egon_head))
+ return readb(&egon_head->boot_media);
+ if (sunxi_toc0_valid(toc0_info))
+ return readb(&toc0_info->platform[0]);
+
+ /* Not a valid image, so we must have been booted via FEL. */
+ return SUNXI_INVALID_BOOT_SOURCE;
}
/* The sunxi internal brom will try to loader external bootloader
@@ -278,12 +297,18 @@ uint32_t sunxi_get_boot_device(void)
}
#ifdef CONFIG_SPL_BUILD
-static u32 sunxi_get_spl_size(void)
+uint32_t sunxi_get_spl_size(void)
{
- if (!is_boot0_magic(SPL_ADDR + 4)) /* eGON.BT0 */
- return 0;
+ struct boot_file_head *egon_head = (void *)SPL_ADDR;
+ struct toc0_main_info *toc0_info = (void *)SPL_ADDR;
+
+ if (sunxi_egon_valid(egon_head))
+ return readl(&egon_head->length);
+ if (sunxi_toc0_valid(toc0_info))
+ return readl(&toc0_info->length);
- return readl(SPL_ADDR + 0x10);
+ /* Not a valid image, so use the default U-Boot offset. */
+ return 0;
}
/*
@@ -321,6 +346,89 @@ __weak void sunxi_sram_init(void)
{
}
+/*
+ * When booting from an eMMC boot partition, the SPL puts the same boot
+ * source code into SRAM A1 as when loading the SPL from the normal
+ * eMMC user data partition: 0x2. So to know where we have been loaded
+ * from, we repeat the BROM algorithm here: checking for a valid eGON boot
+ * image at offset 0 of a (potentially) selected boot partition.
+ * If any of the conditions is not met, it must have been the eMMC user
+ * data partition.
+ */
+static bool sunxi_valid_emmc_boot(struct mmc *mmc)
+{
+ struct blk_desc *bd = mmc_get_blk_desc(mmc);
+ uint32_t *buffer = (void *)(uintptr_t)CONFIG_SYS_TEXT_BASE;
+ struct boot_file_head *egon_head = (void *)buffer;
+ int bootpart = EXT_CSD_EXTRACT_BOOT_PART(mmc->part_config);
+ uint32_t spl_size, emmc_checksum, chksum = 0;
+ ulong count;
+
+ /* The BROM requires BOOT_ACK to be enabled. */
+ if (!EXT_CSD_EXTRACT_BOOT_ACK(mmc->part_config))
+ return false;
+
+ /*
+ * The BOOT_BUS_CONDITION register must be 4-bit SDR, with (0x09)
+ * or without (0x01) high speed timings.
+ */
+ if ((mmc->ext_csd[EXT_CSD_BOOT_BUS_WIDTH] & 0x1b) != 0x01 &&
+ (mmc->ext_csd[EXT_CSD_BOOT_BUS_WIDTH] & 0x1b) != 0x09)
+ return false;
+
+ /* Partition 0 is the user data partition, bootpart must be 1 or 2. */
+ if (bootpart != 1 && bootpart != 2)
+ return false;
+
+ /* Failure to switch to the boot partition is fatal. */
+ if (mmc_switch_part(mmc, bootpart))
+ return false;
+
+ /* Read the first block to do some sanity checks on the eGON header. */
+ count = blk_dread(bd, 0, 1, buffer);
+ if (count != 1 || !sunxi_egon_valid(egon_head))
+ return false;
+
+ /* Read the rest of the SPL now we know it's halfway sane. */
+ spl_size = buffer[4];
+ count = blk_dread(bd, 1, DIV_ROUND_UP(spl_size, bd->blksz) - 1,
+ buffer + bd->blksz / 4);
+
+ /* Save the checksum and replace it with the "stamp value". */
+ emmc_checksum = buffer[3];
+ buffer[3] = 0x5f0a6c39;
+
+ /* The checksum is a simple ignore-carry addition of all words. */
+ for (count = 0; count < spl_size / 4; count++)
+ chksum += buffer[count];
+
+ debug("eMMC boot part SPL checksum: stored: 0x%08x, computed: 0x%08x\n",
+ emmc_checksum, chksum);
+
+ return emmc_checksum == chksum;
+}
+
+u32 spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device)
+{
+ static u32 result = ~0;
+
+ if (result != ~0)
+ return result;
+
+ result = MMCSD_MODE_RAW;
+ if (!IS_SD(mmc) && IS_ENABLED(CONFIG_SUPPORT_EMMC_BOOT)) {
+ if (sunxi_valid_emmc_boot(mmc))
+ result = MMCSD_MODE_EMMCBOOT;
+ else
+ mmc_switch_part(mmc, 0);
+ }
+
+ debug("%s(): %s part\n", __func__,
+ result == MMCSD_MODE_RAW ? "user" : "boot");
+
+ return result;
+}
+
void board_init_f(ulong dummy)
{
sunxi_sram_init();
diff --git a/arch/arm/mach-sunxi/clock_sun50i_h6.c b/arch/arm/mach-sunxi/clock_sun50i_h6.c
index a947463..7926394 100644
--- a/arch/arm/mach-sunxi/clock_sun50i_h6.c
+++ b/arch/arm/mach-sunxi/clock_sun50i_h6.c
@@ -9,10 +9,24 @@ void clock_init_safe(void)
{
struct sunxi_ccm_reg *const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+ struct sunxi_prcm_reg *const prcm =
+ (struct sunxi_prcm_reg *)SUNXI_PRCM_BASE;
+
+ if (IS_ENABLED(CONFIG_MACH_SUN50I_H616)) {
+ /* this seems to enable PLLs on H616 */
+ setbits_le32(&prcm->sys_pwroff_gating, 0x10);
+ setbits_le32(&prcm->res_cal_ctrl, 2);
+ }
+
+ clrbits_le32(&prcm->res_cal_ctrl, 1);
+ setbits_le32(&prcm->res_cal_ctrl, 1);
- /* this seems to enable PLLs on H616 */
- if (IS_ENABLED(CONFIG_MACH_SUN50I_H616))
- setbits_le32(SUNXI_PRCM_BASE + 0x250, 0x10);
+ if (IS_ENABLED(CONFIG_MACH_SUN50I_H6)) {
+ /* set key field for ldo enable */
+ setbits_le32(&prcm->pll_ldo_cfg, 0xA7000000);
+ /* set PLL VDD LDO output to 1.14 V */
+ setbits_le32(&prcm->pll_ldo_cfg, 0x60000);
+ }
clock_set_pll1(408000000);
diff --git a/arch/arm/mach-sunxi/dram_sun50i_h6.c b/arch/arm/mach-sunxi/dram_sun50i_h6.c
index d05375c..b332f3a 100644
--- a/arch/arm/mach-sunxi/dram_sun50i_h6.c
+++ b/arch/arm/mach-sunxi/dram_sun50i_h6.c
@@ -12,6 +12,7 @@
#include <asm/arch/clock.h>
#include <asm/arch/dram.h>
#include <asm/arch/cpu.h>
+#include <asm/arch/prcm.h>
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/kconfig.h>
@@ -665,6 +666,8 @@ unsigned long sunxi_dram_init(void)
{
struct sunxi_mctl_com_reg * const mctl_com =
(struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE;
+ struct sunxi_prcm_reg *const prcm =
+ (struct sunxi_prcm_reg *)SUNXI_PRCM_BASE;
struct dram_para para = {
.clk = CONFIG_DRAM_CLK,
#ifdef CONFIG_SUNXI_DRAM_H6_LPDDR3
@@ -680,9 +683,8 @@ unsigned long sunxi_dram_init(void)
unsigned long size;
- /* RES_CAL_CTRL_REG in BSP U-boot*/
- setbits_le32(0x7010310, BIT(8));
- clrbits_le32(0x7010318, 0x3f);
+ setbits_le32(&prcm->res_cal_ctrl, BIT(8));
+ clrbits_le32(&prcm->ohms240, 0x3f);
mctl_auto_detect_rank_width(&para);
mctl_auto_detect_dram_size(&para);
diff --git a/arch/arm/mach-sunxi/dram_sun50i_h616.c b/arch/arm/mach-sunxi/dram_sun50i_h616.c
index 83e8abc..454c845 100644
--- a/arch/arm/mach-sunxi/dram_sun50i_h616.c
+++ b/arch/arm/mach-sunxi/dram_sun50i_h616.c
@@ -19,6 +19,7 @@
#include <asm/arch/clock.h>
#include <asm/arch/dram.h>
#include <asm/arch/cpu.h>
+#include <asm/arch/prcm.h>
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/kconfig.h>
@@ -1001,14 +1002,16 @@ static unsigned long mctl_calc_size(struct dram_para *para)
unsigned long sunxi_dram_init(void)
{
+ struct sunxi_prcm_reg *const prcm =
+ (struct sunxi_prcm_reg *)SUNXI_PRCM_BASE;
struct dram_para para = {
.clk = CONFIG_DRAM_CLK,
.type = SUNXI_DRAM_TYPE_DDR3,
};
unsigned long size;
- setbits_le32(0x7010310, BIT(8));
- clrbits_le32(0x7010318, 0x3f);
+ setbits_le32(&prcm->res_cal_ctrl, BIT(8));
+ clrbits_le32(&prcm->ohms240, 0x3f);
mctl_auto_detect_rank_width(&para);
mctl_auto_detect_dram_size(&para);
diff --git a/arch/arm/mach-sunxi/spl_spi_sunxi.c b/arch/arm/mach-sunxi/spl_spi_sunxi.c
index 734c165..de9aa68 100644
--- a/arch/arm/mach-sunxi/spl_spi_sunxi.c
+++ b/arch/arm/mach-sunxi/spl_spi_sunxi.c
@@ -337,9 +337,9 @@ static int spl_spi_load_image(struct spl_image_info *spl_image,
int ret = 0;
struct image_header *header;
header = (struct image_header *)(CONFIG_SYS_TEXT_BASE);
- int load_offset = readl(SPL_ADDR + 0x10);
+ uint32_t load_offset = sunxi_get_spl_size();
- load_offset = max(load_offset, CONFIG_SYS_SPI_U_BOOT_OFFS);
+ load_offset = max_t(uint32_t, load_offset, CONFIG_SYS_SPI_U_BOOT_OFFS);
spi0_init();
diff --git a/arch/arm/mach-uniphier/mmc-boot-mode.c b/arch/arm/mach-uniphier/mmc-boot-mode.c
index e47e5df..09cad74 100644
--- a/arch/arm/mach-uniphier/mmc-boot-mode.c
+++ b/arch/arm/mach-uniphier/mmc-boot-mode.c
@@ -7,10 +7,8 @@
#include <mmc.h>
#include <spl.h>
-u32 spl_mmc_boot_mode(const u32 boot_device)
+u32 spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device)
{
- struct mmc *mmc;
-
/*
* work around a bug in the Boot ROM of LD4, Pro4, and sLD8:
*
@@ -24,7 +22,6 @@ u32 spl_mmc_boot_mode(const u32 boot_device)
* Fixup mmc->part_config here because it is used to determine the
* partition which the U-Boot image is read from.
*/
- mmc = find_mmc_device(0);
mmc->part_config &= ~EXT_CSD_BOOT_PART_NUM(PART_ACCESS_MASK);
mmc->part_config |= EXT_CSD_BOOT_PARTITION_ENABLE;
diff --git a/board/sunxi/Kconfig b/board/sunxi/Kconfig
new file mode 100644
index 0000000..084a8b0
--- /dev/null
+++ b/board/sunxi/Kconfig
@@ -0,0 +1,24 @@
+choice
+ prompt "SPL Image Type"
+ default SPL_IMAGE_TYPE_SUNXI_EGON
+
+config SPL_IMAGE_TYPE_SUNXI_EGON
+ bool "eGON (normal)"
+ help
+ Select this option to embed the SPL binary in an eGON.BT0 image,
+ which is compatible with the normal boot ROM (NBROM).
+
+ This is usually the correct option to choose.
+
+config SPL_IMAGE_TYPE_SUNXI_TOC0
+ bool "TOC0 (secure)"
+ help
+ Select this option to embed the SPL binary in a TOC0 image,
+ which is compatible with the secure boot ROM (SBROM).
+
+endchoice
+
+config SPL_IMAGE_TYPE
+ string
+ default "sunxi_egon" if SPL_IMAGE_TYPE_SUNXI_EGON
+ default "sunxi_toc0" if SPL_IMAGE_TYPE_SUNXI_TOC0
diff --git a/board/sunxi/board.c b/board/sunxi/board.c
index 28f702b..8932415 100644
--- a/board/sunxi/board.c
+++ b/board/sunxi/board.c
@@ -107,54 +107,6 @@ void i2c_init_board(void)
#endif
#endif
-#ifdef CONFIG_I2C2_ENABLE
-#if defined(CONFIG_MACH_SUN4I) || \
- defined(CONFIG_MACH_SUN7I) || \
- defined(CONFIG_MACH_SUN8I_R40)
- sunxi_gpio_set_cfgpin(SUNXI_GPB(20), SUN4I_GPB_TWI2);
- sunxi_gpio_set_cfgpin(SUNXI_GPB(21), SUN4I_GPB_TWI2);
- clock_twi_onoff(2, 1);
-#elif defined(CONFIG_MACH_SUN5I)
- sunxi_gpio_set_cfgpin(SUNXI_GPB(17), SUN5I_GPB_TWI2);
- sunxi_gpio_set_cfgpin(SUNXI_GPB(18), SUN5I_GPB_TWI2);
- clock_twi_onoff(2, 1);
-#elif defined(CONFIG_MACH_SUN6I)
- sunxi_gpio_set_cfgpin(SUNXI_GPH(18), SUN6I_GPH_TWI2);
- sunxi_gpio_set_cfgpin(SUNXI_GPH(19), SUN6I_GPH_TWI2);
- clock_twi_onoff(2, 1);
-#elif defined(CONFIG_MACH_SUN8I)
- sunxi_gpio_set_cfgpin(SUNXI_GPE(12), SUN8I_GPE_TWI2);
- sunxi_gpio_set_cfgpin(SUNXI_GPE(13), SUN8I_GPE_TWI2);
- clock_twi_onoff(2, 1);
-#elif defined(CONFIG_MACH_SUN50I)
- sunxi_gpio_set_cfgpin(SUNXI_GPE(14), SUN50I_GPE_TWI2);
- sunxi_gpio_set_cfgpin(SUNXI_GPE(15), SUN50I_GPE_TWI2);
- clock_twi_onoff(2, 1);
-#endif
-#endif
-
-#ifdef CONFIG_I2C3_ENABLE
-#if defined(CONFIG_MACH_SUN6I)
- sunxi_gpio_set_cfgpin(SUNXI_GPG(10), SUN6I_GPG_TWI3);
- sunxi_gpio_set_cfgpin(SUNXI_GPG(11), SUN6I_GPG_TWI3);
- clock_twi_onoff(3, 1);
-#elif defined(CONFIG_MACH_SUN7I) || \
- defined(CONFIG_MACH_SUN8I_R40)
- sunxi_gpio_set_cfgpin(SUNXI_GPI(0), SUN7I_GPI_TWI3);
- sunxi_gpio_set_cfgpin(SUNXI_GPI(1), SUN7I_GPI_TWI3);
- clock_twi_onoff(3, 1);
-#endif
-#endif
-
-#ifdef CONFIG_I2C4_ENABLE
-#if defined(CONFIG_MACH_SUN7I) || \
- defined(CONFIG_MACH_SUN8I_R40)
- sunxi_gpio_set_cfgpin(SUNXI_GPI(2), SUN7I_GPI_TWI4);
- sunxi_gpio_set_cfgpin(SUNXI_GPI(3), SUN7I_GPI_TWI4);
- clock_twi_onoff(4, 1);
-#endif
-#endif
-
#ifdef CONFIG_R_I2C_ENABLE
#ifdef CONFIG_MACH_SUN50I
clock_twi_onoff(5, 1);
@@ -298,17 +250,6 @@ int board_init(void)
i2c_init_board();
#endif
-#ifdef CONFIG_DM_MMC
- /*
- * Temporary workaround for enabling MMC clocks until a sunxi DM
- * pinctrl driver lands.
- */
- mmc_pinmux_setup(CONFIG_MMC_SUNXI_SLOT);
-#if CONFIG_MMC_SUNXI_SLOT_EXTRA != -1
- mmc_pinmux_setup(CONFIG_MMC_SUNXI_SLOT_EXTRA);
-#endif
-#endif /* CONFIG_DM_MMC */
-
eth_init_board();
return 0;
diff --git a/board/sunxi/gmac.c b/board/sunxi/gmac.c
index 1fa54ed..2a88530 100644
--- a/board/sunxi/gmac.c
+++ b/board/sunxi/gmac.c
@@ -1,13 +1,11 @@
#include <common.h>
#include <netdev.h>
#include <miiphy.h>
-#include <asm/gpio.h>
#include <asm/io.h>
#include <asm/arch/clock.h>
void eth_init_board(void)
{
- int pin;
struct sunxi_ccm_reg *const ccm =
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
@@ -21,57 +19,4 @@ void eth_init_board(void)
setbits_le32(&ccm->gmac_clk_cfg, CCM_GMAC_CTRL_TX_CLK_SRC_MII |
CCM_GMAC_CTRL_GPIT_MII);
#endif
-
-#ifndef CONFIG_MACH_SUN6I
- /* Configure pin mux settings for GMAC */
-#ifdef CONFIG_SUN7I_GMAC_FORCE_TXERR
- for (pin = SUNXI_GPA(0); pin <= SUNXI_GPA(17); pin++) {
-#else
- for (pin = SUNXI_GPA(0); pin <= SUNXI_GPA(16); pin++) {
-#endif
-#ifdef CONFIG_RGMII
- /* skip unused pins in RGMII mode */
- if (pin == SUNXI_GPA(9) || pin == SUNXI_GPA(14))
- continue;
-#endif
- sunxi_gpio_set_cfgpin(pin, SUN7I_GPA_GMAC);
- sunxi_gpio_set_drv(pin, 3);
- }
-#elif defined CONFIG_RGMII
- /* Configure sun6i RGMII mode pin mux settings */
- for (pin = SUNXI_GPA(0); pin <= SUNXI_GPA(3); pin++) {
- sunxi_gpio_set_cfgpin(pin, SUN6I_GPA_GMAC);
- sunxi_gpio_set_drv(pin, 3);
- }
- for (pin = SUNXI_GPA(9); pin <= SUNXI_GPA(14); pin++) {
- sunxi_gpio_set_cfgpin(pin, SUN6I_GPA_GMAC);
- sunxi_gpio_set_drv(pin, 3);
- }
- for (pin = SUNXI_GPA(19); pin <= SUNXI_GPA(20); pin++) {
- sunxi_gpio_set_cfgpin(pin, SUN6I_GPA_GMAC);
- sunxi_gpio_set_drv(pin, 3);
- }
- for (pin = SUNXI_GPA(25); pin <= SUNXI_GPA(27); pin++) {
- sunxi_gpio_set_cfgpin(pin, SUN6I_GPA_GMAC);
- sunxi_gpio_set_drv(pin, 3);
- }
-#elif defined CONFIG_GMII
- /* Configure sun6i GMII mode pin mux settings */
- for (pin = SUNXI_GPA(0); pin <= SUNXI_GPA(27); pin++) {
- sunxi_gpio_set_cfgpin(pin, SUN6I_GPA_GMAC);
- sunxi_gpio_set_drv(pin, 2);
- }
-#else
- /* Configure sun6i MII mode pin mux settings */
- for (pin = SUNXI_GPA(0); pin <= SUNXI_GPA(3); pin++)
- sunxi_gpio_set_cfgpin(pin, SUN6I_GPA_GMAC);
- for (pin = SUNXI_GPA(8); pin <= SUNXI_GPA(9); pin++)
- sunxi_gpio_set_cfgpin(pin, SUN6I_GPA_GMAC);
- for (pin = SUNXI_GPA(11); pin <= SUNXI_GPA(14); pin++)
- sunxi_gpio_set_cfgpin(pin, SUN6I_GPA_GMAC);
- for (pin = SUNXI_GPA(19); pin <= SUNXI_GPA(24); pin++)
- sunxi_gpio_set_cfgpin(pin, SUN6I_GPA_GMAC);
- for (pin = SUNXI_GPA(26); pin <= SUNXI_GPA(27); pin++)
- sunxi_gpio_set_cfgpin(pin, SUN6I_GPA_GMAC);
-#endif
}
diff --git a/boot/image.c b/boot/image.c
index 121df0c..5dcb55b 100644
--- a/boot/image.c
+++ b/boot/image.c
@@ -178,6 +178,7 @@ static const table_entry_t uimage_type[] = {
{ IH_TYPE_MTKIMAGE, "mtk_image", "MediaTek BootROM loadable Image" },
{ IH_TYPE_COPRO, "copro", "Coprocessor Image"},
{ IH_TYPE_SUNXI_EGON, "sunxi_egon", "Allwinner eGON Boot Image" },
+ { IH_TYPE_SUNXI_TOC0, "sunxi_toc0", "Allwinner TOC0 Boot Image" },
{ -1, "", "", },
};
diff --git a/common/spl/spl_mmc.c b/common/spl/spl_mmc.c
index 1c41d24..1bb785a 100644
--- a/common/spl/spl_mmc.c
+++ b/common/spl/spl_mmc.c
@@ -327,7 +327,7 @@ static int spl_mmc_do_fs_boot(struct spl_image_info *spl_image,
}
#endif
-u32 __weak spl_mmc_boot_mode(const u32 boot_device)
+u32 __weak spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device)
{
#if defined(CONFIG_SPL_FS_FAT) || defined(CONFIG_SPL_FS_EXT4)
return MMCSD_MODE_FS;
@@ -401,7 +401,7 @@ int spl_mmc_load(struct spl_image_info *spl_image,
}
}
- boot_mode = spl_mmc_boot_mode(bootdev->boot_device);
+ boot_mode = spl_mmc_boot_mode(mmc, bootdev->boot_device);
err = -EINVAL;
switch (boot_mode) {
case MMCSD_MODE_EMMCBOOT:
diff --git a/configs/bananapi_m64_defconfig b/configs/bananapi_m64_defconfig
index 292044d..5463b04 100644
--- a/configs/bananapi_m64_defconfig
+++ b/configs/bananapi_m64_defconfig
@@ -7,6 +7,7 @@ CONFIG_RESERVE_ALLWINNER_BOOT0_HEADER=y
CONFIG_MMC0_CD_PIN="PH13"
CONFIG_MMC_SUNXI_SLOT_EXTRA=2
# CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+CONFIG_SUPPORT_EMMC_BOOT=y
CONFIG_SUN8I_EMAC=y
CONFIG_USB_EHCI_HCD=y
CONFIG_USB_OHCI_HCD=y
diff --git a/configs/emlid_neutis_n5_devboard_defconfig b/configs/emlid_neutis_n5_devboard_defconfig
index e2d3b13..a3b43df 100644
--- a/configs/emlid_neutis_n5_devboard_defconfig
+++ b/configs/emlid_neutis_n5_devboard_defconfig
@@ -8,3 +8,4 @@ CONFIG_DRAM_ZQ=3881977
# CONFIG_DRAM_ODT_EN is not set
CONFIG_MMC_SUNXI_SLOT_EXTRA=2
# CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+CONFIG_SUPPORT_EMMC_BOOT=y
diff --git a/configs/pine64-lts_defconfig b/configs/pine64-lts_defconfig
index 45a9e77..7e7c2d7 100644
--- a/configs/pine64-lts_defconfig
+++ b/configs/pine64-lts_defconfig
@@ -10,6 +10,7 @@ CONFIG_MMC0_CD_PIN=""
CONFIG_MMC_SUNXI_SLOT_EXTRA=2
CONFIG_SPL_SPI_SUNXI=y
# CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+CONFIG_SUPPORT_EMMC_BOOT=y
CONFIG_SPI_FLASH_WINBOND=y
CONFIG_SUN8I_EMAC=y
CONFIG_SPI=y
diff --git a/configs/pine_h64_defconfig b/configs/pine_h64_defconfig
index 1e730dd..09a4275 100644
--- a/configs/pine_h64_defconfig
+++ b/configs/pine_h64_defconfig
@@ -11,6 +11,7 @@ CONFIG_USB3_VBUS_PIN="PL5"
CONFIG_SPL_SPI_SUNXI=y
# CONFIG_PSCI_RESET is not set
# CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+CONFIG_SUPPORT_EMMC_BOOT=y
CONFIG_SPI_FLASH_WINBOND=y
CONFIG_SUN8I_EMAC=y
CONFIG_PHY_SUN50I_USB3=y
diff --git a/configs/sopine_baseboard_defconfig b/configs/sopine_baseboard_defconfig
index 982f7b0..fbbef7a 100644
--- a/configs/sopine_baseboard_defconfig
+++ b/configs/sopine_baseboard_defconfig
@@ -11,6 +11,7 @@ CONFIG_MMC0_CD_PIN=""
CONFIG_MMC_SUNXI_SLOT_EXTRA=2
CONFIG_SPL_SPI_SUNXI=y
# CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
+CONFIG_SUPPORT_EMMC_BOOT=y
CONFIG_SPI_FLASH_WINBOND=y
CONFIG_SUN8I_EMAC=y
CONFIG_SPI=y
diff --git a/drivers/gpio/sunxi_gpio.c b/drivers/gpio/sunxi_gpio.c
index 6c3c108..1e85db1 100644
--- a/drivers/gpio/sunxi_gpio.c
+++ b/drivers/gpio/sunxi_gpio.c
@@ -16,15 +16,8 @@
#include <malloc.h>
#include <asm/io.h>
#include <asm/gpio.h>
-#include <dm/device-internal.h>
#include <dt-bindings/gpio/gpio.h>
-struct sunxi_gpio_plat {
- struct sunxi_gpio *regs;
- const char *bank_name; /* Name of bank, e.g. "B" */
- int gpio_count;
-};
-
#if !CONFIG_IS_ENABLED(DM_GPIO)
static int sunxi_gpio_output(u32 pin, u32 val)
{
@@ -211,28 +204,6 @@ static const struct dm_gpio_ops gpio_sunxi_ops = {
.set_flags = sunxi_gpio_set_flags,
};
-/**
- * Returns the name of a GPIO bank
- *
- * GPIO banks are named A, B, C, ...
- *
- * @bank: Bank number (0, 1..n-1)
- * Return: allocated string containing the name
- */
-static char *gpio_bank_name(int bank)
-{
- char *name;
-
- name = malloc(3);
- if (name) {
- name[0] = 'P';
- name[1] = 'A' + bank;
- name[2] = '\0';
- }
-
- return name;
-}
-
static int gpio_sunxi_probe(struct udevice *dev)
{
struct sunxi_gpio_plat *plat = dev_get_plat(dev);
@@ -240,114 +211,17 @@ static int gpio_sunxi_probe(struct udevice *dev)
/* Tell the uclass how many GPIOs we have */
if (plat) {
- uc_priv->gpio_count = plat->gpio_count;
+ uc_priv->gpio_count = SUNXI_GPIOS_PER_BANK;
uc_priv->bank_name = plat->bank_name;
}
return 0;
}
-struct sunxi_gpio_soc_data {
- int start;
- int no_banks;
-};
-
-/**
- * We have a top-level GPIO device with no actual GPIOs. It has a child
- * device for each Sunxi bank.
- */
-static int gpio_sunxi_bind(struct udevice *parent)
-{
- struct sunxi_gpio_soc_data *soc_data =
- (struct sunxi_gpio_soc_data *)dev_get_driver_data(parent);
- struct sunxi_gpio_plat *plat = dev_get_plat(parent);
- struct sunxi_gpio_reg *ctlr;
- int bank, ret;
-
- /* If this is a child device, there is nothing to do here */
- if (plat)
- return 0;
-
- ctlr = dev_read_addr_ptr(parent);
- for (bank = 0; bank < soc_data->no_banks; bank++) {
- struct sunxi_gpio_plat *plat;
- struct udevice *dev;
-
- plat = calloc(1, sizeof(*plat));
- if (!plat)
- return -ENOMEM;
- plat->regs = &ctlr->gpio_bank[bank];
- plat->bank_name = gpio_bank_name(soc_data->start + bank);
- plat->gpio_count = SUNXI_GPIOS_PER_BANK;
-
- ret = device_bind(parent, parent->driver, plat->bank_name, plat,
- dev_ofnode(parent), &dev);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-static const struct sunxi_gpio_soc_data soc_data_a_all = {
- .start = 0,
- .no_banks = SUNXI_GPIO_BANKS,
-};
-
-static const struct sunxi_gpio_soc_data soc_data_l_1 = {
- .start = 'L' - 'A',
- .no_banks = 1,
-};
-
-static const struct sunxi_gpio_soc_data soc_data_l_2 = {
- .start = 'L' - 'A',
- .no_banks = 2,
-};
-
-static const struct sunxi_gpio_soc_data soc_data_l_3 = {
- .start = 'L' - 'A',
- .no_banks = 3,
-};
-
-#define ID(_compat_, _soc_data_) \
- { .compatible = _compat_, .data = (ulong)&soc_data_##_soc_data_ }
-
-static const struct udevice_id sunxi_gpio_ids[] = {
- ID("allwinner,sun4i-a10-pinctrl", a_all),
- ID("allwinner,sun5i-a10s-pinctrl", a_all),
- ID("allwinner,sun5i-a13-pinctrl", a_all),
- ID("allwinner,sun50i-h5-pinctrl", a_all),
- ID("allwinner,sun6i-a31-pinctrl", a_all),
- ID("allwinner,sun6i-a31s-pinctrl", a_all),
- ID("allwinner,sun7i-a20-pinctrl", a_all),
- ID("allwinner,sun8i-a23-pinctrl", a_all),
- ID("allwinner,sun8i-a33-pinctrl", a_all),
- ID("allwinner,sun8i-a83t-pinctrl", a_all),
- ID("allwinner,sun8i-h3-pinctrl", a_all),
- ID("allwinner,sun8i-r40-pinctrl", a_all),
- ID("allwinner,sun8i-v3-pinctrl", a_all),
- ID("allwinner,sun8i-v3s-pinctrl", a_all),
- ID("allwinner,sun9i-a80-pinctrl", a_all),
- ID("allwinner,sun50i-a64-pinctrl", a_all),
- ID("allwinner,sun50i-h6-pinctrl", a_all),
- ID("allwinner,sun50i-h616-pinctrl", a_all),
- ID("allwinner,sun6i-a31-r-pinctrl", l_2),
- ID("allwinner,sun8i-a23-r-pinctrl", l_1),
- ID("allwinner,sun8i-a83t-r-pinctrl", l_1),
- ID("allwinner,sun8i-h3-r-pinctrl", l_1),
- ID("allwinner,sun9i-a80-r-pinctrl", l_3),
- ID("allwinner,sun50i-a64-r-pinctrl", l_1),
- ID("allwinner,sun50i-h6-r-pinctrl", l_2),
- ID("allwinner,sun50i-h616-r-pinctrl", l_1),
- { }
-};
-
U_BOOT_DRIVER(gpio_sunxi) = {
.name = "gpio_sunxi",
.id = UCLASS_GPIO,
- .ops = &gpio_sunxi_ops,
- .of_match = sunxi_gpio_ids,
- .bind = gpio_sunxi_bind,
.probe = gpio_sunxi_probe,
+ .ops = &gpio_sunxi_ops,
};
#endif /* DM_GPIO */
diff --git a/drivers/i2c/sun6i_p2wi.c b/drivers/i2c/sun6i_p2wi.c
index c9e1b3f..d221323 100644
--- a/drivers/i2c/sun6i_p2wi.c
+++ b/drivers/i2c/sun6i_p2wi.c
@@ -14,10 +14,12 @@
*/
#include <axp_pmic.h>
+#include <clk.h>
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <i2c.h>
+#include <reset.h>
#include <time.h>
#include <asm/io.h>
#include <asm/arch/cpu.h>
@@ -102,12 +104,6 @@ static int sun6i_p2wi_change_to_p2wi_mode(struct sunxi_p2wi_reg *base,
static void sun6i_p2wi_init(struct sunxi_p2wi_reg *base)
{
- /* Enable p2wi and PIO clk, and de-assert their resets */
- prcm_apb0_enable(PRCM_APB0_GATE_PIO | PRCM_APB0_GATE_P2WI);
-
- sunxi_gpio_set_cfgpin(SUNXI_GPL(0), SUN6I_GPL0_R_P2WI_SCK);
- sunxi_gpio_set_cfgpin(SUNXI_GPL(1), SUN6I_GPL1_R_P2WI_SDA);
-
/* Reset p2wi controller and set clock to CLKIN(12)/8 = 1.5 MHz */
writel(P2WI_CTRL_RESET, &base->ctrl);
sdelay(0x100);
@@ -142,6 +138,12 @@ void p2wi_init(void)
{
struct sunxi_p2wi_reg *base = (struct sunxi_p2wi_reg *)SUN6I_P2WI_BASE;
+ /* Enable p2wi and PIO clk, and de-assert their resets */
+ prcm_apb0_enable(PRCM_APB0_GATE_PIO | PRCM_APB0_GATE_P2WI);
+
+ sunxi_gpio_set_cfgpin(SUNXI_GPL(0), SUN6I_GPL0_R_P2WI_SCK);
+ sunxi_gpio_set_cfgpin(SUNXI_GPL(1), SUN6I_GPL1_R_P2WI_SDA);
+
sun6i_p2wi_init(base);
}
#endif
@@ -180,9 +182,19 @@ static int sun6i_p2wi_probe_chip(struct udevice *bus, uint chip_addr,
static int sun6i_p2wi_probe(struct udevice *bus)
{
struct sun6i_p2wi_priv *priv = dev_get_priv(bus);
+ struct reset_ctl *reset;
+ struct clk *clk;
priv->base = dev_read_addr_ptr(bus);
+ reset = devm_reset_control_get(bus, NULL);
+ if (!IS_ERR(reset))
+ reset_deassert(reset);
+
+ clk = devm_clk_get(bus, NULL);
+ if (!IS_ERR(clk))
+ clk_enable(clk);
+
sun6i_p2wi_init(priv->base);
return 0;
@@ -191,11 +203,12 @@ static int sun6i_p2wi_probe(struct udevice *bus)
static int sun6i_p2wi_child_pre_probe(struct udevice *child)
{
struct dm_i2c_chip *chip = dev_get_parent_plat(child);
+ struct udevice *bus = child->parent;
/* Ensure each transfer is for a single register. */
chip->flags |= DM_I2C_CHIP_RD_ADDRESS | DM_I2C_CHIP_WR_ADDRESS;
- return 0;
+ return sun6i_p2wi_probe_chip(bus, chip->chip_addr, 0);
}
static const struct dm_i2c_ops sun6i_p2wi_ops = {
diff --git a/drivers/i2c/sun8i_rsb.c b/drivers/i2c/sun8i_rsb.c
index 716b245..47fa05b 100644
--- a/drivers/i2c/sun8i_rsb.c
+++ b/drivers/i2c/sun8i_rsb.c
@@ -9,10 +9,12 @@
*/
#include <axp_pmic.h>
+#include <clk.h>
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <i2c.h>
+#include <reset.h>
#include <time.h>
#include <asm/arch/cpu.h>
#include <asm/arch/gpio.h>
@@ -95,27 +97,6 @@ static int sun8i_rsb_set_device_address(struct sunxi_rsb_reg *base,
return sun8i_rsb_do_trans(base);
}
-static void sun8i_rsb_cfg_io(void)
-{
-#ifdef CONFIG_MACH_SUN8I
- sunxi_gpio_set_cfgpin(SUNXI_GPL(0), SUN8I_GPL_R_RSB);
- sunxi_gpio_set_cfgpin(SUNXI_GPL(1), SUN8I_GPL_R_RSB);
- sunxi_gpio_set_pull(SUNXI_GPL(0), 1);
- sunxi_gpio_set_pull(SUNXI_GPL(1), 1);
- sunxi_gpio_set_drv(SUNXI_GPL(0), 2);
- sunxi_gpio_set_drv(SUNXI_GPL(1), 2);
-#elif defined CONFIG_MACH_SUN9I
- sunxi_gpio_set_cfgpin(SUNXI_GPN(0), SUN9I_GPN_R_RSB);
- sunxi_gpio_set_cfgpin(SUNXI_GPN(1), SUN9I_GPN_R_RSB);
- sunxi_gpio_set_pull(SUNXI_GPN(0), 1);
- sunxi_gpio_set_pull(SUNXI_GPN(1), 1);
- sunxi_gpio_set_drv(SUNXI_GPN(0), 2);
- sunxi_gpio_set_drv(SUNXI_GPN(1), 2);
-#else
-#error unsupported MACH_SUNXI
-#endif
-}
-
static void sun8i_rsb_set_clk(struct sunxi_rsb_reg *base)
{
u32 div = 0;
@@ -147,12 +128,6 @@ static int sun8i_rsb_set_device_mode(struct sunxi_rsb_reg *base)
static int sun8i_rsb_init(struct sunxi_rsb_reg *base)
{
- /* Enable RSB and PIO clk, and de-assert their resets */
- prcm_apb0_enable(PRCM_APB0_GATE_PIO | PRCM_APB0_GATE_RSB);
-
- /* Setup external pins */
- sun8i_rsb_cfg_io();
-
writel(RSB_CTRL_SOFT_RST, &base->ctrl);
sun8i_rsb_set_clk(base);
@@ -185,6 +160,25 @@ int rsb_init(void)
{
struct sunxi_rsb_reg *base = (struct sunxi_rsb_reg *)SUNXI_RSB_BASE;
+ /* Enable RSB and PIO clk, and de-assert their resets */
+ prcm_apb0_enable(PRCM_APB0_GATE_PIO | PRCM_APB0_GATE_RSB);
+
+ if (IS_ENABLED(CONFIG_MACH_SUN9I)) {
+ sunxi_gpio_set_cfgpin(SUNXI_GPN(0), SUN9I_GPN_R_RSB);
+ sunxi_gpio_set_cfgpin(SUNXI_GPN(1), SUN9I_GPN_R_RSB);
+ sunxi_gpio_set_pull(SUNXI_GPN(0), 1);
+ sunxi_gpio_set_pull(SUNXI_GPN(1), 1);
+ sunxi_gpio_set_drv(SUNXI_GPN(0), 2);
+ sunxi_gpio_set_drv(SUNXI_GPN(1), 2);
+ } else {
+ sunxi_gpio_set_cfgpin(SUNXI_GPL(0), SUN8I_GPL_R_RSB);
+ sunxi_gpio_set_cfgpin(SUNXI_GPL(1), SUN8I_GPL_R_RSB);
+ sunxi_gpio_set_pull(SUNXI_GPL(0), 1);
+ sunxi_gpio_set_pull(SUNXI_GPL(1), 1);
+ sunxi_gpio_set_drv(SUNXI_GPL(0), 2);
+ sunxi_gpio_set_drv(SUNXI_GPL(1), 2);
+ }
+
return sun8i_rsb_init(base);
}
#endif
@@ -243,20 +237,31 @@ static int sun8i_rsb_probe_chip(struct udevice *bus, uint chip_addr,
static int sun8i_rsb_probe(struct udevice *bus)
{
struct sun8i_rsb_priv *priv = dev_get_priv(bus);
+ struct reset_ctl *reset;
+ struct clk *clk;
priv->base = dev_read_addr_ptr(bus);
+ reset = devm_reset_control_get(bus, NULL);
+ if (!IS_ERR(reset))
+ reset_deassert(reset);
+
+ clk = devm_clk_get(bus, NULL);
+ if (!IS_ERR(clk))
+ clk_enable(clk);
+
return sun8i_rsb_init(priv->base);
}
static int sun8i_rsb_child_pre_probe(struct udevice *child)
{
struct dm_i2c_chip *chip = dev_get_parent_plat(child);
+ struct udevice *bus = child->parent;
/* Ensure each transfer is for a single register. */
chip->flags |= DM_I2C_CHIP_RD_ADDRESS | DM_I2C_CHIP_WR_ADDRESS;
- return 0;
+ return sun8i_rsb_probe_chip(bus, chip->chip_addr, 0);
}
static const struct dm_i2c_ops sun8i_rsb_ops = {
diff --git a/drivers/net/sun8i_emac.c b/drivers/net/sun8i_emac.c
index 2e24d12..b23faa2 100644
--- a/drivers/net/sun8i_emac.c
+++ b/drivers/net/sun8i_emac.c
@@ -29,7 +29,6 @@
#include <miiphy.h>
#include <net.h>
#include <reset.h>
-#include <dt-bindings/pinctrl/sun4i-a10.h>
#include <wait_bit.h>
#define MDIO_CMD_MII_BUSY BIT(0)
@@ -81,13 +80,6 @@
#define AHB_GATE_OFFSET_EPHY 0
-/* IO mux settings */
-#define SUN8I_IOMUX_H3 2
-#define SUN8I_IOMUX_R40 5
-#define SUN8I_IOMUX_H6 5
-#define SUN8I_IOMUX_H616 2
-#define SUN8I_IOMUX 4
-
/* H3/A64 EMAC Register's offset */
#define EMAC_CTL0 0x00
#define EMAC_CTL0_FULL_DUPLEX BIT(0)
@@ -519,85 +511,6 @@ static int sun8i_emac_eth_start(struct udevice *dev)
return 0;
}
-static int parse_phy_pins(struct udevice *dev)
-{
- int offset;
- const char *pin_name;
- int drive, pull = SUN4I_PINCTRL_NO_PULL, i;
- u32 iomux;
-
- offset = fdtdec_lookup_phandle(gd->fdt_blob, dev_of_offset(dev),
- "pinctrl-0");
- if (offset < 0) {
- printf("WARNING: emac: cannot find pinctrl-0 node\n");
- return offset;
- }
-
- drive = fdt_getprop_u32_default_node(gd->fdt_blob, offset, 0,
- "drive-strength", ~0);
- if (drive != ~0) {
- if (drive <= 10)
- drive = SUN4I_PINCTRL_10_MA;
- else if (drive <= 20)
- drive = SUN4I_PINCTRL_20_MA;
- else if (drive <= 30)
- drive = SUN4I_PINCTRL_30_MA;
- else
- drive = SUN4I_PINCTRL_40_MA;
- }
-
- if (fdt_get_property(gd->fdt_blob, offset, "bias-pull-up", NULL))
- pull = SUN4I_PINCTRL_PULL_UP;
- else if (fdt_get_property(gd->fdt_blob, offset, "bias-pull-down", NULL))
- pull = SUN4I_PINCTRL_PULL_DOWN;
-
- /*
- * The GPIO pinmux value is an integration choice, so depends on the
- * SoC, not the EMAC variant.
- */
- if (IS_ENABLED(CONFIG_MACH_SUNXI_H3_H5))
- iomux = SUN8I_IOMUX_H3;
- else if (IS_ENABLED(CONFIG_MACH_SUN8I_R40))
- iomux = SUN8I_IOMUX_R40;
- else if (IS_ENABLED(CONFIG_MACH_SUN50I_H6))
- iomux = SUN8I_IOMUX_H6;
- else if (IS_ENABLED(CONFIG_MACH_SUN50I_H616))
- iomux = SUN8I_IOMUX_H616;
- else if (IS_ENABLED(CONFIG_MACH_SUN8I_A83T))
- iomux = SUN8I_IOMUX;
- else if (IS_ENABLED(CONFIG_MACH_SUN50I))
- iomux = SUN8I_IOMUX;
- else
- BUILD_BUG_ON_MSG(1, "missing pinmux value for Ethernet pins");
-
- for (i = 0; ; i++) {
- int pin;
-
- pin_name = fdt_stringlist_get(gd->fdt_blob, offset,
- "pins", i, NULL);
- if (!pin_name)
- break;
-
- pin = sunxi_name_to_gpio(pin_name);
- if (pin < 0)
- continue;
-
- sunxi_gpio_set_cfgpin(pin, iomux);
-
- if (drive != ~0)
- sunxi_gpio_set_drv(pin, drive);
- if (pull != ~0)
- sunxi_gpio_set_pull(pin, pull);
- }
-
- if (!i) {
- printf("WARNING: emac: cannot find pins property\n");
- return -2;
- }
-
- return 0;
-}
-
static int sun8i_emac_eth_recv(struct udevice *dev, int flags, uchar **packetp)
{
struct emac_eth_dev *priv = dev_get_priv(dev);
@@ -965,9 +878,6 @@ static int sun8i_emac_eth_of_to_plat(struct udevice *dev)
priv->interface = pdata->phy_interface;
- if (!priv->use_internal_phy)
- parse_phy_pins(dev);
-
sun8i_pdata->tx_delay_ps = fdtdec_get_int(gd->fdt_blob, node,
"allwinner,tx-delay-ps", 0);
if (sun8i_pdata->tx_delay_ps < 0 || sun8i_pdata->tx_delay_ps > 700)
diff --git a/drivers/net/sunxi_emac.c b/drivers/net/sunxi_emac.c
index 17ad88e..d15b0ad 100644
--- a/drivers/net/sunxi_emac.c
+++ b/drivers/net/sunxi_emac.c
@@ -17,7 +17,6 @@
#include <net.h>
#include <asm/io.h>
#include <asm/arch/clock.h>
-#include <asm/arch/gpio.h>
/* EMAC register */
struct emac_regs {
@@ -511,15 +510,11 @@ static int sunxi_emac_board_setup(struct udevice *dev,
struct sunxi_sramc_regs *sram =
(struct sunxi_sramc_regs *)SUNXI_SRAMC_BASE;
struct emac_regs *regs = priv->regs;
- int pin, ret;
+ int ret;
/* Map SRAM to EMAC */
setbits_le32(&sram->ctrl1, 0x5 << 2);
- /* Configure pin mux settings for MII Ethernet */
- for (pin = SUNXI_GPA(0); pin <= SUNXI_GPA(17); pin++)
- sunxi_gpio_set_cfgpin(pin, SUNXI_GPA_EMAC);
-
/* Set up clock gating */
ret = clk_enable(&priv->clk);
if (ret) {
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index d7477d7..1303319 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -342,6 +342,7 @@ source "drivers/pinctrl/nexell/Kconfig"
source "drivers/pinctrl/nxp/Kconfig"
source "drivers/pinctrl/renesas/Kconfig"
source "drivers/pinctrl/rockchip/Kconfig"
+source "drivers/pinctrl/sunxi/Kconfig"
source "drivers/pinctrl/uniphier/Kconfig"
endmenu
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 030c38f..9b49782 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -14,7 +14,7 @@ obj-$(CONFIG_PINCTRL_INTEL) += intel/
obj-$(CONFIG_ARCH_MTMIPS) += mtmips/
obj-$(CONFIG_ARCH_RMOBILE) += renesas/
obj-$(CONFIG_PINCTRL_SANDBOX) += pinctrl-sandbox.o
-
+obj-$(CONFIG_PINCTRL_SUNXI) += sunxi/
obj-$(CONFIG_PINCTRL_UNIPHIER) += uniphier/
obj-$(CONFIG_PINCTRL_PIC32) += pinctrl_pic32.o
obj-$(CONFIG_PINCTRL_EXYNOS) += exynos/
diff --git a/drivers/pinctrl/sunxi/Kconfig b/drivers/pinctrl/sunxi/Kconfig
new file mode 100644
index 0000000..77da908
--- /dev/null
+++ b/drivers/pinctrl/sunxi/Kconfig
@@ -0,0 +1,127 @@
+# SPDX-License-Identifier: GPL-2.0
+
+if ARCH_SUNXI
+
+config PINCTRL_SUNXI
+ select PINCTRL_FULL
+ select PINCTRL_GENERIC
+ select PINCONF
+ select PINMUX
+ bool
+
+config PINCTRL_SUNIV_F1C100S
+ bool "Support for the Allwinner F1C100s PIO"
+ default MACH_SUNIV
+ select PINCTRL_SUNXI
+
+config PINCTRL_SUN4I_A10
+ bool "Support for the Allwinner A10 PIO"
+ default MACH_SUN4I
+ select PINCTRL_SUNXI
+
+config PINCTRL_SUN5I_A13
+ bool "Support for the Allwinner A10s/A13 PIO"
+ default MACH_SUN5I
+ select PINCTRL_SUNXI
+
+config PINCTRL_SUN6I_A31
+ bool "Support for the Allwinner A31 PIO"
+ default MACH_SUN6I
+ select PINCTRL_SUNXI
+
+config PINCTRL_SUN6I_A31_R
+ bool "Support for the Allwinner A31 R-PIO"
+ default MACH_SUN6I
+ select PINCTRL_SUNXI
+
+config PINCTRL_SUN7I_A20
+ bool "Support for the Allwinner A20/R40 PIO"
+ default MACH_SUN7I || MACH_SUN8I_R40
+ select PINCTRL_SUNXI
+
+config PINCTRL_SUN8I_A23
+ bool "Support for the Allwinner A23 PIO"
+ default MACH_SUN8I_A23
+ select PINCTRL_SUNXI
+
+config PINCTRL_SUN8I_A23_R
+ bool "Support for the Allwinner A23/A33 R-PIO"
+ default MACH_SUN8I_A23 || MACH_SUN8I_A33
+ select PINCTRL_SUNXI
+
+config PINCTRL_SUN8I_A33
+ bool "Support for the Allwinner A33 PIO"
+ default MACH_SUN8I_A33
+ select PINCTRL_SUNXI
+
+config PINCTRL_SUN8I_A83T
+ bool "Support for the Allwinner A83T PIO"
+ default MACH_SUN8I_A83T
+ select PINCTRL_SUNXI
+
+config PINCTRL_SUN8I_A83T_R
+ bool "Support for the Allwinner A83T R-PIO"
+ default MACH_SUN8I_A83T
+ select PINCTRL_SUNXI
+
+config PINCTRL_SUN8I_H3
+ bool "Support for the Allwinner H3 PIO"
+ default MACH_SUN8I_H3
+ select PINCTRL_SUNXI
+
+config PINCTRL_SUN8I_H3_R
+ bool "Support for the Allwinner H3/H5 R-PIO"
+ default MACH_SUN8I_H3 || MACH_SUN50I_H5
+ select PINCTRL_SUNXI
+
+config PINCTRL_SUN8I_V3S
+ bool "Support for the Allwinner V3s PIO"
+ default MACH_SUN8I_V3S
+ select PINCTRL_SUNXI
+
+config PINCTRL_SUN9I_A80
+ bool "Support for the Allwinner A80 PIO"
+ default MACH_SUN9I
+ select PINCTRL_SUNXI
+
+config PINCTRL_SUN9I_A80_R
+ bool "Support for the Allwinner A80 R-PIO"
+ default MACH_SUN9I
+ select PINCTRL_SUNXI
+
+config PINCTRL_SUN50I_A64
+ bool "Support for the Allwinner A64 PIO"
+ default MACH_SUN50I
+ select PINCTRL_SUNXI
+
+config PINCTRL_SUN50I_A64_R
+ bool "Support for the Allwinner A64 R-PIO"
+ default MACH_SUN50I
+ select PINCTRL_SUNXI
+
+config PINCTRL_SUN50I_H5
+ bool "Support for the Allwinner H5 PIO"
+ default MACH_SUN50I_H5
+ select PINCTRL_SUNXI
+
+config PINCTRL_SUN50I_H6
+ bool "Support for the Allwinner H6 PIO"
+ default MACH_SUN50I_H6
+ select PINCTRL_SUNXI
+
+config PINCTRL_SUN50I_H6_R
+ bool "Support for the Allwinner H6 R-PIO"
+ default MACH_SUN50I_H6
+ select PINCTRL_SUNXI
+
+config PINCTRL_SUN50I_H616
+ bool "Support for the Allwinner H616 PIO"
+ default MACH_SUN50I_H616
+ select PINCTRL_SUNXI
+
+config PINCTRL_SUN50I_H616_R
+ bool "Support for the Allwinner H616 R-PIO"
+ default MACH_SUN50I_H616
+ select PINCTRL_SUNXI
+
+endif
diff --git a/drivers/pinctrl/sunxi/Makefile b/drivers/pinctrl/sunxi/Makefile
new file mode 100644
index 0000000..6a8c01f
--- /dev/null
+++ b/drivers/pinctrl/sunxi/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-y += pinctrl-sunxi.o
diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
new file mode 100644
index 0000000..9ce2bc1
--- /dev/null
+++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
@@ -0,0 +1,897 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <clk.h>
+#include <dm.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <dm/pinctrl.h>
+#include <errno.h>
+#include <malloc.h>
+
+#include <asm/gpio.h>
+
+extern U_BOOT_DRIVER(gpio_sunxi);
+
+/*
+ * This structure implements a simplified view of the possible pinmux settings:
+ * Each mux value is assumed to be the same for a given function, across the
+ * pins in each group (almost universally true, with same rare exceptions not
+ * relevant to U-Boot), but also across different ports (not true in many
+ * cases). We ignore the first problem, and work around the latter by just
+ * supporting one particular port for a each function. This works fine for all
+ * board configurations so far. If this would need to be revisited, we could
+ * add a "u8 port;" below and match that, with 0 encoding the "don't care" case.
+ */
+struct sunxi_pinctrl_function {
+ const char name[sizeof("gpio_out")];
+ u8 mux;
+};
+
+struct sunxi_pinctrl_desc {
+ const struct sunxi_pinctrl_function *functions;
+ u8 num_functions;
+ u8 first_bank;
+ u8 num_banks;
+};
+
+struct sunxi_pinctrl_plat {
+ struct sunxi_gpio __iomem *base;
+};
+
+static int sunxi_pinctrl_get_pins_count(struct udevice *dev)
+{
+ const struct sunxi_pinctrl_desc *desc = dev_get_priv(dev);
+
+ return desc->num_banks * SUNXI_GPIOS_PER_BANK;
+}
+
+static const char *sunxi_pinctrl_get_pin_name(struct udevice *dev,
+ uint pin_selector)
+{
+ const struct sunxi_pinctrl_desc *desc = dev_get_priv(dev);
+ static char pin_name[sizeof("PN31")];
+
+ snprintf(pin_name, sizeof(pin_name), "P%c%d",
+ pin_selector / SUNXI_GPIOS_PER_BANK + desc->first_bank + 'A',
+ pin_selector % SUNXI_GPIOS_PER_BANK);
+
+ return pin_name;
+}
+
+static int sunxi_pinctrl_get_functions_count(struct udevice *dev)
+{
+ const struct sunxi_pinctrl_desc *desc = dev_get_priv(dev);
+
+ return desc->num_functions;
+}
+
+static const char *sunxi_pinctrl_get_function_name(struct udevice *dev,
+ uint func_selector)
+{
+ const struct sunxi_pinctrl_desc *desc = dev_get_priv(dev);
+
+ return desc->functions[func_selector].name;
+}
+
+static int sunxi_pinctrl_pinmux_set(struct udevice *dev, uint pin_selector,
+ uint func_selector)
+{
+ const struct sunxi_pinctrl_desc *desc = dev_get_priv(dev);
+ struct sunxi_pinctrl_plat *plat = dev_get_plat(dev);
+ int bank = pin_selector / SUNXI_GPIOS_PER_BANK;
+ int pin = pin_selector % SUNXI_GPIOS_PER_BANK;
+
+ debug("set mux: %-4s => %s (%d)\n",
+ sunxi_pinctrl_get_pin_name(dev, pin_selector),
+ sunxi_pinctrl_get_function_name(dev, func_selector),
+ desc->functions[func_selector].mux);
+
+ sunxi_gpio_set_cfgbank(plat->base + bank, pin,
+ desc->functions[func_selector].mux);
+
+ return 0;
+}
+
+static const struct pinconf_param sunxi_pinctrl_pinconf_params[] = {
+ { "bias-disable", PIN_CONFIG_BIAS_DISABLE, 0 },
+ { "bias-pull-down", PIN_CONFIG_BIAS_PULL_DOWN, 2 },
+ { "bias-pull-up", PIN_CONFIG_BIAS_PULL_UP, 1 },
+ { "drive-strength", PIN_CONFIG_DRIVE_STRENGTH, 10 },
+};
+
+static int sunxi_pinctrl_pinconf_set_pull(struct sunxi_pinctrl_plat *plat,
+ uint bank, uint pin, uint bias)
+{
+ struct sunxi_gpio *regs = &plat->base[bank];
+
+ sunxi_gpio_set_pull_bank(regs, pin, bias);
+
+ return 0;
+}
+
+static int sunxi_pinctrl_pinconf_set_drive(struct sunxi_pinctrl_plat *plat,
+ uint bank, uint pin, uint drive)
+{
+ struct sunxi_gpio *regs = &plat->base[bank];
+
+ if (drive < 10 || drive > 40)
+ return -EINVAL;
+
+ /* Convert mA to the register value, rounding down. */
+ sunxi_gpio_set_drv_bank(regs, pin, drive / 10 - 1);
+
+ return 0;
+}
+
+static int sunxi_pinctrl_pinconf_set(struct udevice *dev, uint pin_selector,
+ uint param, uint val)
+{
+ struct sunxi_pinctrl_plat *plat = dev_get_plat(dev);
+ int bank = pin_selector / SUNXI_GPIOS_PER_BANK;
+ int pin = pin_selector % SUNXI_GPIOS_PER_BANK;
+
+ switch (param) {
+ case PIN_CONFIG_BIAS_DISABLE:
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ case PIN_CONFIG_BIAS_PULL_UP:
+ return sunxi_pinctrl_pinconf_set_pull(plat, bank, pin, val);
+ case PIN_CONFIG_DRIVE_STRENGTH:
+ return sunxi_pinctrl_pinconf_set_drive(plat, bank, pin, val);
+ }
+
+ return -EINVAL;
+}
+
+static int sunxi_pinctrl_get_pin_muxing(struct udevice *dev, uint pin_selector,
+ char *buf, int size)
+{
+ struct sunxi_pinctrl_plat *plat = dev_get_plat(dev);
+ int bank = pin_selector / SUNXI_GPIOS_PER_BANK;
+ int pin = pin_selector % SUNXI_GPIOS_PER_BANK;
+ int mux = sunxi_gpio_get_cfgbank(plat->base + bank, pin);
+
+ switch (mux) {
+ case SUNXI_GPIO_INPUT:
+ strlcpy(buf, "gpio input", size);
+ break;
+ case SUNXI_GPIO_OUTPUT:
+ strlcpy(buf, "gpio output", size);
+ break;
+ case SUNXI_GPIO_DISABLE:
+ strlcpy(buf, "disabled", size);
+ break;
+ default:
+ snprintf(buf, size, "function %d", mux);
+ break;
+ }
+
+ return 0;
+}
+
+static const struct pinctrl_ops sunxi_pinctrl_ops = {
+ .get_pins_count = sunxi_pinctrl_get_pins_count,
+ .get_pin_name = sunxi_pinctrl_get_pin_name,
+ .get_functions_count = sunxi_pinctrl_get_functions_count,
+ .get_function_name = sunxi_pinctrl_get_function_name,
+ .pinmux_set = sunxi_pinctrl_pinmux_set,
+ .pinconf_num_params = ARRAY_SIZE(sunxi_pinctrl_pinconf_params),
+ .pinconf_params = sunxi_pinctrl_pinconf_params,
+ .pinconf_set = sunxi_pinctrl_pinconf_set,
+ .set_state = pinctrl_generic_set_state,
+ .get_pin_muxing = sunxi_pinctrl_get_pin_muxing,
+};
+
+static int sunxi_pinctrl_bind(struct udevice *dev)
+{
+ struct sunxi_pinctrl_plat *plat = dev_get_plat(dev);
+ struct sunxi_pinctrl_desc *desc;
+ struct sunxi_gpio_plat *gpio_plat;
+ struct udevice *gpio_dev;
+ int i, ret;
+
+ desc = (void *)dev_get_driver_data(dev);
+ if (!desc)
+ return -EINVAL;
+ dev_set_priv(dev, desc);
+
+ plat->base = dev_read_addr_ptr(dev);
+
+ ret = device_bind_driver_to_node(dev, "gpio_sunxi", dev->name,
+ dev_ofnode(dev), &gpio_dev);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < desc->num_banks; ++i) {
+ gpio_plat = malloc(sizeof(*gpio_plat));
+ if (!gpio_plat)
+ return -ENOMEM;
+
+ gpio_plat->regs = plat->base + i;
+ gpio_plat->bank_name[0] = 'P';
+ gpio_plat->bank_name[1] = 'A' + desc->first_bank + i;
+ gpio_plat->bank_name[2] = '\0';
+
+ ret = device_bind(gpio_dev, DM_DRIVER_REF(gpio_sunxi),
+ gpio_plat->bank_name, gpio_plat,
+ ofnode_null(), NULL);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int sunxi_pinctrl_probe(struct udevice *dev)
+{
+ struct clk *apb_clk;
+
+ apb_clk = devm_clk_get(dev, "apb");
+ if (!IS_ERR(apb_clk))
+ clk_enable(apb_clk);
+
+ return 0;
+}
+
+static const struct sunxi_pinctrl_function suniv_f1c100s_pinctrl_functions[] = {
+ { "gpio_in", 0 },
+ { "gpio_out", 1 },
+ { "i2c0", 3 }, /* PE11-PE12 */
+ { "i2c1", 3 }, /* PD5-PD6 */
+ { "mmc0", 2 }, /* PF0-PF5 */
+ { "mmc1", 3 }, /* PC0-PC2 */
+ { "spi0", 2 }, /* PC0-PC3 */
+#if IS_ENABLED(CONFIG_UART0_PORT_F)
+ { "uart0", 3 }, /* PF2-PF4 */
+#else
+ { "uart0", 5 }, /* PE0-PE1 */
+#endif
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused suniv_f1c100s_pinctrl_desc = {
+ .functions = suniv_f1c100s_pinctrl_functions,
+ .num_functions = ARRAY_SIZE(suniv_f1c100s_pinctrl_functions),
+ .first_bank = SUNXI_GPIO_A,
+ .num_banks = 6,
+};
+
+static const struct sunxi_pinctrl_function sun4i_a10_pinctrl_functions[] = {
+ { "emac", 2 }, /* PA0-PA17 */
+ { "gpio_in", 0 },
+ { "gpio_out", 1 },
+ { "i2c0", 2 }, /* PB0-PB1 */
+ { "i2c1", 2 }, /* PB18-PB19 */
+ { "mmc0", 2 }, /* PF0-PF5 */
+#if IS_ENABLED(CONFIG_MMC1_PINS_PH)
+ { "mmc1", 5 }, /* PH22-PH27 */
+#else
+ { "mmc1", 4 }, /* PG0-PG5 */
+#endif
+ { "mmc2", 3 }, /* PC6-PC15 */
+ { "mmc3", 2 }, /* PI4-PI9 */
+ { "spi0", 3 }, /* PC0-PC2, PC23 */
+#if IS_ENABLED(CONFIG_UART0_PORT_F)
+ { "uart0", 4 }, /* PF2-PF4 */
+#else
+ { "uart0", 2 }, /* PB22-PB23 */
+#endif
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun4i_a10_pinctrl_desc = {
+ .functions = sun4i_a10_pinctrl_functions,
+ .num_functions = ARRAY_SIZE(sun4i_a10_pinctrl_functions),
+ .first_bank = SUNXI_GPIO_A,
+ .num_banks = 9,
+};
+
+static const struct sunxi_pinctrl_function sun5i_a13_pinctrl_functions[] = {
+ { "emac", 2 }, /* PA0-PA17 */
+ { "gpio_in", 0 },
+ { "gpio_out", 1 },
+ { "i2c0", 2 }, /* PB0-PB1 */
+ { "i2c1", 2 }, /* PB15-PB16 */
+ { "mmc0", 2 }, /* PF0-PF5 */
+ { "mmc1", 2 }, /* PG3-PG8 */
+ { "mmc2", 3 }, /* PC6-PC15 */
+ { "spi0", 3 }, /* PC0-PC3 */
+#if IS_ENABLED(CONFIG_UART0_PORT_F)
+ { "uart0", 4 }, /* PF2-PF4 */
+#else
+ { "uart0", 2 }, /* PB19-PB20 */
+#endif
+ { "uart1", 4 }, /* PG3-PG4 */
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun5i_a13_pinctrl_desc = {
+ .functions = sun5i_a13_pinctrl_functions,
+ .num_functions = ARRAY_SIZE(sun5i_a13_pinctrl_functions),
+ .first_bank = SUNXI_GPIO_A,
+ .num_banks = 7,
+};
+
+static const struct sunxi_pinctrl_function sun6i_a31_pinctrl_functions[] = {
+ { "gmac", 2 }, /* PA0-PA27 */
+ { "gpio_in", 0 },
+ { "gpio_out", 1 },
+ { "i2c0", 2 }, /* PH14-PH15 */
+ { "i2c1", 2 }, /* PH16-PH17 */
+ { "mmc0", 2 }, /* PF0-PF5 */
+ { "mmc1", 2 }, /* PG0-PG5 */
+ { "mmc2", 3 }, /* PC6-PC15, PC24 */
+ { "mmc3", 4 }, /* PC6-PC15, PC24 */
+ { "spi0", 3 }, /* PC0-PC2, PC27 */
+#if IS_ENABLED(CONFIG_UART0_PORT_F)
+ { "uart0", 3 }, /* PF2-PF4 */
+#else
+ { "uart0", 2 }, /* PH20-PH21 */
+#endif
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun6i_a31_pinctrl_desc = {
+ .functions = sun6i_a31_pinctrl_functions,
+ .num_functions = ARRAY_SIZE(sun6i_a31_pinctrl_functions),
+ .first_bank = SUNXI_GPIO_A,
+ .num_banks = 8,
+};
+
+static const struct sunxi_pinctrl_function sun6i_a31_r_pinctrl_functions[] = {
+ { "gpio_in", 0 },
+ { "gpio_out", 1 },
+ { "s_i2c", 2 }, /* PL0-PL1 */
+ { "s_uart", 2 }, /* PL2-PL3 */
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun6i_a31_r_pinctrl_desc = {
+ .functions = sun6i_a31_r_pinctrl_functions,
+ .num_functions = ARRAY_SIZE(sun6i_a31_r_pinctrl_functions),
+ .first_bank = SUNXI_GPIO_L,
+ .num_banks = 2,
+};
+
+static const struct sunxi_pinctrl_function sun7i_a20_pinctrl_functions[] = {
+ { "emac", 2 }, /* PA0-PA17 */
+ { "gmac", 5 }, /* PA0-PA17 */
+ { "gpio_in", 0 },
+ { "gpio_out", 1 },
+ { "i2c0", 2 }, /* PB0-PB1 */
+ { "i2c1", 2 }, /* PB18-PB19 */
+ { "mmc0", 2 }, /* PF0-PF5 */
+#if IS_ENABLED(CONFIG_MMC1_PINS_PH)
+ { "mmc1", 5 }, /* PH22-PH27 */
+#else
+ { "mmc1", 4 }, /* PG0-PG5 */
+#endif
+ { "mmc2", 3 }, /* PC5-PC15, PC24 */
+ { "spi0", 3 }, /* PC0-PC2, PC23 */
+#if IS_ENABLED(CONFIG_UART0_PORT_F)
+ { "uart0", 4 }, /* PF2-PF4 */
+#else
+ { "uart0", 2 }, /* PB22-PB23 */
+#endif
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun7i_a20_pinctrl_desc = {
+ .functions = sun7i_a20_pinctrl_functions,
+ .num_functions = ARRAY_SIZE(sun7i_a20_pinctrl_functions),
+ .first_bank = SUNXI_GPIO_A,
+ .num_banks = 9,
+};
+
+static const struct sunxi_pinctrl_function sun8i_a23_pinctrl_functions[] = {
+ { "gpio_in", 0 },
+ { "gpio_out", 1 },
+ { "i2c0", 2 }, /* PH2-PH3 */
+ { "i2c1", 2 }, /* PH4-PH5 */
+ { "mmc0", 2 }, /* PF0-PF5 */
+ { "mmc1", 2 }, /* PG0-PG5 */
+ { "mmc2", 3 }, /* PC5-PC16 */
+ { "spi0", 3 }, /* PC0-PC3 */
+#if IS_ENABLED(CONFIG_UART0_PORT_F)
+ { "uart0", 3 }, /* PF2-PF4 */
+#endif
+ { "uart1", 2 }, /* PG6-PG7 */
+ { "uart2", 2 }, /* PB0-PB1 */
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun8i_a23_pinctrl_desc = {
+ .functions = sun8i_a23_pinctrl_functions,
+ .num_functions = ARRAY_SIZE(sun8i_a23_pinctrl_functions),
+ .first_bank = SUNXI_GPIO_A,
+ .num_banks = 8,
+};
+
+static const struct sunxi_pinctrl_function sun8i_a23_r_pinctrl_functions[] = {
+ { "gpio_in", 0 },
+ { "gpio_out", 1 },
+ { "s_i2c", 3 }, /* PL0-PL1 */
+ { "s_uart", 2 }, /* PL2-PL3 */
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun8i_a23_r_pinctrl_desc = {
+ .functions = sun8i_a23_r_pinctrl_functions,
+ .num_functions = ARRAY_SIZE(sun8i_a23_r_pinctrl_functions),
+ .first_bank = SUNXI_GPIO_L,
+ .num_banks = 1,
+};
+
+static const struct sunxi_pinctrl_function sun8i_a33_pinctrl_functions[] = {
+ { "gpio_in", 0 },
+ { "gpio_out", 1 },
+ { "i2c0", 2 }, /* PH2-PH3 */
+ { "i2c1", 2 }, /* PH4-PH5 */
+ { "mmc0", 2 }, /* PF0-PF5 */
+ { "mmc1", 2 }, /* PG0-PG5 */
+ { "mmc2", 3 }, /* PC5-PC16 */
+ { "spi0", 3 }, /* PC0-PC3 */
+#if IS_ENABLED(CONFIG_UART0_PORT_F)
+ { "uart0", 3 }, /* PF2-PF4 */
+#else
+ { "uart0", 3 }, /* PB0-PB1 */
+#endif
+ { "uart1", 2 }, /* PG6-PG7 */
+ { "uart2", 2 }, /* PB0-PB1 */
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun8i_a33_pinctrl_desc = {
+ .functions = sun8i_a33_pinctrl_functions,
+ .num_functions = ARRAY_SIZE(sun8i_a33_pinctrl_functions),
+ .first_bank = SUNXI_GPIO_A,
+ .num_banks = 8,
+};
+
+static const struct sunxi_pinctrl_function sun8i_a83t_pinctrl_functions[] = {
+ { "gmac", 4 }, /* PD2-PD23 */
+ { "gpio_in", 0 },
+ { "gpio_out", 1 },
+ { "i2c0", 2 }, /* PH0-PH1 */
+ { "i2c1", 2 }, /* PH2-PH3 */
+ { "mmc0", 2 }, /* PF0-PF5 */
+ { "mmc1", 2 }, /* PG0-PG5 */
+ { "mmc2", 3 }, /* PC5-PC16 */
+ { "spi0", 3 }, /* PC0-PC3 */
+#if IS_ENABLED(CONFIG_UART0_PORT_F)
+ { "uart0", 3 }, /* PF2-PF4 */
+#else
+ { "uart0", 2 }, /* PB9-PB10 */
+#endif
+ { "uart1", 2 }, /* PG6-PG7 */
+ { "uart2", 2 }, /* PB0-PB1 */
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun8i_a83t_pinctrl_desc = {
+ .functions = sun8i_a83t_pinctrl_functions,
+ .num_functions = ARRAY_SIZE(sun8i_a83t_pinctrl_functions),
+ .first_bank = SUNXI_GPIO_A,
+ .num_banks = 8,
+};
+
+static const struct sunxi_pinctrl_function sun8i_a83t_r_pinctrl_functions[] = {
+ { "gpio_in", 0 },
+ { "gpio_out", 1 },
+ { "s_i2c", 2 }, /* PL8-PL9 */
+ { "s_uart", 2 }, /* PL2-PL3 */
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun8i_a83t_r_pinctrl_desc = {
+ .functions = sun8i_a83t_r_pinctrl_functions,
+ .num_functions = ARRAY_SIZE(sun8i_a83t_r_pinctrl_functions),
+ .first_bank = SUNXI_GPIO_L,
+ .num_banks = 1,
+};
+
+static const struct sunxi_pinctrl_function sun8i_h3_pinctrl_functions[] = {
+ { "emac", 2 }, /* PD0-PD17 */
+ { "gpio_in", 0 },
+ { "gpio_out", 1 },
+ { "i2c0", 2 }, /* PA11-PA12 */
+ { "i2c1", 3 }, /* PA18-PA19 */
+ { "mmc0", 2 }, /* PF0-PF5 */
+ { "mmc1", 2 }, /* PG0-PG5 */
+ { "mmc2", 3 }, /* PC5-PC16 */
+ { "spi0", 3 }, /* PC0-PC3 */
+#if IS_ENABLED(CONFIG_UART0_PORT_F)
+ { "uart0", 3 }, /* PF2-PF4 */
+#else
+ { "uart0", 2 }, /* PA4-PA5 */
+#endif
+ { "uart1", 2 }, /* PG6-PG7 */
+ { "uart2", 2 }, /* PA0-PA1 */
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun8i_h3_pinctrl_desc = {
+ .functions = sun8i_h3_pinctrl_functions,
+ .num_functions = ARRAY_SIZE(sun8i_h3_pinctrl_functions),
+ .first_bank = SUNXI_GPIO_A,
+ .num_banks = 7,
+};
+
+static const struct sunxi_pinctrl_function sun8i_h3_r_pinctrl_functions[] = {
+ { "gpio_in", 0 },
+ { "gpio_out", 1 },
+ { "s_i2c", 2 }, /* PL0-PL1 */
+ { "s_uart", 2 }, /* PL2-PL3 */
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun8i_h3_r_pinctrl_desc = {
+ .functions = sun8i_h3_r_pinctrl_functions,
+ .num_functions = ARRAY_SIZE(sun8i_h3_r_pinctrl_functions),
+ .first_bank = SUNXI_GPIO_L,
+ .num_banks = 1,
+};
+
+static const struct sunxi_pinctrl_function sun8i_v3s_pinctrl_functions[] = {
+ { "emac", 4 }, /* PD0-PD17 */
+ { "gpio_in", 0 },
+ { "gpio_out", 1 },
+ { "i2c0", 2 }, /* PB6-PB7 */
+ { "i2c1", 2 }, /* PB8-PB9 */
+ { "mmc0", 2 }, /* PF0-PF5 */
+ { "mmc1", 2 }, /* PG0-PG5 */
+ { "mmc2", 2 }, /* PC0-PC10 */
+ { "spi0", 3 }, /* PC0-PC3 */
+#if IS_ENABLED(CONFIG_UART0_PORT_F)
+ { "uart0", 3 }, /* PF2-PF4 */
+#else
+ { "uart0", 3 }, /* PB8-PB9 */
+#endif
+ { "uart1", 2 }, /* PG6-PG7 */
+ { "uart2", 2 }, /* PB0-PB1 */
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun8i_v3s_pinctrl_desc = {
+ .functions = sun8i_v3s_pinctrl_functions,
+ .num_functions = ARRAY_SIZE(sun8i_v3s_pinctrl_functions),
+ .first_bank = SUNXI_GPIO_A,
+ .num_banks = 7,
+};
+
+static const struct sunxi_pinctrl_function sun9i_a80_pinctrl_functions[] = {
+ { "gmac", 2 }, /* PA0-PA17 */
+ { "gpio_in", 0 },
+ { "gpio_out", 1 },
+ { "i2c0", 2 }, /* PH0-PH1 */
+ { "i2c1", 2 }, /* PH2-PH3 */
+ { "mmc0", 2 }, /* PF0-PF5 */
+ { "mmc1", 2 }, /* PG0-PG5 */
+ { "mmc2", 3 }, /* PC6-PC16 */
+ { "spi0", 3 }, /* PC0-PC2, PC19 */
+#if IS_ENABLED(CONFIG_UART0_PORT_F)
+ { "uart0", 4 }, /* PF2-PF4 */
+#else
+ { "uart0", 2 }, /* PH12-PH13 */
+#endif
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun9i_a80_pinctrl_desc = {
+ .functions = sun9i_a80_pinctrl_functions,
+ .num_functions = ARRAY_SIZE(sun9i_a80_pinctrl_functions),
+ .first_bank = SUNXI_GPIO_A,
+ .num_banks = 8,
+};
+
+static const struct sunxi_pinctrl_function sun9i_a80_r_pinctrl_functions[] = {
+ { "gpio_in", 0 },
+ { "gpio_out", 1 },
+ { "s_i2c0", 2 }, /* PN0-PN1 */
+ { "s_i2c1", 3 }, /* PM8-PM9 */
+ { "s_uart", 3 }, /* PL0-PL1 */
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun9i_a80_r_pinctrl_desc = {
+ .functions = sun9i_a80_r_pinctrl_functions,
+ .num_functions = ARRAY_SIZE(sun9i_a80_r_pinctrl_functions),
+ .first_bank = SUNXI_GPIO_L,
+ .num_banks = 3,
+};
+
+static const struct sunxi_pinctrl_function sun50i_a64_pinctrl_functions[] = {
+ { "emac", 4 }, /* PD8-PD23 */
+ { "gpio_in", 0 },
+ { "gpio_out", 1 },
+ { "i2c0", 2 }, /* PH0-PH1 */
+ { "i2c1", 2 }, /* PH2-PH3 */
+ { "mmc0", 2 }, /* PF0-PF5 */
+ { "mmc1", 2 }, /* PG0-PG5 */
+ { "mmc2", 3 }, /* PC1-PC16 */
+ { "pwm", 2 }, /* PD22 */
+ { "spi0", 4 }, /* PC0-PC3 */
+#if IS_ENABLED(CONFIG_UART0_PORT_F)
+ { "uart0", 3 }, /* PF2-PF4 */
+#else
+ { "uart0", 4 }, /* PB8-PB9 */
+#endif
+ { "uart1", 2 }, /* PG6-PG7 */
+ { "uart2", 2 }, /* PB0-PB1 */
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun50i_a64_pinctrl_desc = {
+ .functions = sun50i_a64_pinctrl_functions,
+ .num_functions = ARRAY_SIZE(sun50i_a64_pinctrl_functions),
+ .first_bank = SUNXI_GPIO_A,
+ .num_banks = 8,
+};
+
+static const struct sunxi_pinctrl_function sun50i_a64_r_pinctrl_functions[] = {
+ { "gpio_in", 0 },
+ { "gpio_out", 1 },
+ { "s_i2c", 2 }, /* PL8-PL9 */
+ { "s_uart", 2 }, /* PL2-PL3 */
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun50i_a64_r_pinctrl_desc = {
+ .functions = sun50i_a64_r_pinctrl_functions,
+ .num_functions = ARRAY_SIZE(sun50i_a64_r_pinctrl_functions),
+ .first_bank = SUNXI_GPIO_L,
+ .num_banks = 1,
+};
+
+static const struct sunxi_pinctrl_function sun50i_h5_pinctrl_functions[] = {
+ { "emac", 2 }, /* PD0-PD17 */
+ { "gpio_in", 0 },
+ { "gpio_out", 1 },
+ { "i2c0", 2 }, /* PA11-PA12 */
+ { "i2c1", 3 }, /* PA18-PA19 */
+ { "mmc0", 2 }, /* PF0-PF5 */
+ { "mmc1", 2 }, /* PG0-PG5 */
+ { "mmc2", 3 }, /* PC1-PC16 */
+ { "spi0", 3 }, /* PC0-PC3 */
+#if IS_ENABLED(CONFIG_UART0_PORT_F)
+ { "uart0", 3 }, /* PF2-PF4 */
+#else
+ { "uart0", 2 }, /* PA4-PA5 */
+#endif
+ { "uart1", 2 }, /* PG6-PG7 */
+ { "uart2", 2 }, /* PA0-PA1 */
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun50i_h5_pinctrl_desc = {
+ .functions = sun50i_h5_pinctrl_functions,
+ .num_functions = ARRAY_SIZE(sun50i_h5_pinctrl_functions),
+ .first_bank = SUNXI_GPIO_A,
+ .num_banks = 7,
+};
+
+static const struct sunxi_pinctrl_function sun50i_h6_pinctrl_functions[] = {
+ { "emac", 5 }, /* PD0-PD20 */
+ { "gpio_in", 0 },
+ { "gpio_out", 1 },
+ { "i2c0", 2 }, /* PD25-PD26 */
+ { "i2c1", 4 }, /* PH5-PH6 */
+ { "mmc0", 2 }, /* PF0-PF5 */
+ { "mmc1", 2 }, /* PG0-PG5 */
+ { "mmc2", 3 }, /* PC1-PC14 */
+ { "spi0", 4 }, /* PC0-PC7 */
+#if IS_ENABLED(CONFIG_UART0_PORT_F)
+ { "uart0", 3 }, /* PF2-PF4 */
+#else
+ { "uart0", 2 }, /* PH0-PH1 */
+#endif
+ { "uart1", 2 }, /* PG6-PG7 */
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun50i_h6_pinctrl_desc = {
+ .functions = sun50i_h6_pinctrl_functions,
+ .num_functions = ARRAY_SIZE(sun50i_h6_pinctrl_functions),
+ .first_bank = SUNXI_GPIO_A,
+ .num_banks = 8,
+};
+
+static const struct sunxi_pinctrl_function sun50i_h6_r_pinctrl_functions[] = {
+ { "gpio_in", 0 },
+ { "gpio_out", 1 },
+ { "s_i2c", 3 }, /* PL0-PL1 */
+ { "s_uart", 2 }, /* PL2-PL3 */
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun50i_h6_r_pinctrl_desc = {
+ .functions = sun50i_h6_r_pinctrl_functions,
+ .num_functions = ARRAY_SIZE(sun50i_h6_r_pinctrl_functions),
+ .first_bank = SUNXI_GPIO_L,
+ .num_banks = 2,
+};
+
+static const struct sunxi_pinctrl_function sun50i_h616_pinctrl_functions[] = {
+ { "emac0", 2 }, /* PI0-PI16 */
+ { "gpio_in", 0 },
+ { "gpio_out", 1 },
+ { "mmc0", 2 }, /* PF0-PF5 */
+ { "mmc1", 2 }, /* PG0-PG5 */
+ { "mmc2", 3 }, /* PC0-PC16 */
+ { "spi0", 4 }, /* PC0-PC7, PC15-PC16 */
+#if IS_ENABLED(CONFIG_UART0_PORT_F)
+ { "uart0", 3 }, /* PF2-PF4 */
+#else
+ { "uart0", 2 }, /* PH0-PH1 */
+#endif
+ { "uart1", 2 }, /* PG6-PG7 */
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun50i_h616_pinctrl_desc = {
+ .functions = sun50i_h616_pinctrl_functions,
+ .num_functions = ARRAY_SIZE(sun50i_h616_pinctrl_functions),
+ .first_bank = SUNXI_GPIO_A,
+ .num_banks = 9,
+};
+
+static const struct sunxi_pinctrl_function sun50i_h616_r_pinctrl_functions[] = {
+ { "gpio_in", 0 },
+ { "gpio_out", 1 },
+ { "s_i2c", 3 }, /* PL0-PL1 */
+ { "s_uart", 2 }, /* PL2-PL3 */
+};
+
+static const struct sunxi_pinctrl_desc __maybe_unused sun50i_h616_r_pinctrl_desc = {
+ .functions = sun50i_h616_r_pinctrl_functions,
+ .num_functions = ARRAY_SIZE(sun50i_h616_r_pinctrl_functions),
+ .first_bank = SUNXI_GPIO_L,
+ .num_banks = 1,
+};
+
+static const struct udevice_id sunxi_pinctrl_ids[] = {
+#ifdef CONFIG_PINCTRL_SUNIV_F1C100S
+ {
+ .compatible = "allwinner,suniv-f1c100s-pinctrl",
+ .data = (ulong)&suniv_f1c100s_pinctrl_desc,
+ },
+#endif
+#ifdef CONFIG_PINCTRL_SUN4I_A10
+ {
+ .compatible = "allwinner,sun4i-a10-pinctrl",
+ .data = (ulong)&sun4i_a10_pinctrl_desc,
+ },
+#endif
+#ifdef CONFIG_PINCTRL_SUN5I_A13
+ {
+ .compatible = "allwinner,sun5i-a10s-pinctrl",
+ .data = (ulong)&sun5i_a13_pinctrl_desc,
+ },
+ {
+ .compatible = "allwinner,sun5i-a13-pinctrl",
+ .data = (ulong)&sun5i_a13_pinctrl_desc,
+ },
+#endif
+#ifdef CONFIG_PINCTRL_SUN6I_A31
+ {
+ .compatible = "allwinner,sun6i-a31-pinctrl",
+ .data = (ulong)&sun6i_a31_pinctrl_desc,
+ },
+ {
+ .compatible = "allwinner,sun6i-a31s-pinctrl",
+ .data = (ulong)&sun6i_a31_pinctrl_desc,
+ },
+#endif
+#ifdef CONFIG_PINCTRL_SUN6I_A31_R
+ {
+ .compatible = "allwinner,sun6i-a31-r-pinctrl",
+ .data = (ulong)&sun6i_a31_r_pinctrl_desc,
+ },
+#endif
+#ifdef CONFIG_PINCTRL_SUN7I_A20
+ {
+ .compatible = "allwinner,sun7i-a20-pinctrl",
+ .data = (ulong)&sun7i_a20_pinctrl_desc,
+ },
+#endif
+#ifdef CONFIG_PINCTRL_SUN8I_A23
+ {
+ .compatible = "allwinner,sun8i-a23-pinctrl",
+ .data = (ulong)&sun8i_a23_pinctrl_desc,
+ },
+#endif
+#ifdef CONFIG_PINCTRL_SUN8I_A23_R
+ {
+ .compatible = "allwinner,sun8i-a23-r-pinctrl",
+ .data = (ulong)&sun8i_a23_r_pinctrl_desc,
+ },
+#endif
+#ifdef CONFIG_PINCTRL_SUN8I_A33
+ {
+ .compatible = "allwinner,sun8i-a33-pinctrl",
+ .data = (ulong)&sun8i_a33_pinctrl_desc,
+ },
+#endif
+#ifdef CONFIG_PINCTRL_SUN8I_A83T
+ {
+ .compatible = "allwinner,sun8i-a83t-pinctrl",
+ .data = (ulong)&sun8i_a83t_pinctrl_desc,
+ },
+#endif
+#ifdef CONFIG_PINCTRL_SUN8I_A83T_R
+ {
+ .compatible = "allwinner,sun8i-a83t-r-pinctrl",
+ .data = (ulong)&sun8i_a83t_r_pinctrl_desc,
+ },
+#endif
+#ifdef CONFIG_PINCTRL_SUN8I_H3
+ {
+ .compatible = "allwinner,sun8i-h3-pinctrl",
+ .data = (ulong)&sun8i_h3_pinctrl_desc,
+ },
+#endif
+#ifdef CONFIG_PINCTRL_SUN8I_H3_R
+ {
+ .compatible = "allwinner,sun8i-h3-r-pinctrl",
+ .data = (ulong)&sun8i_h3_r_pinctrl_desc,
+ },
+#endif
+#ifdef CONFIG_PINCTRL_SUN7I_A20
+ {
+ .compatible = "allwinner,sun8i-r40-pinctrl",
+ .data = (ulong)&sun7i_a20_pinctrl_desc,
+ },
+#endif
+#ifdef CONFIG_PINCTRL_SUN8I_V3S
+ {
+ .compatible = "allwinner,sun8i-v3-pinctrl",
+ .data = (ulong)&sun8i_v3s_pinctrl_desc,
+ },
+ {
+ .compatible = "allwinner,sun8i-v3s-pinctrl",
+ .data = (ulong)&sun8i_v3s_pinctrl_desc,
+ },
+#endif
+#ifdef CONFIG_PINCTRL_SUN9I_A80
+ {
+ .compatible = "allwinner,sun9i-a80-pinctrl",
+ .data = (ulong)&sun9i_a80_pinctrl_desc,
+ },
+#endif
+#ifdef CONFIG_PINCTRL_SUN9I_A80_R
+ {
+ .compatible = "allwinner,sun9i-a80-r-pinctrl",
+ .data = (ulong)&sun9i_a80_r_pinctrl_desc,
+ },
+#endif
+#ifdef CONFIG_PINCTRL_SUN50I_A64
+ {
+ .compatible = "allwinner,sun50i-a64-pinctrl",
+ .data = (ulong)&sun50i_a64_pinctrl_desc,
+ },
+#endif
+#ifdef CONFIG_PINCTRL_SUN50I_A64_R
+ {
+ .compatible = "allwinner,sun50i-a64-r-pinctrl",
+ .data = (ulong)&sun50i_a64_r_pinctrl_desc,
+ },
+#endif
+#ifdef CONFIG_PINCTRL_SUN50I_H5
+ {
+ .compatible = "allwinner,sun50i-h5-pinctrl",
+ .data = (ulong)&sun50i_h5_pinctrl_desc,
+ },
+#endif
+#ifdef CONFIG_PINCTRL_SUN50I_H6
+ {
+ .compatible = "allwinner,sun50i-h6-pinctrl",
+ .data = (ulong)&sun50i_h6_pinctrl_desc,
+ },
+#endif
+#ifdef CONFIG_PINCTRL_SUN50I_H6_R
+ {
+ .compatible = "allwinner,sun50i-h6-r-pinctrl",
+ .data = (ulong)&sun50i_h6_r_pinctrl_desc,
+ },
+#endif
+#ifdef CONFIG_PINCTRL_SUN50I_H616
+ {
+ .compatible = "allwinner,sun50i-h616-pinctrl",
+ .data = (ulong)&sun50i_h616_pinctrl_desc,
+ },
+#endif
+#ifdef CONFIG_PINCTRL_SUN50I_H616_R
+ {
+ .compatible = "allwinner,sun50i-h616-r-pinctrl",
+ .data = (ulong)&sun50i_h616_r_pinctrl_desc,
+ },
+#endif
+ {}
+};
+
+U_BOOT_DRIVER(sunxi_pinctrl) = {
+ .name = "sunxi-pinctrl",
+ .id = UCLASS_PINCTRL,
+ .of_match = sunxi_pinctrl_ids,
+ .bind = sunxi_pinctrl_bind,
+ .probe = sunxi_pinctrl_probe,
+ .plat_auto = sizeof(struct sunxi_pinctrl_plat),
+ .ops = &sunxi_pinctrl_ops,
+};
diff --git a/drivers/pwm/sunxi_pwm.c b/drivers/pwm/sunxi_pwm.c
index e3d5ee4..bb1bec0 100644
--- a/drivers/pwm/sunxi_pwm.c
+++ b/drivers/pwm/sunxi_pwm.c
@@ -13,7 +13,6 @@
#include <asm/global_data.h>
#include <asm/io.h>
#include <asm/arch/pwm.h>
-#include <asm/arch/gpio.h>
#include <power/regulator.h>
DECLARE_GLOBAL_DATA_PTR;
@@ -45,14 +44,6 @@ static const u32 prescaler_table[] = {
1, /* 1111 */
};
-static int sunxi_pwm_config_pinmux(void)
-{
-#ifdef CONFIG_MACH_SUN50I
- sunxi_gpio_set_cfgpin(SUNXI_GPD(22), SUNXI_GPD_PWM);
-#endif
- return 0;
-}
-
static int sunxi_pwm_set_invert(struct udevice *dev, uint channel,
bool polarity)
{
@@ -137,8 +128,6 @@ static int sunxi_pwm_set_enable(struct udevice *dev, uint channel, bool enable)
return 0;
}
- sunxi_pwm_config_pinmux();
-
if (priv->invert)
v &= ~SUNXI_PWM_CTRL_CH0_ACT_STA;
else
diff --git a/drivers/spi/spi-sunxi.c b/drivers/spi/spi-sunxi.c
index d62355e..b6cd7dd 100644
--- a/drivers/spi/spi-sunxi.c
+++ b/drivers/spi/spi-sunxi.c
@@ -32,7 +32,6 @@
#include <linux/bitops.h>
#include <asm/bitops.h>
-#include <asm/gpio.h>
#include <asm/io.h>
#include <linux/iopoll.h>
@@ -180,87 +179,6 @@ static void sun4i_spi_set_cs(struct udevice *bus, u8 cs, bool enable)
writel(reg, SPI_REG(priv, SPI_TCR));
}
-static int sun4i_spi_parse_pins(struct udevice *dev)
-{
- const void *fdt = gd->fdt_blob;
- const char *pin_name;
- const fdt32_t *list;
- u32 phandle;
- int drive, pull = 0, pin, i;
- int offset;
- int size;
-
- list = fdt_getprop(fdt, dev_of_offset(dev), "pinctrl-0", &size);
- if (!list) {
- printf("WARNING: sun4i_spi: cannot find pinctrl-0 node\n");
- return -EINVAL;
- }
-
- while (size) {
- phandle = fdt32_to_cpu(*list++);
- size -= sizeof(*list);
-
- offset = fdt_node_offset_by_phandle(fdt, phandle);
- if (offset < 0)
- return offset;
-
- drive = fdt_getprop_u32_default_node(fdt, offset, 0,
- "drive-strength", 0);
- if (drive) {
- if (drive <= 10)
- drive = 0;
- else if (drive <= 20)
- drive = 1;
- else if (drive <= 30)
- drive = 2;
- else
- drive = 3;
- } else {
- drive = fdt_getprop_u32_default_node(fdt, offset, 0,
- "allwinner,drive",
- 0);
- drive = min(drive, 3);
- }
-
- if (fdt_get_property(fdt, offset, "bias-disable", NULL))
- pull = 0;
- else if (fdt_get_property(fdt, offset, "bias-pull-up", NULL))
- pull = 1;
- else if (fdt_get_property(fdt, offset, "bias-pull-down", NULL))
- pull = 2;
- else
- pull = fdt_getprop_u32_default_node(fdt, offset, 0,
- "allwinner,pull",
- 0);
- pull = min(pull, 2);
-
- for (i = 0; ; i++) {
- pin_name = fdt_stringlist_get(fdt, offset,
- "pins", i, NULL);
- if (!pin_name) {
- pin_name = fdt_stringlist_get(fdt, offset,
- "allwinner,pins",
- i, NULL);
- if (!pin_name)
- break;
- }
-
- pin = sunxi_name_to_gpio(pin_name);
- if (pin < 0)
- break;
-
- if (IS_ENABLED(CONFIG_MACH_SUN50I) ||
- IS_ENABLED(CONFIG_SUN50I_GEN_H6))
- sunxi_gpio_set_cfgpin(pin, SUN50I_GPC_SPI0);
- else
- sunxi_gpio_set_cfgpin(pin, SUNXI_GPC_SPI0);
- sunxi_gpio_set_drv(pin, drive);
- sunxi_gpio_set_pull(pin, pull);
- }
- }
- return 0;
-}
-
static inline int sun4i_spi_set_clock(struct udevice *dev, bool enable)
{
struct sun4i_spi_priv *priv = dev_get_priv(dev);
@@ -507,8 +425,6 @@ static int sun4i_spi_probe(struct udevice *bus)
return ret;
}
- sun4i_spi_parse_pins(bus);
-
priv->variant = plat->variant;
priv->base = plat->base;
priv->freq = plat->max_hz;
diff --git a/include/image.h b/include/image.h
index 498eb7f..673b5f5 100644
--- a/include/image.h
+++ b/include/image.h
@@ -228,6 +228,7 @@ enum {
IH_TYPE_IMX8IMAGE, /* Freescale IMX8Boot Image */
IH_TYPE_COPRO, /* Coprocessor Image for remoteproc*/
IH_TYPE_SUNXI_EGON, /* Allwinner eGON Boot Image */
+ IH_TYPE_SUNXI_TOC0, /* Allwinner TOC0 Boot Image */
IH_TYPE_COUNT, /* Number of image types */
};
diff --git a/include/spl.h b/include/spl.h
index 8ceb3c0..6134aba 100644
--- a/include/spl.h
+++ b/include/spl.h
@@ -14,6 +14,7 @@
#include <asm/global_data.h>
#include <asm/spl.h>
#include <handoff.h>
+#include <mmc.h>
struct blk_desc;
struct image_header;
@@ -375,7 +376,7 @@ u32 spl_boot_device(void);
* Note: It is important to use the boot_device parameter instead of e.g.
* spl_boot_device() as U-Boot is not always loaded from the same device as SPL.
*/
-u32 spl_mmc_boot_mode(const u32 boot_device);
+u32 spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device);
/**
* spl_mmc_boot_partition() - MMC partition to load U-Boot from.
diff --git a/include/sunxi_image.h b/include/sunxi_image.h
index 5b2055c..379ca91 100644
--- a/include/sunxi_image.h
+++ b/include/sunxi_image.h
@@ -9,9 +9,13 @@
*
* Shared between mkimage and the SPL.
*/
+
#ifndef SUNXI_IMAGE_H
#define SUNXI_IMAGE_H
+#include <linux/compiler_attributes.h>
+#include <linux/types.h>
+
#define BOOT0_MAGIC "eGON.BT0"
#define BROM_STAMP_VALUE 0x5f0a6c39
#define SPL_SIGNATURE "SPL" /* marks "sunxi" SPL header */
@@ -79,4 +83,37 @@ struct boot_file_head {
/* Compile time check to assure proper alignment of structure */
typedef char boot_file_head_not_multiple_of_32[1 - 2*(sizeof(struct boot_file_head) % 32)];
+struct __packed toc0_main_info {
+ uint8_t name[8];
+ __le32 magic;
+ __le32 checksum;
+ __le32 serial;
+ __le32 status;
+ __le32 num_items;
+ __le32 length;
+ uint8_t platform[4];
+ uint8_t reserved[8];
+ uint8_t end[4];
+};
+
+#define TOC0_MAIN_INFO_NAME "TOC0.GLH"
+#define TOC0_MAIN_INFO_MAGIC 0x89119800
+#define TOC0_MAIN_INFO_END "MIE;"
+
+struct __packed toc0_item_info {
+ __le32 name;
+ __le32 offset;
+ __le32 length;
+ __le32 status;
+ __le32 type;
+ __le32 load_addr;
+ uint8_t reserved[4];
+ uint8_t end[4];
+};
+
+#define TOC0_ITEM_INFO_NAME_CERT 0x00010101
+#define TOC0_ITEM_INFO_NAME_FIRMWARE 0x00010202
+#define TOC0_ITEM_INFO_NAME_KEY 0x00010303
+#define TOC0_ITEM_INFO_END "IIE;"
+
#endif
diff --git a/scripts/Makefile.spl b/scripts/Makefile.spl
index 83a95ee..6ad82ce 100644
--- a/scripts/Makefile.spl
+++ b/scripts/Makefile.spl
@@ -411,7 +411,10 @@ endif
$(obj)/$(SPL_BIN).sfp: $(obj)/$(SPL_BIN).bin FORCE
$(call if_changed,mkimage)
-MKIMAGEFLAGS_sunxi-spl.bin = -T sunxi_egon \
+MKIMAGEFLAGS_sunxi-spl.bin = \
+ -A $(ARCH) \
+ -T $(CONFIG_SPL_IMAGE_TYPE) \
+ -a $(CONFIG_SPL_TEXT_BASE) \
-n $(CONFIG_DEFAULT_DEVICE_TREE)
OBJCOPYFLAGS_u-boot-spl-dtb.hex := -I binary -O ihex --change-address=$(CONFIG_SPL_TEXT_BASE)
diff --git a/tools/Makefile b/tools/Makefile
index 60231c7..e17271b 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -94,9 +94,10 @@ ECDSA_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := $(addprefix lib/ecdsa/, ecdsa-libcrypto.
AES_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := $(addprefix lib/aes/, \
aes-encrypt.o aes-decrypt.o)
-# Cryptographic helpers that depend on openssl/libcrypto
-LIBCRYPTO_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := $(addprefix lib/, \
- fdt-libcrypto.o)
+# Cryptographic helpers and image types that depend on openssl/libcrypto
+LIBCRYPTO_OBJS-$(CONFIG_TOOLS_LIBCRYPTO) := \
+ lib/fdt-libcrypto.o \
+ sunxi_toc0.o
ROCKCHIP_OBS = lib/rc4.o rkcommon.o rkimage.o rksd.o rkspi.o
diff --git a/tools/imagetool.h b/tools/imagetool.h
index 5169b02..05dd94d 100644
--- a/tools/imagetool.h
+++ b/tools/imagetool.h
@@ -53,6 +53,7 @@ struct image_tool_params {
int pflag;
int vflag;
int xflag;
+ int Aflag;
int skipcpy;
int os;
int arch;
diff --git a/tools/mkimage.c b/tools/mkimage.c
index 74bd072..b46216f 100644
--- a/tools/mkimage.c
+++ b/tools/mkimage.c
@@ -172,6 +172,7 @@ static void process_args(int argc, char **argv)
show_valid_options(IH_ARCH);
usage("Invalid architecture");
}
+ params.Aflag = 1;
break;
case 'b':
if (add_content(IH_TYPE_FLATDT, optarg)) {
diff --git a/tools/sunxi_egon.c b/tools/sunxi_egon.c
index d1398c0..d45b6f5 100644
--- a/tools/sunxi_egon.c
+++ b/tools/sunxi_egon.c
@@ -15,9 +15,29 @@
#define PAD_SIZE 8192
#define PAD_SIZE_MIN 512
+static int egon_get_arch(struct image_tool_params *params)
+{
+ if (params->Aflag)
+ return params->arch;
+
+ /* For compatibility, assume ARM when no architecture specified */
+ return IH_ARCH_ARM;
+}
+
static int egon_check_params(struct image_tool_params *params)
{
- /* We just need a binary image file. */
+ /*
+ * Check whether the architecture is supported.
+ */
+ switch (egon_get_arch(params)) {
+ case IH_ARCH_ARM:
+ case IH_ARCH_RISCV:
+ break;
+ default:
+ return EXIT_FAILURE;
+ }
+
+ /* We need a binary image file. */
return !params->dflag;
}
@@ -27,9 +47,22 @@ static int egon_verify_header(unsigned char *ptr, int image_size,
const struct boot_file_head *header = (void *)ptr;
uint32_t length;
- /* First 4 bytes must be an ARM branch instruction. */
- if ((le32_to_cpu(header->b_instruction) & 0xff000000) != 0xea000000)
- return EXIT_FAILURE;
+ /*
+ * First 4 bytes must be a branch instruction of the corresponding
+ * architecture.
+ */
+ switch (egon_get_arch(params)) {
+ case IH_ARCH_ARM:
+ if ((le32_to_cpu(header->b_instruction) & 0xff000000) != 0xea000000)
+ return EXIT_FAILURE;
+ break;
+ case IH_ARCH_RISCV:
+ if ((le32_to_cpu(header->b_instruction) & 0x00000fff) != 0x0000006f)
+ return EXIT_FAILURE;
+ break;
+ default:
+ return EXIT_FAILURE; /* Unknown architecture */
+ }
if (memcmp(header->magic, BOOT0_MAGIC, sizeof(header->magic)))
return EXIT_FAILURE;
@@ -78,9 +111,35 @@ static void egon_set_header(void *buf, struct stat *sbuf, int infd,
uint32_t checksum = 0, value;
int i;
- /* Generate an ARM branch instruction to jump over the header. */
- value = 0xea000000 | (sizeof(struct boot_file_head) / 4 - 2);
- header->b_instruction = cpu_to_le32(value);
+ /*
+ * Different architectures need different first instruction to
+ * branch to the body.
+ */
+ switch (egon_get_arch(params)) {
+ case IH_ARCH_ARM:
+ /* Generate an ARM branch instruction to jump over the header. */
+ value = 0xea000000 | (sizeof(struct boot_file_head) / 4 - 2);
+ header->b_instruction = cpu_to_le32(value);
+ break;
+ case IH_ARCH_RISCV:
+ /*
+ * Generate a RISC-V JAL instruction with rd=x0
+ * (pseudo instruction J, jump without side effects).
+ *
+ * The following weird bit operation maps imm[20]
+ * to inst[31], imm[10:1] to inst[30:21],
+ * imm[11] to inst[20], imm[19:12] to inst[19:12],
+ * and imm[0] is dropped (because 1-byte RISC-V instruction
+ * is not allowed).
+ */
+ value = 0x0000006f |
+ ((sizeof(struct boot_file_head) & 0x00100000) << 11) |
+ ((sizeof(struct boot_file_head) & 0x000007fe) << 20) |
+ ((sizeof(struct boot_file_head) & 0x00000800) << 9) |
+ ((sizeof(struct boot_file_head) & 0x000ff000) << 0);
+ header->b_instruction = cpu_to_le32(value);
+ break;
+ }
memcpy(header->magic, BOOT0_MAGIC, sizeof(header->magic));
header->check_sum = cpu_to_le32(BROM_STAMP_VALUE);
diff --git a/tools/sunxi_toc0.c b/tools/sunxi_toc0.c
new file mode 100644
index 0000000..58a6e7a
--- /dev/null
+++ b/tools/sunxi_toc0.c
@@ -0,0 +1,907 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2018 Arm Ltd.
+ * (C) Copyright 2020-2021 Samuel Holland <samuel@sholland.org>
+ */
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <openssl/asn1t.h>
+#include <openssl/pem.h>
+#include <openssl/rsa.h>
+
+#include <image.h>
+#include <sunxi_image.h>
+
+#include "imagetool.h"
+#include "mkimage.h"
+
+/*
+ * NAND requires 8K padding. For other devices, BROM requires only
+ * 512B padding, but let's use the larger padding to cover everything.
+ */
+#define PAD_SIZE 8192
+
+#define pr_fmt(fmt) "mkimage (TOC0): %s: " fmt
+#define pr_err(fmt, args...) fprintf(stderr, pr_fmt(fmt), "error", ##args)
+#define pr_warn(fmt, args...) fprintf(stderr, pr_fmt(fmt), "warning", ##args)
+#define pr_info(fmt, args...) fprintf(stderr, pr_fmt(fmt), "info", ##args)
+
+struct __packed toc0_key_item {
+ __le32 vendor_id;
+ __le32 key0_n_len;
+ __le32 key0_e_len;
+ __le32 key1_n_len;
+ __le32 key1_e_len;
+ __le32 sig_len;
+ uint8_t key0[512];
+ uint8_t key1[512];
+ uint8_t reserved[32];
+ uint8_t sig[256];
+};
+
+/*
+ * This looks somewhat like an X.509 certificate, but it is not valid BER.
+ *
+ * Some differences:
+ * - Some X.509 certificate fields are missing or rearranged.
+ * - Some sequences have the wrong tag.
+ * - Zero-length sequences are accepted.
+ * - Large strings and integers must be an even number of bytes long.
+ * - Positive integers are not zero-extended to maintain their sign.
+ *
+ * See https://linux-sunxi.org/TOC0 for more information.
+ */
+struct __packed toc0_small_tag {
+ uint8_t tag;
+ uint8_t length;
+};
+
+typedef struct toc0_small_tag toc0_small_int;
+typedef struct toc0_small_tag toc0_small_oct;
+typedef struct toc0_small_tag toc0_small_seq;
+typedef struct toc0_small_tag toc0_small_exp;
+
+#define TOC0_SMALL_INT(len) { 0x02, (len) }
+#define TOC0_SMALL_SEQ(len) { 0x30, (len) }
+#define TOC0_SMALL_EXP(tag, len) { 0xa0 | (tag), len }
+
+struct __packed toc0_large_tag {
+ uint8_t tag;
+ uint8_t prefix;
+ uint8_t length_hi;
+ uint8_t length_lo;
+};
+
+typedef struct toc0_large_tag toc0_large_int;
+typedef struct toc0_large_tag toc0_large_bit;
+typedef struct toc0_large_tag toc0_large_seq;
+
+#define TOC0_LARGE_INT(len) { 0x02, 0x82, (len) >> 8, (len) & 0xff }
+#define TOC0_LARGE_BIT(len) { 0x03, 0x82, (len) >> 8, (len) & 0xff }
+#define TOC0_LARGE_SEQ(len) { 0x30, 0x82, (len) >> 8, (len) & 0xff }
+
+struct __packed toc0_cert_item {
+ toc0_large_seq tag_totalSequence;
+ struct __packed toc0_totalSequence {
+ toc0_large_seq tag_mainSequence;
+ struct __packed toc0_mainSequence {
+ toc0_small_exp tag_explicit0;
+ struct __packed toc0_explicit0 {
+ toc0_small_int tag_version;
+ uint8_t version;
+ } explicit0;
+ toc0_small_int tag_serialNumber;
+ uint8_t serialNumber;
+ toc0_small_seq tag_signature;
+ toc0_small_seq tag_issuer;
+ toc0_small_seq tag_validity;
+ toc0_small_seq tag_subject;
+ toc0_large_seq tag_subjectPublicKeyInfo;
+ struct __packed toc0_subjectPublicKeyInfo {
+ toc0_small_seq tag_algorithm;
+ toc0_large_seq tag_publicKey;
+ struct __packed toc0_publicKey {
+ toc0_large_int tag_n;
+ uint8_t n[256];
+ toc0_small_int tag_e;
+ uint8_t e[3];
+ } publicKey;
+ } subjectPublicKeyInfo;
+ toc0_small_exp tag_explicit3;
+ struct __packed toc0_explicit3 {
+ toc0_small_seq tag_extension;
+ struct __packed toc0_extension {
+ toc0_small_int tag_digest;
+ uint8_t digest[32];
+ } extension;
+ } explicit3;
+ } mainSequence;
+ toc0_large_bit tag_sigSequence;
+ struct __packed toc0_sigSequence {
+ toc0_small_seq tag_algorithm;
+ toc0_large_bit tag_signature;
+ uint8_t signature[256];
+ } sigSequence;
+ } totalSequence;
+};
+
+#define sizeof_field(TYPE, MEMBER) sizeof((((TYPE *)0)->MEMBER))
+
+static const struct toc0_cert_item cert_item_template = {
+ TOC0_LARGE_SEQ(sizeof(struct toc0_totalSequence)),
+ {
+ TOC0_LARGE_SEQ(sizeof(struct toc0_mainSequence)),
+ {
+ TOC0_SMALL_EXP(0, sizeof(struct toc0_explicit0)),
+ {
+ TOC0_SMALL_INT(sizeof_field(struct toc0_explicit0, version)),
+ 0,
+ },
+ TOC0_SMALL_INT(sizeof_field(struct toc0_mainSequence, serialNumber)),
+ 0,
+ TOC0_SMALL_SEQ(0),
+ TOC0_SMALL_SEQ(0),
+ TOC0_SMALL_SEQ(0),
+ TOC0_SMALL_SEQ(0),
+ TOC0_LARGE_SEQ(sizeof(struct toc0_subjectPublicKeyInfo)),
+ {
+ TOC0_SMALL_SEQ(0),
+ TOC0_LARGE_SEQ(sizeof(struct toc0_publicKey)),
+ {
+ TOC0_LARGE_INT(sizeof_field(struct toc0_publicKey, n)),
+ {},
+ TOC0_SMALL_INT(sizeof_field(struct toc0_publicKey, e)),
+ {},
+ },
+ },
+ TOC0_SMALL_EXP(3, sizeof(struct toc0_explicit3)),
+ {
+ TOC0_SMALL_SEQ(sizeof(struct toc0_extension)),
+ {
+ TOC0_SMALL_INT(sizeof_field(struct toc0_extension, digest)),
+ {},
+ },
+ },
+ },
+ TOC0_LARGE_BIT(sizeof(struct toc0_sigSequence)),
+ {
+ TOC0_SMALL_SEQ(0),
+ TOC0_LARGE_BIT(sizeof_field(struct toc0_sigSequence, signature)),
+ {},
+ },
+ },
+};
+
+#define TOC0_DEFAULT_NUM_ITEMS 3
+#define TOC0_DEFAULT_HEADER_LEN \
+ ALIGN( \
+ sizeof(struct toc0_main_info) + \
+ sizeof(struct toc0_item_info) * TOC0_DEFAULT_NUM_ITEMS + \
+ sizeof(struct toc0_cert_item) + \
+ sizeof(struct toc0_key_item), \
+ 32)
+
+static char *fw_key_file = "fw_key.pem";
+static char *key_item_file = "key_item.bin";
+static char *root_key_file = "root_key.pem";
+
+/*
+ * Create a key item in @buf, containing the public keys @root_key and @fw_key,
+ * and signed by the RSA key @root_key.
+ */
+static int toc0_create_key_item(uint8_t *buf, uint32_t *len,
+ RSA *root_key, RSA *fw_key)
+{
+ struct toc0_key_item *key_item = (void *)buf;
+ uint8_t digest[SHA256_DIGEST_LENGTH];
+ int ret = EXIT_FAILURE;
+ unsigned int sig_len;
+ int n_len, e_len;
+
+ /* Store key 0. */
+ n_len = BN_bn2bin(RSA_get0_n(root_key), key_item->key0);
+ e_len = BN_bn2bin(RSA_get0_e(root_key), key_item->key0 + n_len);
+ if (n_len + e_len > sizeof(key_item->key0)) {
+ pr_err("Root key is too big for key item\n");
+ goto err;
+ }
+ key_item->key0_n_len = cpu_to_le32(n_len);
+ key_item->key0_e_len = cpu_to_le32(e_len);
+
+ /* Store key 1. */
+ n_len = BN_bn2bin(RSA_get0_n(fw_key), key_item->key1);
+ e_len = BN_bn2bin(RSA_get0_e(fw_key), key_item->key1 + n_len);
+ if (n_len + e_len > sizeof(key_item->key1)) {
+ pr_err("Firmware key is too big for key item\n");
+ goto err;
+ }
+ key_item->key1_n_len = cpu_to_le32(n_len);
+ key_item->key1_e_len = cpu_to_le32(e_len);
+
+ /* Sign the key item. */
+ key_item->sig_len = cpu_to_le32(RSA_size(root_key));
+ SHA256(buf, key_item->sig - buf, digest);
+ if (!RSA_sign(NID_sha256, digest, sizeof(digest),
+ key_item->sig, &sig_len, root_key)) {
+ pr_err("Failed to sign key item\n");
+ goto err;
+ }
+ if (sig_len != sizeof(key_item->sig)) {
+ pr_err("Bad key item signature length\n");
+ goto err;
+ }
+
+ *len = sizeof(*key_item);
+ ret = EXIT_SUCCESS;
+
+err:
+ return ret;
+}
+
+/*
+ * Verify the key item in @buf, containing two public keys @key0 and @key1,
+ * and signed by the RSA key @key0. If @root_key is provided, only signatures
+ * by that key will be accepted. @key1 is returned in @key.
+ */
+static int toc0_verify_key_item(const uint8_t *buf, uint32_t len,
+ RSA *root_key, RSA **fw_key)
+{
+ struct toc0_key_item *key_item = (void *)buf;
+ uint8_t digest[SHA256_DIGEST_LENGTH];
+ int ret = EXIT_FAILURE;
+ int n_len, e_len;
+ RSA *key0 = NULL;
+ RSA *key1 = NULL;
+ BIGNUM *n, *e;
+
+ if (len < sizeof(*key_item))
+ goto err;
+
+ /* Load key 0. */
+ n_len = le32_to_cpu(key_item->key0_n_len);
+ e_len = le32_to_cpu(key_item->key0_e_len);
+ if (n_len + e_len > sizeof(key_item->key0)) {
+ pr_err("Bad root key size in key item\n");
+ goto err;
+ }
+ n = BN_bin2bn(key_item->key0, n_len, NULL);
+ e = BN_bin2bn(key_item->key0 + n_len, e_len, NULL);
+ key0 = RSA_new();
+ if (!key0)
+ goto err;
+ if (!RSA_set0_key(key0, n, e, NULL))
+ goto err;
+
+ /* If a root key was provided, compare it to key 0. */
+ if (root_key && (BN_cmp(n, RSA_get0_n(root_key)) ||
+ BN_cmp(e, RSA_get0_e(root_key)))) {
+ pr_err("Wrong root key in key item\n");
+ goto err;
+ }
+
+ /* Verify the key item signature. */
+ SHA256(buf, key_item->sig - buf, digest);
+ if (!RSA_verify(NID_sha256, digest, sizeof(digest),
+ key_item->sig, le32_to_cpu(key_item->sig_len), key0)) {
+ pr_err("Bad key item signature\n");
+ goto err;
+ }
+
+ if (fw_key) {
+ /* Load key 1. */
+ n_len = le32_to_cpu(key_item->key1_n_len);
+ e_len = le32_to_cpu(key_item->key1_e_len);
+ if (n_len + e_len > sizeof(key_item->key1)) {
+ pr_err("Bad firmware key size in key item\n");
+ goto err;
+ }
+ n = BN_bin2bn(key_item->key1, n_len, NULL);
+ e = BN_bin2bn(key_item->key1 + n_len, e_len, NULL);
+ key1 = RSA_new();
+ if (!key1)
+ goto err;
+ if (!RSA_set0_key(key1, n, e, NULL))
+ goto err;
+
+ if (*fw_key) {
+ /* If a FW key was provided, compare it to key 1. */
+ if (BN_cmp(n, RSA_get0_n(*fw_key)) ||
+ BN_cmp(e, RSA_get0_e(*fw_key))) {
+ pr_err("Wrong firmware key in key item\n");
+ goto err;
+ }
+ } else {
+ /* Otherwise, send key1 back to the caller. */
+ *fw_key = key1;
+ key1 = NULL;
+ }
+ }
+
+ ret = EXIT_SUCCESS;
+
+err:
+ RSA_free(key0);
+ RSA_free(key1);
+
+ return ret;
+}
+
+/*
+ * Create a certificate in @buf, describing the firmware with SHA256 digest
+ * @digest, and signed by the RSA key @fw_key.
+ */
+static int toc0_create_cert_item(uint8_t *buf, uint32_t *len, RSA *fw_key,
+ uint8_t digest[static SHA256_DIGEST_LENGTH])
+{
+ struct toc0_cert_item *cert_item = (void *)buf;
+ uint8_t cert_digest[SHA256_DIGEST_LENGTH];
+ struct toc0_totalSequence *totalSequence;
+ struct toc0_sigSequence *sigSequence;
+ struct toc0_extension *extension;
+ struct toc0_publicKey *publicKey;
+ int ret = EXIT_FAILURE;
+ unsigned int sig_len;
+
+ memcpy(cert_item, &cert_item_template, sizeof(*cert_item));
+ *len = sizeof(*cert_item);
+
+ /*
+ * Fill in the public key.
+ *
+ * Only 2048-bit RSA keys are supported. Since this uses a fixed-size
+ * structure, it may fail for non-standard exponents.
+ */
+ totalSequence = &cert_item->totalSequence;
+ publicKey = &totalSequence->mainSequence.subjectPublicKeyInfo.publicKey;
+ if (BN_bn2binpad(RSA_get0_n(fw_key), publicKey->n, sizeof(publicKey->n)) < 0 ||
+ BN_bn2binpad(RSA_get0_e(fw_key), publicKey->e, sizeof(publicKey->e)) < 0) {
+ pr_err("Firmware key is too big for certificate\n");
+ goto err;
+ }
+
+ /* Fill in the firmware digest. */
+ extension = &totalSequence->mainSequence.explicit3.extension;
+ memcpy(&extension->digest, digest, SHA256_DIGEST_LENGTH);
+
+ /*
+ * Sign the certificate.
+ *
+ * In older SBROM versions (and by default in newer versions),
+ * the last 4 bytes of the certificate are not signed.
+ *
+ * (The buffer passed to SHA256 starts at tag_mainSequence, but
+ * the buffer size does not include the length of that tag.)
+ */
+ SHA256((uint8_t *)totalSequence, sizeof(struct toc0_mainSequence), cert_digest);
+ sigSequence = &totalSequence->sigSequence;
+ if (!RSA_sign(NID_sha256, cert_digest, SHA256_DIGEST_LENGTH,
+ sigSequence->signature, &sig_len, fw_key)) {
+ pr_err("Failed to sign certificate\n");
+ goto err;
+ }
+ if (sig_len != sizeof(sigSequence->signature)) {
+ pr_err("Bad certificate signature length\n");
+ goto err;
+ }
+
+ ret = EXIT_SUCCESS;
+
+err:
+ return ret;
+}
+
+/*
+ * Verify the certificate in @buf, describing the firmware with SHA256 digest
+ * @digest, and signed by the RSA key contained within. If @fw_key is provided,
+ * only that key will be accepted.
+ *
+ * This function is only expected to work with images created by mkimage.
+ */
+static int toc0_verify_cert_item(const uint8_t *buf, uint32_t len, RSA *fw_key,
+ uint8_t digest[static SHA256_DIGEST_LENGTH])
+{
+ const struct toc0_cert_item *cert_item = (const void *)buf;
+ uint8_t cert_digest[SHA256_DIGEST_LENGTH];
+ const struct toc0_totalSequence *totalSequence;
+ const struct toc0_sigSequence *sigSequence;
+ const struct toc0_extension *extension;
+ const struct toc0_publicKey *publicKey;
+ int ret = EXIT_FAILURE;
+ RSA *key = NULL;
+ BIGNUM *n, *e;
+
+ /* Extract the public key from the certificate. */
+ totalSequence = &cert_item->totalSequence;
+ publicKey = &totalSequence->mainSequence.subjectPublicKeyInfo.publicKey;
+ n = BN_bin2bn(publicKey->n, sizeof(publicKey->n), NULL);
+ e = BN_bin2bn(publicKey->e, sizeof(publicKey->e), NULL);
+ key = RSA_new();
+ if (!key)
+ goto err;
+ if (!RSA_set0_key(key, n, e, NULL))
+ goto err;
+
+ /* If a key was provided, compare it to the embedded key. */
+ if (fw_key && (BN_cmp(RSA_get0_n(key), RSA_get0_n(fw_key)) ||
+ BN_cmp(RSA_get0_e(key), RSA_get0_e(fw_key)))) {
+ pr_err("Wrong firmware key in certificate\n");
+ goto err;
+ }
+
+ /* If a digest was provided, compare it to the embedded digest. */
+ extension = &totalSequence->mainSequence.explicit3.extension;
+ if (digest && memcmp(&extension->digest, digest, SHA256_DIGEST_LENGTH)) {
+ pr_err("Wrong firmware digest in certificate\n");
+ goto err;
+ }
+
+ /* Verify the certificate's signature. See the comment above. */
+ SHA256((uint8_t *)totalSequence, sizeof(struct toc0_mainSequence), cert_digest);
+ sigSequence = &totalSequence->sigSequence;
+ if (!RSA_verify(NID_sha256, cert_digest, SHA256_DIGEST_LENGTH,
+ sigSequence->signature,
+ sizeof(sigSequence->signature), key)) {
+ pr_err("Bad certificate signature\n");
+ goto err;
+ }
+
+ ret = EXIT_SUCCESS;
+
+err:
+ RSA_free(key);
+
+ return ret;
+}
+
+/*
+ * Always create a TOC0 containing 3 items. The extra item will be ignored on
+ * SoCs which do not support it.
+ */
+static int toc0_create(uint8_t *buf, uint32_t len, RSA *root_key, RSA *fw_key,
+ uint8_t *key_item, uint32_t key_item_len,
+ uint8_t *fw_item, uint32_t fw_item_len, uint32_t fw_addr)
+{
+ struct toc0_main_info *main_info = (void *)buf;
+ struct toc0_item_info *item_info = (void *)(main_info + 1);
+ uint8_t digest[SHA256_DIGEST_LENGTH];
+ uint32_t *buf32 = (void *)buf;
+ RSA *orig_fw_key = fw_key;
+ int ret = EXIT_FAILURE;
+ uint32_t checksum = 0;
+ uint32_t item_offset;
+ uint32_t item_length;
+ int i;
+
+ /* Hash the firmware for inclusion in the certificate. */
+ SHA256(fw_item, fw_item_len, digest);
+
+ /* Create the main TOC0 header, containing three items. */
+ memcpy(main_info->name, TOC0_MAIN_INFO_NAME, sizeof(main_info->name));
+ main_info->magic = cpu_to_le32(TOC0_MAIN_INFO_MAGIC);
+ main_info->checksum = cpu_to_le32(BROM_STAMP_VALUE);
+ main_info->num_items = cpu_to_le32(TOC0_DEFAULT_NUM_ITEMS);
+ memcpy(main_info->end, TOC0_MAIN_INFO_END, sizeof(main_info->end));
+
+ /* The first item links the ROTPK to the signing key. */
+ item_offset = sizeof(*main_info) +
+ sizeof(*item_info) * TOC0_DEFAULT_NUM_ITEMS;
+ /* Using an existing key item avoids needing the root private key. */
+ if (key_item) {
+ item_length = sizeof(*key_item);
+ if (toc0_verify_key_item(key_item, item_length,
+ root_key, &fw_key))
+ goto err;
+ memcpy(buf + item_offset, key_item, item_length);
+ } else if (toc0_create_key_item(buf + item_offset, &item_length,
+ root_key, fw_key)) {
+ goto err;
+ }
+
+ item_info->name = cpu_to_le32(TOC0_ITEM_INFO_NAME_KEY);
+ item_info->offset = cpu_to_le32(item_offset);
+ item_info->length = cpu_to_le32(item_length);
+ memcpy(item_info->end, TOC0_ITEM_INFO_END, sizeof(item_info->end));
+
+ /* The second item contains a certificate signed by the firmware key. */
+ item_offset = item_offset + item_length;
+ if (toc0_create_cert_item(buf + item_offset, &item_length,
+ fw_key, digest))
+ goto err;
+
+ item_info++;
+ item_info->name = cpu_to_le32(TOC0_ITEM_INFO_NAME_CERT);
+ item_info->offset = cpu_to_le32(item_offset);
+ item_info->length = cpu_to_le32(item_length);
+ memcpy(item_info->end, TOC0_ITEM_INFO_END, sizeof(item_info->end));
+
+ /* The third item contains the actual boot code. */
+ item_offset = ALIGN(item_offset + item_length, 32);
+ item_length = fw_item_len;
+ if (buf + item_offset != fw_item)
+ memmove(buf + item_offset, fw_item, item_length);
+
+ item_info++;
+ item_info->name = cpu_to_le32(TOC0_ITEM_INFO_NAME_FIRMWARE);
+ item_info->offset = cpu_to_le32(item_offset);
+ item_info->length = cpu_to_le32(item_length);
+ item_info->load_addr = cpu_to_le32(fw_addr);
+ memcpy(item_info->end, TOC0_ITEM_INFO_END, sizeof(item_info->end));
+
+ /* Pad to the required block size with 0xff to be flash-friendly. */
+ item_offset = item_offset + item_length;
+ item_length = ALIGN(item_offset, PAD_SIZE) - item_offset;
+ memset(buf + item_offset, 0xff, item_length);
+
+ /* Fill in the total padded file length. */
+ item_offset = item_offset + item_length;
+ main_info->length = cpu_to_le32(item_offset);
+
+ /* Verify enough space was provided when creating the image. */
+ assert(len >= item_offset);
+
+ /* Calculate the checksum. Yes, it's that simple. */
+ for (i = 0; i < item_offset / 4; ++i)
+ checksum += le32_to_cpu(buf32[i]);
+ main_info->checksum = cpu_to_le32(checksum);
+
+ ret = EXIT_SUCCESS;
+
+err:
+ if (fw_key != orig_fw_key)
+ RSA_free(fw_key);
+
+ return ret;
+}
+
+static const struct toc0_item_info *
+toc0_find_item(const struct toc0_main_info *main_info, uint32_t name,
+ uint32_t *offset, uint32_t *length)
+{
+ const struct toc0_item_info *item_info = (void *)(main_info + 1);
+ uint32_t item_offset, item_length;
+ uint32_t num_items, main_length;
+ int i;
+
+ num_items = le32_to_cpu(main_info->num_items);
+ main_length = le32_to_cpu(main_info->length);
+
+ for (i = 0; i < num_items; ++i, ++item_info) {
+ if (le32_to_cpu(item_info->name) != name)
+ continue;
+
+ item_offset = le32_to_cpu(item_info->offset);
+ item_length = le32_to_cpu(item_info->length);
+
+ if (item_offset > main_length ||
+ item_length > main_length - item_offset)
+ continue;
+
+ *offset = item_offset;
+ *length = item_length;
+
+ return item_info;
+ }
+
+ return NULL;
+}
+
+static int toc0_verify(const uint8_t *buf, uint32_t len, RSA *root_key)
+{
+ const struct toc0_main_info *main_info = (void *)buf;
+ const struct toc0_item_info *item_info;
+ uint8_t digest[SHA256_DIGEST_LENGTH];
+ uint32_t main_length = le32_to_cpu(main_info->length);
+ uint32_t checksum = BROM_STAMP_VALUE;
+ uint32_t *buf32 = (void *)buf;
+ uint32_t length, offset;
+ int ret = EXIT_FAILURE;
+ RSA *fw_key = NULL;
+ int i;
+
+ if (len < main_length)
+ goto err;
+
+ /* Verify the main header. */
+ if (memcmp(main_info->name, TOC0_MAIN_INFO_NAME, sizeof(main_info->name)))
+ goto err;
+ if (le32_to_cpu(main_info->magic) != TOC0_MAIN_INFO_MAGIC)
+ goto err;
+ /* Verify the checksum without modifying the buffer. */
+ for (i = 0; i < main_length / 4; ++i)
+ checksum += le32_to_cpu(buf32[i]);
+ if (checksum != 2 * le32_to_cpu(main_info->checksum))
+ goto err;
+ /* The length must be at least 512 byte aligned. */
+ if (main_length % 512)
+ goto err;
+ if (memcmp(main_info->end, TOC0_MAIN_INFO_END, sizeof(main_info->end)))
+ goto err;
+
+ /* Verify the key item if present (it is optional). */
+ item_info = toc0_find_item(main_info, TOC0_ITEM_INFO_NAME_KEY,
+ &offset, &length);
+ if (!item_info)
+ fw_key = root_key;
+ else if (toc0_verify_key_item(buf + offset, length, root_key, &fw_key))
+ goto err;
+
+ /* Hash the firmware to compare with the certificate. */
+ item_info = toc0_find_item(main_info, TOC0_ITEM_INFO_NAME_FIRMWARE,
+ &offset, &length);
+ if (!item_info) {
+ pr_err("Missing firmware item\n");
+ goto err;
+ }
+ SHA256(buf + offset, length, digest);
+
+ /* Verify the certificate item. */
+ item_info = toc0_find_item(main_info, TOC0_ITEM_INFO_NAME_CERT,
+ &offset, &length);
+ if (!item_info) {
+ pr_err("Missing certificate item\n");
+ goto err;
+ }
+ if (toc0_verify_cert_item(buf + offset, length, fw_key, digest))
+ goto err;
+
+ ret = EXIT_SUCCESS;
+
+err:
+ if (fw_key != root_key)
+ RSA_free(fw_key);
+
+ return ret;
+}
+
+static int toc0_check_params(struct image_tool_params *params)
+{
+ if (!params->dflag)
+ return -EINVAL;
+
+ /*
+ * If a key directory was provided, look for key files there.
+ * Otherwise, look for them in the current directory. The key files are
+ * the "quoted" terms in the description below.
+ *
+ * A summary of the chain of trust on most SoCs:
+ * 1) eFuse contains a SHA256 digest of the public "root key".
+ * 2) Private "root key" signs the certificate item (generated here).
+ * 3) Certificate item contains a SHA256 digest of the firmware item.
+ *
+ * A summary of the chain of trust on the H6 (by default; a bit in the
+ * BROM_CONFIG eFuse makes it work like above):
+ * 1) eFuse contains a SHA256 digest of the public "root key".
+ * 2) Private "root key" signs the "key item" (generated here).
+ * 3) "Key item" contains the public "root key" and public "fw key".
+ * 4) Private "fw key" signs the certificate item (generated here).
+ * 5) Certificate item contains a SHA256 digest of the firmware item.
+ *
+ * This means there are three valid ways to generate a TOC0:
+ * 1) Provide the private "root key" only. This works everywhere.
+ * For H6, the "root key" will also be used as the "fw key".
+ * 2) FOR H6 ONLY: Provide the private "root key" and a separate
+ * private "fw key".
+ * 3) FOR H6 ONLY: Provide the private "fw key" and a pre-existing
+ * "key item" containing the corresponding public "fw key".
+ * In this case, the private "root key" can be kept offline. The
+ * "key item" can be extracted from a TOC0 image generated using
+ * method #2 above.
+ *
+ * Note that until the ROTPK_HASH eFuse is programmed, any "root key"
+ * will be accepted by the BROM.
+ */
+ if (params->keydir) {
+ if (asprintf(&fw_key_file, "%s/%s", params->keydir, fw_key_file) < 0)
+ return -ENOMEM;
+ if (asprintf(&key_item_file, "%s/%s", params->keydir, key_item_file) < 0)
+ return -ENOMEM;
+ if (asprintf(&root_key_file, "%s/%s", params->keydir, root_key_file) < 0)
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int toc0_verify_header(unsigned char *buf, int image_size,
+ struct image_tool_params *params)
+{
+ int ret = EXIT_FAILURE;
+ RSA *root_key = NULL;
+ FILE *fp;
+
+ /* A root public key is optional. */
+ fp = fopen(root_key_file, "rb");
+ if (fp) {
+ pr_info("Verifying image with existing root key\n");
+ root_key = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
+ if (!root_key)
+ root_key = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL);
+ fclose(fp);
+ if (!root_key) {
+ pr_err("Failed to read public key from '%s'\n",
+ root_key_file);
+ goto err;
+ }
+ }
+
+ ret = toc0_verify(buf, image_size, root_key);
+
+err:
+ RSA_free(root_key);
+
+ return ret;
+}
+
+static const char *toc0_item_name(uint32_t name)
+{
+ if (name == TOC0_ITEM_INFO_NAME_CERT)
+ return "Certificate";
+ if (name == TOC0_ITEM_INFO_NAME_FIRMWARE)
+ return "Firmware";
+ if (name == TOC0_ITEM_INFO_NAME_KEY)
+ return "Key";
+ return "(unknown)";
+}
+
+static void toc0_print_header(const void *buf)
+{
+ const struct toc0_main_info *main_info = buf;
+ const struct toc0_item_info *item_info = (void *)(main_info + 1);
+ uint32_t head_length, main_length, num_items;
+ uint32_t item_offset, item_length, item_name;
+ int load_addr = -1;
+ int i;
+
+ num_items = le32_to_cpu(main_info->num_items);
+ head_length = sizeof(*main_info) + num_items * sizeof(*item_info);
+ main_length = le32_to_cpu(main_info->length);
+
+ printf("Allwinner TOC0 Image\n"
+ "Size: %d bytes\n"
+ "Contents: %d items\n"
+ " 00000000:%08x Headers\n",
+ main_length, num_items, head_length);
+
+ for (i = 0; i < num_items; ++i, ++item_info) {
+ item_offset = le32_to_cpu(item_info->offset);
+ item_length = le32_to_cpu(item_info->length);
+ item_name = le32_to_cpu(item_info->name);
+
+ if (item_name == TOC0_ITEM_INFO_NAME_FIRMWARE)
+ load_addr = le32_to_cpu(item_info->load_addr);
+
+ printf(" %08x:%08x %s\n",
+ item_offset, item_length,
+ toc0_item_name(item_name));
+ }
+
+ if (num_items && item_offset + item_length < main_length) {
+ item_offset = item_offset + item_length;
+ item_length = main_length - item_offset;
+
+ printf(" %08x:%08x Padding\n",
+ item_offset, item_length);
+ }
+
+ if (load_addr != -1)
+ printf("Load address: 0x%08x\n", load_addr);
+}
+
+static void toc0_set_header(void *buf, struct stat *sbuf, int ifd,
+ struct image_tool_params *params)
+{
+ uint32_t key_item_len = 0;
+ uint8_t *key_item = NULL;
+ int ret = EXIT_FAILURE;
+ RSA *root_key = NULL;
+ RSA *fw_key = NULL;
+ FILE *fp;
+
+ /* Either a key item or the root private key is required. */
+ fp = fopen(key_item_file, "rb");
+ if (fp) {
+ pr_info("Creating image using existing key item\n");
+ key_item_len = sizeof(struct toc0_key_item);
+ key_item = OPENSSL_malloc(key_item_len);
+ if (!key_item || fread(key_item, key_item_len, 1, fp) != 1) {
+ pr_err("Failed to read key item from '%s'\n",
+ root_key_file);
+ goto err;
+ }
+ fclose(fp);
+ fp = NULL;
+ }
+
+ fp = fopen(root_key_file, "rb");
+ if (fp) {
+ root_key = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
+ if (!root_key)
+ root_key = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL);
+ fclose(fp);
+ fp = NULL;
+ }
+
+ /* When using an existing key item, the root key is optional. */
+ if (!key_item && (!root_key || !RSA_get0_d(root_key))) {
+ pr_err("Failed to read private key from '%s'\n",
+ root_key_file);
+ pr_info("Try 'openssl genrsa -out root_key.pem'\n");
+ goto err;
+ }
+
+ /* The certificate/firmware private key is always required. */
+ fp = fopen(fw_key_file, "rb");
+ if (fp) {
+ fw_key = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
+ fclose(fp);
+ fp = NULL;
+ }
+ if (!fw_key) {
+ /* If the root key is a private key, it can be used instead. */
+ if (root_key && RSA_get0_d(root_key)) {
+ pr_info("Using root key as firmware key\n");
+ fw_key = root_key;
+ } else {
+ pr_err("Failed to read private key from '%s'\n",
+ fw_key_file);
+ goto err;
+ }
+ }
+
+ /* Warn about potential compatibility issues. */
+ if (key_item || fw_key != root_key)
+ pr_warn("Only H6 supports separate root and firmware keys\n");
+
+ ret = toc0_create(buf, params->file_size, root_key, fw_key,
+ key_item, key_item_len,
+ buf + TOC0_DEFAULT_HEADER_LEN,
+ params->orig_file_size, params->addr);
+
+err:
+ OPENSSL_free(key_item);
+ OPENSSL_free(root_key);
+ if (fw_key != root_key)
+ OPENSSL_free(fw_key);
+ if (fp)
+ fclose(fp);
+
+ if (ret != EXIT_SUCCESS)
+ exit(ret);
+}
+
+static int toc0_check_image_type(uint8_t type)
+{
+ return type == IH_TYPE_SUNXI_TOC0 ? 0 : 1;
+}
+
+static int toc0_vrec_header(struct image_tool_params *params,
+ struct image_type_params *tparams)
+{
+ tparams->hdr = calloc(tparams->header_size, 1);
+
+ /* Save off the unpadded data size for SHA256 calculation. */
+ params->orig_file_size = params->file_size - TOC0_DEFAULT_HEADER_LEN;
+
+ /* Return padding to 8K blocks. */
+ return ALIGN(params->file_size, PAD_SIZE) - params->file_size;
+}
+
+U_BOOT_IMAGE_TYPE(
+ sunxi_toc0,
+ "Allwinner TOC0 Boot Image support",
+ TOC0_DEFAULT_HEADER_LEN,
+ NULL,
+ toc0_check_params,
+ toc0_verify_header,
+ toc0_print_header,
+ toc0_set_header,
+ NULL,
+ toc0_check_image_type,
+ NULL,
+ toc0_vrec_header
+);