diff --git a/Kconfig b/Kconfig index 520679f57e..990353800f 100644 --- a/Kconfig +++ b/Kconfig @@ -200,7 +200,7 @@ config SYS_MALLOC_F_LEN default 0x2000 if (ARCH_IMX8 || ARCH_IMX8M || ARCH_MX7 || \ ARCH_MX7ULP || ARCH_MX6 || ARCH_MX5 || \ ARCH_LS1012A || ARCH_LS1021A || ARCH_LS1043A || \ - ARCH_LS1046A || ARCH_QEMU) + ARCH_LS1046A || ARCH_QEMU || ARCH_SUNXI) default 0x400 help Before relocation, memory is very limited on many platforms. Still, diff --git a/Makefile b/Makefile index cb28cf6aa0..e08cd751e0 100644 --- a/Makefile +++ b/Makefile @@ -1332,6 +1332,7 @@ cmd_binman = $(srctree)/tools/binman/binman $(if $(BINMAN_DEBUG),-D) \ -I arch/$(ARCH)/dts -a of-list=$(CONFIG_OF_LIST) \ -a atf-bl31-path=${BL31} \ -a default-dt=$(default_dt) \ + -a scp-path=$(SCP) \ $(BINMAN_$(@F)) OBJCOPYFLAGS_u-boot.ldr.hex := -I binary -O ihex @@ -1441,11 +1442,13 @@ else MKIMAGEFLAGS_u-boot.itb = -E endif +ifdef U_BOOT_ITS u-boot.itb: u-boot-nodtb.bin \ $(if $(CONFIG_OF_SEPARATE)$(CONFIG_OF_EMBED)$(CONFIG_OF_HOSTFILE),dts/dt.dtb) \ $(U_BOOT_ITS) FORCE $(call if_changed,mkfitimage) $(BOARD_SIZE_CHECK) +endif u-boot-spl.kwb: u-boot.img spl/u-boot-spl.bin FORCE $(call if_changed,mkimage) diff --git a/arch/arm/dts/sunxi-u-boot.dtsi b/arch/arm/dts/sunxi-u-boot.dtsi index c97943b3c1..c77cf7cacf 100644 --- a/arch/arm/dts/sunxi-u-boot.dtsi +++ b/arch/arm/dts/sunxi-u-boot.dtsi @@ -1,5 +1,13 @@ #include +#ifdef CONFIG_MACH_SUN50I_H6 +#define BL31_ADDR 0x104000 +#define SCP_ADDR 0x114000 +#else +#define BL31_ADDR 0x44000 +#define SCP_ADDR 0x50000 +#endif + / { aliases { mmc1 = &mmc2; @@ -14,9 +22,11 @@ u-boot-sunxi-with-spl { filename = "u-boot-sunxi-with-spl.bin"; pad-byte = <0xff>; + blob { filename = "spl/sunxi-spl.bin"; }; + #ifdef CONFIG_ARM64 fit { description = "Configuration to load ATF before U-Boot"; @@ -27,6 +37,7 @@ uboot { description = "U-Boot (64-bit)"; type = "standalone"; + os = "u-boot"; arch = "arm64"; compression = "none"; load = <0x4a000000>; @@ -34,24 +45,35 @@ u-boot-nodtb { }; }; + atf { description = "ARM Trusted Firmware"; type = "firmware"; + os = "arm-trusted-firmware"; arch = "arm64"; compression = "none"; -/* TODO: Do this with an overwrite in this board's dtb? */ -#ifdef CONFIG_MACH_SUN50I_H6 - load = <0x104000>; - entry = <0x104000>; -#else - load = <0x44000>; - entry = <0x44000>; -#endif + load = ; + entry = ; + atf-bl31 { + filename = "bl31.bin"; missing-msg = "atf-bl31-sunxi"; }; }; + scp { + description = "SCP firmware"; + type = "firmware"; + arch = "or1k"; + compression = "none"; + load = ; + + scp { + filename = "scp.bin"; + missing-msg = "scp-sunxi"; + }; + }; + @fdt-SEQ { description = "NAME"; type = "flat_dt"; @@ -61,10 +83,11 @@ configurations { default = "config-1"; + @config-SEQ { description = "NAME"; - firmware = "uboot"; - loadables = "atf"; + firmware = "atf"; + loadables = "scp", "uboot"; fdt = "fdt-SEQ"; }; }; diff --git a/arch/arm/include/asm/arch-sunxi/cpu.h b/arch/arm/include/asm/arch-sunxi/cpu.h index 4c399b0a15..8b57d24e2f 100644 --- a/arch/arm/include/asm/arch-sunxi/cpu.h +++ b/arch/arm/include/asm/arch-sunxi/cpu.h @@ -16,6 +16,7 @@ #define SOCID_A64 0x1689 #define SOCID_H3 0x1680 +#define SOCID_V3S 0x1681 #define SOCID_H5 0x1718 #define SOCID_R40 0x1701 diff --git a/arch/arm/mach-sunxi/dram_sunxi_dw.c b/arch/arm/mach-sunxi/dram_sunxi_dw.c index a462538521..d0600011ff 100644 --- a/arch/arm/mach-sunxi/dram_sunxi_dw.c +++ b/arch/arm/mach-sunxi/dram_sunxi_dw.c @@ -63,6 +63,8 @@ enum { MBUS_PORT_CSI = 5, MBUS_PORT_NAND = 6, MBUS_PORT_SS = 7, + MBUS_PORT_DE_V3S = 8, + MBUS_PORT_DE_CFD_V3S = 9, MBUS_PORT_TS = 8, MBUS_PORT_DI = 9, MBUS_PORT_DE = 10, @@ -134,6 +136,29 @@ static void mctl_set_master_priority_h3(void) MBUS_CONF(DE_CFD, true, HIGH, 0, 1024, 288, 64); } +static void mctl_set_master_priority_v3s(void) +{ + struct sunxi_mctl_com_reg * const mctl_com = + (struct sunxi_mctl_com_reg *)SUNXI_DRAM_COM_BASE; + + /* enable bandwidth limit windows and set windows size 1us */ + writel((1 << 16) | (400 << 0), &mctl_com->bwcr); + + /* set cpu high priority */ + writel(0x00000001, &mctl_com->mapr); + + MBUS_CONF( CPU, true, HIGHEST, 0, 160, 100, 80); + MBUS_CONF( GPU, true, HIGH, 0, 1792, 1536, 0); + MBUS_CONF( UNUSED, true, HIGHEST, 0, 256, 128, 80); + MBUS_CONF( DMA, true, HIGH, 0, 256, 100, 0); + MBUS_CONF( VE, true, HIGH, 0, 2048, 1600, 0); + MBUS_CONF( CSI, true, HIGHEST, 0, 384, 256, 0); + MBUS_CONF( NAND, true, HIGH, 0, 100, 50, 0); + MBUS_CONF( SS, true, HIGH, 0, 384, 256, 0); + MBUS_CONF( DE_V3S, false, HIGH, 0, 8192, 4096, 0); + MBUS_CONF(DE_CFD_V3S, true, HIGH, 0, 640, 256, 0); +} + static void mctl_set_master_priority_a64(void) { struct sunxi_mctl_com_reg * const mctl_com = @@ -231,6 +256,9 @@ static void mctl_set_master_priority(uint16_t socid) case SOCID_H3: mctl_set_master_priority_h3(); return; + case SOCID_V3S: + mctl_set_master_priority_v3s(); + return; case SOCID_A64: mctl_set_master_priority_a64(); return; @@ -334,6 +362,28 @@ static void mctl_h3_zq_calibration_quirk(struct dram_para *para) } } +static void mctl_v3s_zq_calibration_quirk(struct dram_para *para) +{ + struct sunxi_mctl_ctl_reg * const mctl_ctl = + (struct sunxi_mctl_ctl_reg *)SUNXI_DRAM_CTL0_BASE; + + u32 reg_val; + + clrsetbits_le32(&mctl_ctl->zqcr, 0xffffff, + CONFIG_DRAM_ZQ & 0xffffff); + mctl_phy_init(PIR_ZCAL); + + reg_val = readl(&mctl_ctl->zqdr[0]); + reg_val &= (0x1f << 16) | (0x1f << 0); + reg_val |= reg_val << 8; + writel(reg_val, &mctl_ctl->zqdr[0]); + + reg_val = readl(&mctl_ctl->zqdr[1]); + reg_val &= (0x1f << 16) | (0x1f << 0); + reg_val |= reg_val << 8; + writel(reg_val, &mctl_ctl->zqdr[1]); +} + static void mctl_set_cr(uint16_t socid, struct dram_para *para) { struct sunxi_mctl_com_reg * const mctl_com = @@ -391,7 +441,7 @@ static void mctl_sys_init(uint16_t socid, struct dram_para *para) CCM_DRAMCLK_CFG_DIV(1) | CCM_DRAMCLK_CFG_SRC_PLL11 | CCM_DRAMCLK_CFG_UPD); - } else if (socid == SOCID_H3 || socid == SOCID_H5) { + } else if (socid == SOCID_H3 || socid == SOCID_H5 || socid == SOCID_V3S) { clock_set_pll5(CONFIG_DRAM_CLK * 2 * 1000000, false); clrsetbits_le32(&ccm->dram_clk_cfg, CCM_DRAMCLK_CFG_DIV_MASK | @@ -474,6 +524,13 @@ static int mctl_channel_init(uint16_t socid, struct dram_para *para) /* dphy & aphy phase select 270 degree */ clrsetbits_le32(&mctl_ctl->pgcr[2], (0x3 << 10) | (0x3 << 8), (0x1 << 10) | (0x2 << 8)); + } else if (socid == SOCID_V3S) { + /* dx ddr_clk & hdr_clk dynamic mode */ + clrbits_le32(&mctl_ctl->pgcr[0], (0x3 << 14) | (0x3 << 12)); + + /* dphy & aphy phase select 270 degree */ + clrsetbits_le32(&mctl_ctl->pgcr[2], (0x3 << 10) | (0x3 << 8), + (0x1 << 10) | (0x1 << 8)); } else if (socid == SOCID_A64 || socid == SOCID_H5) { /* dphy & aphy phase select ? */ clrsetbits_le32(&mctl_ctl->pgcr[2], (0x3 << 10) | (0x3 << 8), @@ -506,7 +563,12 @@ static int mctl_channel_init(uint16_t socid, struct dram_para *para) mctl_set_bit_delays(para); udelay(50); - if (socid == SOCID_H3) { + if (socid == SOCID_V3S) { + mctl_v3s_zq_calibration_quirk(para); + + mctl_phy_init(PIR_PLLINIT | PIR_DCAL | PIR_PHYRST | + PIR_DRAMRST | PIR_DRAMINIT | PIR_QSGATE); + } else if (socid == SOCID_H3) { mctl_h3_zq_calibration_quirk(para); mctl_phy_init(PIR_PLLINIT | PIR_DCAL | PIR_PHYRST | @@ -570,7 +632,7 @@ static int mctl_channel_init(uint16_t socid, struct dram_para *para) udelay(10); /* set PGCR3, CKE polarity */ - if (socid == SOCID_H3) + if (socid == SOCID_H3 || socid == SOCID_V3S) writel(0x00aa0060, &mctl_ctl->pgcr[3]); else if (socid == SOCID_A64 || socid == SOCID_H5 || socid == SOCID_R40) writel(0xc0aa0060, &mctl_ctl->pgcr[3]); @@ -636,6 +698,22 @@ static void mctl_auto_detect_dram_size(uint16_t socid, struct dram_para *para) 0, 0, 0, 0, 0, 0, 0, 0, \ 0, 0, 0, 0, 0, 0, 0 } +#define SUN8I_V3S_DX_READ_DELAYS \ + {{ 8, 8, 8, 8, 8, 8, 8, 8, 8, 0, 0 }, \ + { 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0 }, \ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, \ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }} +#define SUN8I_V3S_DX_WRITE_DELAYS \ + {{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4 }, \ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2 }, \ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, \ + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }} +#define SUN8I_V3S_AC_DELAYS \ + { 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0 } + #define SUN8I_R40_DX_READ_DELAYS \ {{ 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0 }, \ { 14, 14, 14, 14, 14, 14, 14, 14, 14, 0, 0 }, \ @@ -702,6 +780,10 @@ unsigned long sunxi_dram_init(void) .dx_read_delays = SUN8I_H3_DX_READ_DELAYS, .dx_write_delays = SUN8I_H3_DX_WRITE_DELAYS, .ac_delays = SUN8I_H3_AC_DELAYS, +#elif defined(CONFIG_MACH_SUN8I_V3S) + .dx_read_delays = SUN8I_V3S_DX_READ_DELAYS, + .dx_write_delays = SUN8I_V3S_DX_WRITE_DELAYS, + .ac_delays = SUN8I_V3S_AC_DELAYS, #elif defined(CONFIG_MACH_SUN8I_R40) .dx_read_delays = SUN8I_R40_DX_READ_DELAYS, .dx_write_delays = SUN8I_R40_DX_WRITE_DELAYS, @@ -728,8 +810,7 @@ unsigned long sunxi_dram_init(void) /* Currently we cannot support R40 with dual rank memory */ para.dual_rank = 0; #elif defined(CONFIG_MACH_SUN8I_V3S) - /* TODO: set delays and mbus priority for V3s */ - uint16_t socid = SOCID_H3; + uint16_t socid = SOCID_V3S; #elif defined(CONFIG_MACH_SUN50I) uint16_t socid = SOCID_A64; #elif defined(CONFIG_MACH_SUN50I_H5) diff --git a/board/sunxi/README.sunxi64 b/board/sunxi/README.sunxi64 index 258921af22..4803bc9ff2 100644 --- a/board/sunxi/README.sunxi64 +++ b/board/sunxi/README.sunxi64 @@ -14,8 +14,12 @@ Quick Start / Overview - Build the ARM Trusted Firmware binary (see "ARM Trusted Firmware (ATF)" below) $ cd /src/arm-trusted-firmware $ make PLAT=sun50i_a64 DEBUG=1 bl31 +- Build the SCP firmware binary (see "SCP firmware (Crust)" below) + $ cd /src/crust + $ make pine64_plus_defconfig && make -j5 scp - Build U-Boot (see "SPL/U-Boot" below) $ export BL31=/path/to/bl31.bin + $ export SCP=/src/crust/build/scp/scp.bin $ make pine64_plus_defconfig && make -j5 - Transfer to an uSD card (see "microSD card" below) $ dd if=u-boot-sunxi-with-spl.bin of=/dev/sdx bs=8k seek=1 @@ -24,13 +28,17 @@ Quick Start / Overview Building the firmware ===================== -The Allwinner A64/H5 firmware consists of three parts: U-Boot's SPL, an -ARM Trusted Firmware (ATF) build and the U-Boot proper. -The SPL will load both ATF and U-Boot proper along with the right device -tree blob (.dtb) and will pass execution to ATF (in EL3), which in turn will -drop into the U-Boot proper (in EL2). -As the ATF binary will become part of the U-Boot image file, you will need -to build it first. +The Allwinner A64/H5/H6 firmware consists of several parts: U-Boot's SPL, +ARM Trusted Firmware (ATF), optional System Control Processor (SCP) firmware +(e.g. Crust), and the U-Boot proper. + +The SPL will load all of the other firmware binaries into RAM, along with the +right device tree blob (.dtb), and will pass execution to ATF (in EL3). If SCP +firmware was loaded, ATF will power on the SCP and wait for it to boot. +ATF will then drop into U-Boot proper (in EL2). + +As the ATF binary and SCP firmware will become part of the U-Boot image file, +you will need to build them first. ARM Trusted Firmware (ATF) ---------------------------- @@ -53,6 +61,31 @@ As sometimes the ATF build process is a bit picky about the toolchain used, or if you can't be bothered with building ATF, there are known working binaries in the firmware repository[3], purely for convenience reasons. + SCP firmware (Crust) +---------------------- +SCP firmware is responsible for implementing system suspend/resume, and (on +boards without a PMIC) soft poweroff/on. ATF contains fallback code for CPU +power control, so SCP firmware is optional if you don't need either of these +features. It runs on the AR100, with is an or1k CPU, not ARM, so it needs a +different cross toolchain. + +There is one SCP firmware implementation currently available, Crust: +$ git clone https://github.com/crust-firmware/crust +$ cd crust +$ export CROSS_COMPILE=or1k-linux-musl- +$ make pine64_plus_defconfig +$ make scp + +The same configuration generally works on any board with the same SoC (A64, H5, +or H6), so if there is no config for your board, use one for a similar board. + +Like for ATF, U-Boot finds the SCP firmware binary via an environment variable: +$ export SCP=/src/crust/build/scp/scp.bin + +If you do not want to use SCP firmware, you can silence the warning from binman +by pointing it to an empty file: +$ export SCP=/dev/null + SPL/U-Boot ------------ Both U-Boot proper and the SPL are using the 64-bit mode. As the boot ROM diff --git a/common/spl/Kconfig b/common/spl/Kconfig index af47f5ce1c..d8086bd9e8 100644 --- a/common/spl/Kconfig +++ b/common/spl/Kconfig @@ -465,9 +465,7 @@ config SPL_FIT_IMAGE_TINY Enable this to reduce the size of the FIT image loading code in SPL, if space for the SPL binary is very tight. - This removes the detection of image types (which forces the - first image to be treated as having a U-Boot style calling - convention) and skips the recording of each loaded payload + This skips the recording of each loaded payload (i.e. loadable) into the FDT (modifying the loaded FDT to ensure this information is available to the next image invoked). diff --git a/common/spl/spl_fit.c b/common/spl/spl_fit.c index a90d821c82..fd6086a65c 100644 --- a/common/spl/spl_fit.c +++ b/common/spl/spl_fit.c @@ -466,7 +466,22 @@ static int spl_fit_record_loadable(const void *fit, int images, int index, static int spl_fit_image_get_os(const void *fit, int noffset, uint8_t *os) { #if CONFIG_IS_ENABLED(FIT_IMAGE_TINY) && !defined(CONFIG_SPL_OS_BOOT) - return -ENOTSUPP; + const char *name = fdt_getprop(fit, noffset, FIT_OS_PROP, NULL); + + if (!name) + return -ENOENT; + + /* + * We don't care what the type of the image actually is, + * only whether or not it is U-Boot. This saves some + * space by omitting the large table of OS types. + */ + if (!strcmp(name, "u-boot")) + *os = IH_OS_U_BOOT; + else + *os = IH_OS_INVALID; + + return 0; #else return fit_image_get_os(fit, noffset, os); #endif diff --git a/configs/A20-OLinuXino-Lime2-eMMC_defconfig b/configs/A20-OLinuXino-Lime2-eMMC_defconfig index b1a38d7a4d..f9f8f14151 100644 --- a/configs/A20-OLinuXino-Lime2-eMMC_defconfig +++ b/configs/A20-OLinuXino-Lime2-eMMC_defconfig @@ -19,6 +19,8 @@ CONFIG_CMD_USB_MASS_STORAGE=y CONFIG_SCSI_AHCI=y CONFIG_DFU_RAM=y CONFIG_FASTBOOT_CMD_OEM_FORMAT=y +CONFIG_PHY_MICREL=y +CONFIG_PHY_MICREL_KSZ90X1=y CONFIG_PHY_REALTEK=y CONFIG_ETH_DESIGNWARE=y CONFIG_RGMII=y diff --git a/configs/pine_h64_defconfig b/configs/pine_h64_defconfig index 328849d724..07ad31e3a7 100644 --- a/configs/pine_h64_defconfig +++ b/configs/pine_h64_defconfig @@ -11,5 +11,6 @@ CONFIG_SPL_SPI_SUNXI=y CONFIG_DEFAULT_DEVICE_TREE="sun50i-h6-pine-h64" # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_SUN8I_EMAC=y +CONFIG_MACPWR="PC16" CONFIG_USB_EHCI_HCD=y CONFIG_USB_OHCI_HCD=y diff --git a/configs/teres_i_defconfig b/configs/teres_i_defconfig index 1eba20e5f5..ee07f86f64 100644 --- a/configs/teres_i_defconfig +++ b/configs/teres_i_defconfig @@ -7,6 +7,7 @@ CONFIG_DRAM_ZQ=3881949 CONFIG_MMC_SUNXI_SLOT_EXTRA=2 CONFIG_USB1_VBUS_PIN="PL7" CONFIG_I2C0_ENABLE=y +CONFIG_PREBOOT="setenv usb_pgood_delay 2000; usb start" CONFIG_DEFAULT_DEVICE_TREE="sun50i-a64-teres-i" CONFIG_DM_REGULATOR=y CONFIG_DM_REGULATOR_FIXED=y diff --git a/doc/git-mailrc b/doc/git-mailrc index 31595a71c9..bbca3a9a37 100644 --- a/doc/git-mailrc +++ b/doc/git-mailrc @@ -36,7 +36,7 @@ alias marex Marek Vasut alias mariosix Mario Six alias masahiro Masahiro Yamada alias mateusz Mateusz Kulikowski -alias maxime Maxime Ripard +alias maxime Maxime Ripard alias mbrugger Matthias Brugger alias monstr Michal Simek alias prom Minkyu Kang diff --git a/drivers/net/sun8i_emac.c b/drivers/net/sun8i_emac.c index 1dae81c7bf..4524604126 100644 --- a/drivers/net/sun8i_emac.c +++ b/drivers/net/sun8i_emac.c @@ -29,6 +29,7 @@ #include #include #include +#include #if CONFIG_IS_ENABLED(DM_GPIO) #include #endif @@ -40,6 +41,11 @@ #define MDIO_CMD_MII_PHY_REG_ADDR_SHIFT 4 #define MDIO_CMD_MII_PHY_ADDR_MASK 0x0001f000 #define MDIO_CMD_MII_PHY_ADDR_SHIFT 12 +#define MDIO_CMD_MII_CLK_CSR_DIV_16 0x0 +#define MDIO_CMD_MII_CLK_CSR_DIV_32 0x1 +#define MDIO_CMD_MII_CLK_CSR_DIV_64 0x2 +#define MDIO_CMD_MII_CLK_CSR_DIV_128 0x3 +#define MDIO_CMD_MII_CLK_CSR_SHIFT 20 #define CONFIG_TX_DESCR_NUM 32 #define CONFIG_RX_DESCR_NUM 32 @@ -84,15 +90,32 @@ /* H3/A64 EMAC Register's offset */ #define EMAC_CTL0 0x00 +#define EMAC_CTL0_FULL_DUPLEX BIT(0) +#define EMAC_CTL0_SPEED_MASK GENMASK(3, 2) +#define EMAC_CTL0_SPEED_10 (0x2 << 2) +#define EMAC_CTL0_SPEED_100 (0x3 << 2) +#define EMAC_CTL0_SPEED_1000 (0x0 << 2) #define EMAC_CTL1 0x04 +#define EMAC_CTL1_SOFT_RST BIT(0) +#define EMAC_CTL1_BURST_LEN_SHIFT 24 #define EMAC_INT_STA 0x08 #define EMAC_INT_EN 0x0c #define EMAC_TX_CTL0 0x10 +#define EMAC_TX_CTL0_TX_EN BIT(31) #define EMAC_TX_CTL1 0x14 +#define EMAC_TX_CTL1_TX_MD BIT(1) +#define EMAC_TX_CTL1_TX_DMA_EN BIT(30) +#define EMAC_TX_CTL1_TX_DMA_START BIT(31) #define EMAC_TX_FLOW_CTL 0x1c #define EMAC_TX_DMA_DESC 0x20 #define EMAC_RX_CTL0 0x24 +#define EMAC_RX_CTL0_RX_EN BIT(31) #define EMAC_RX_CTL1 0x28 +#define EMAC_RX_CTL1_RX_MD BIT(1) +#define EMAC_RX_CTL1_RX_RUNT_FRM BIT(2) +#define EMAC_RX_CTL1_RX_ERR_FRM BIT(3) +#define EMAC_RX_CTL1_RX_DMA_EN BIT(30) +#define EMAC_RX_CTL1_RX_DMA_START BIT(31) #define EMAC_RX_DMA_DESC 0x34 #define EMAC_MII_CMD 0x48 #define EMAC_MII_DATA 0x4c @@ -104,6 +127,13 @@ #define EMAC_RX_DMA_STA 0xc0 #define EMAC_RX_CUR_DESC 0xc4 +#define EMAC_DESC_OWN_DMA BIT(31) +#define EMAC_DESC_LAST_DESC BIT(30) +#define EMAC_DESC_FIRST_DESC BIT(29) +#define EMAC_DESC_CHAIN_SECOND BIT(24) + +#define EMAC_DESC_RX_ERROR_MASK 0x400068db + DECLARE_GLOBAL_DATA_PTR; enum emac_variant { @@ -116,7 +146,7 @@ enum emac_variant { struct emac_dma_desc { u32 status; - u32 st; + u32 ctl_size; u32 buf_addr; u32 next; } __aligned(ARCH_DMA_MINALIGN); @@ -166,32 +196,31 @@ static int sun8i_mdio_read(struct mii_dev *bus, int addr, int devad, int reg) { struct udevice *dev = bus->priv; struct emac_eth_dev *priv = dev_get_priv(dev); - ulong start; - u32 miiaddr = 0; - int timeout = CONFIG_MDIO_TIMEOUT; + u32 mii_cmd; + int ret; - miiaddr &= ~MDIO_CMD_MII_WRITE; - miiaddr &= ~MDIO_CMD_MII_PHY_REG_ADDR_MASK; - miiaddr |= (reg << MDIO_CMD_MII_PHY_REG_ADDR_SHIFT) & + mii_cmd = (reg << MDIO_CMD_MII_PHY_REG_ADDR_SHIFT) & MDIO_CMD_MII_PHY_REG_ADDR_MASK; - - miiaddr &= ~MDIO_CMD_MII_PHY_ADDR_MASK; - - miiaddr |= (addr << MDIO_CMD_MII_PHY_ADDR_SHIFT) & + mii_cmd |= (addr << MDIO_CMD_MII_PHY_ADDR_SHIFT) & MDIO_CMD_MII_PHY_ADDR_MASK; - miiaddr |= MDIO_CMD_MII_BUSY; + /* + * The EMAC clock is either 200 or 300 MHz, so we need a divider + * of 128 to get the MDIO frequency below the required 2.5 MHz. + */ + mii_cmd |= MDIO_CMD_MII_CLK_CSR_DIV_128 << MDIO_CMD_MII_CLK_CSR_SHIFT; - writel(miiaddr, priv->mac_reg + EMAC_MII_CMD); + mii_cmd |= MDIO_CMD_MII_BUSY; - start = get_timer(0); - while (get_timer(start) < timeout) { - if (!(readl(priv->mac_reg + EMAC_MII_CMD) & MDIO_CMD_MII_BUSY)) - return readl(priv->mac_reg + EMAC_MII_DATA); - udelay(10); - }; + writel(mii_cmd, priv->mac_reg + EMAC_MII_CMD); - return -1; + ret = wait_for_bit_le32(priv->mac_reg + EMAC_MII_CMD, + MDIO_CMD_MII_BUSY, false, + CONFIG_MDIO_TIMEOUT, true); + if (ret < 0) + return ret; + + return readl(priv->mac_reg + EMAC_MII_DATA); } static int sun8i_mdio_write(struct mii_dev *bus, int addr, int devad, int reg, @@ -199,39 +228,35 @@ static int sun8i_mdio_write(struct mii_dev *bus, int addr, int devad, int reg, { struct udevice *dev = bus->priv; struct emac_eth_dev *priv = dev_get_priv(dev); - ulong start; - u32 miiaddr = 0; - int ret = -1, timeout = CONFIG_MDIO_TIMEOUT; + u32 mii_cmd; - miiaddr &= ~MDIO_CMD_MII_PHY_REG_ADDR_MASK; - miiaddr |= (reg << MDIO_CMD_MII_PHY_REG_ADDR_SHIFT) & + mii_cmd = (reg << MDIO_CMD_MII_PHY_REG_ADDR_SHIFT) & MDIO_CMD_MII_PHY_REG_ADDR_MASK; - - miiaddr &= ~MDIO_CMD_MII_PHY_ADDR_MASK; - miiaddr |= (addr << MDIO_CMD_MII_PHY_ADDR_SHIFT) & + mii_cmd |= (addr << MDIO_CMD_MII_PHY_ADDR_SHIFT) & MDIO_CMD_MII_PHY_ADDR_MASK; - miiaddr |= MDIO_CMD_MII_WRITE; - miiaddr |= MDIO_CMD_MII_BUSY; + /* + * The EMAC clock is either 200 or 300 MHz, so we need a divider + * of 128 to get the MDIO frequency below the required 2.5 MHz. + */ + mii_cmd |= MDIO_CMD_MII_CLK_CSR_DIV_128 << MDIO_CMD_MII_CLK_CSR_SHIFT; + + mii_cmd |= MDIO_CMD_MII_WRITE; + mii_cmd |= MDIO_CMD_MII_BUSY; writel(val, priv->mac_reg + EMAC_MII_DATA); - writel(miiaddr, priv->mac_reg + EMAC_MII_CMD); + writel(mii_cmd, priv->mac_reg + EMAC_MII_CMD); - start = get_timer(0); - while (get_timer(start) < timeout) { - if (!(readl(priv->mac_reg + EMAC_MII_CMD) & - MDIO_CMD_MII_BUSY)) { - ret = 0; - break; - } - udelay(10); - }; - - return ret; + return wait_for_bit_le32(priv->mac_reg + EMAC_MII_CMD, + MDIO_CMD_MII_BUSY, false, + CONFIG_MDIO_TIMEOUT, true); } -static int _sun8i_write_hwaddr(struct emac_eth_dev *priv, u8 *mac_id) +static int sun8i_eth_write_hwaddr(struct udevice *dev) { + struct emac_eth_dev *priv = dev_get_priv(dev); + struct eth_pdata *pdata = dev_get_platdata(dev); + uchar *mac_id = pdata->enetaddr; u32 macid_lo, macid_hi; macid_lo = mac_id[0] + (mac_id[1] << 8) + (mac_id[2] << 16) + @@ -252,21 +277,21 @@ static void sun8i_adjust_link(struct emac_eth_dev *priv, v = readl(priv->mac_reg + EMAC_CTL0); if (phydev->duplex) - v |= BIT(0); + v |= EMAC_CTL0_FULL_DUPLEX; else - v &= ~BIT(0); + v &= ~EMAC_CTL0_FULL_DUPLEX; - v &= ~0x0C; + v &= ~EMAC_CTL0_SPEED_MASK; switch (phydev->speed) { case 1000: + v |= EMAC_CTL0_SPEED_1000; break; case 100: - v |= BIT(2); - v |= BIT(3); + v |= EMAC_CTL0_SPEED_100; break; case 10: - v |= BIT(3); + v |= EMAC_CTL0_SPEED_10; break; } writel(v, priv->mac_reg + EMAC_CTL0); @@ -372,24 +397,36 @@ static int sun8i_phy_init(struct emac_eth_dev *priv, void *dev) return 0; } +#define cache_clean_descriptor(desc) \ + flush_dcache_range((uintptr_t)(desc), \ + (uintptr_t)(desc) + sizeof(struct emac_dma_desc)) + +#define cache_inv_descriptor(desc) \ + invalidate_dcache_range((uintptr_t)(desc), \ + (uintptr_t)(desc) + sizeof(struct emac_dma_desc)) + static void rx_descs_init(struct emac_eth_dev *priv) { struct emac_dma_desc *desc_table_p = &priv->rx_chain[0]; char *rxbuffs = &priv->rxbuffer[0]; struct emac_dma_desc *desc_p; - u32 idx; + int i; - /* flush Rx buffers */ - flush_dcache_range((uintptr_t)rxbuffs, (ulong)rxbuffs + - RX_TOTAL_BUFSIZE); + /* + * Make sure we don't have dirty cache lines around, which could + * be cleaned to DRAM *after* the MAC has already written data to it. + */ + invalidate_dcache_range((uintptr_t)desc_table_p, + (uintptr_t)desc_table_p + sizeof(priv->rx_chain)); + invalidate_dcache_range((uintptr_t)rxbuffs, + (uintptr_t)rxbuffs + sizeof(priv->rxbuffer)); - for (idx = 0; idx < CONFIG_RX_DESCR_NUM; idx++) { - desc_p = &desc_table_p[idx]; - desc_p->buf_addr = (uintptr_t)&rxbuffs[idx * CONFIG_ETH_BUFSIZE] - ; - desc_p->next = (uintptr_t)&desc_table_p[idx + 1]; - desc_p->st |= CONFIG_ETH_RXSIZE; - desc_p->status = BIT(31); + for (i = 0; i < CONFIG_RX_DESCR_NUM; i++) { + desc_p = &desc_table_p[i]; + desc_p->buf_addr = (uintptr_t)&rxbuffs[i * CONFIG_ETH_BUFSIZE]; + desc_p->next = (uintptr_t)&desc_table_p[i + 1]; + desc_p->ctl_size = CONFIG_ETH_RXSIZE; + desc_p->status = EMAC_DESC_OWN_DMA; } /* Correcting the last pointer of the chain */ @@ -408,87 +445,74 @@ static void tx_descs_init(struct emac_eth_dev *priv) struct emac_dma_desc *desc_table_p = &priv->tx_chain[0]; char *txbuffs = &priv->txbuffer[0]; struct emac_dma_desc *desc_p; - u32 idx; + int i; - for (idx = 0; idx < CONFIG_TX_DESCR_NUM; idx++) { - desc_p = &desc_table_p[idx]; - desc_p->buf_addr = (uintptr_t)&txbuffs[idx * CONFIG_ETH_BUFSIZE] - ; - desc_p->next = (uintptr_t)&desc_table_p[idx + 1]; - desc_p->status = (1 << 31); - desc_p->st = 0; + for (i = 0; i < CONFIG_TX_DESCR_NUM; i++) { + desc_p = &desc_table_p[i]; + desc_p->buf_addr = (uintptr_t)&txbuffs[i * CONFIG_ETH_BUFSIZE]; + desc_p->next = (uintptr_t)&desc_table_p[i + 1]; + desc_p->ctl_size = 0; + desc_p->status = 0; } /* Correcting the last pointer of the chain */ desc_p->next = (uintptr_t)&desc_table_p[0]; - /* Flush all Tx buffer descriptors */ - flush_dcache_range((uintptr_t)priv->tx_chain, - (uintptr_t)priv->tx_chain + - sizeof(priv->tx_chain)); + /* Flush the first TX buffer descriptor we will tell the MAC about. */ + cache_clean_descriptor(desc_table_p); writel((uintptr_t)&desc_table_p[0], priv->mac_reg + EMAC_TX_DMA_DESC); priv->tx_currdescnum = 0; } -static int _sun8i_emac_eth_init(struct emac_eth_dev *priv, u8 *enetaddr) +static int sun8i_emac_eth_start(struct udevice *dev) { - u32 reg, v; - int timeout = 100; + struct emac_eth_dev *priv = dev_get_priv(dev); + int ret; - reg = readl((priv->mac_reg + EMAC_CTL1)); - - if (!(reg & 0x1)) { - /* Soft reset MAC */ - setbits_le32((priv->mac_reg + EMAC_CTL1), 0x1); - do { - reg = readl(priv->mac_reg + EMAC_CTL1); - } while ((reg & 0x01) != 0 && (--timeout)); - if (!timeout) { - printf("%s: Timeout\n", __func__); - return -1; - } + /* Soft reset MAC */ + writel(EMAC_CTL1_SOFT_RST, priv->mac_reg + EMAC_CTL1); + ret = wait_for_bit_le32(priv->mac_reg + EMAC_CTL1, + EMAC_CTL1_SOFT_RST, false, 10, true); + if (ret) { + printf("%s: Timeout\n", __func__); + return ret; } /* Rewrite mac address after reset */ - _sun8i_write_hwaddr(priv, enetaddr); + sun8i_eth_write_hwaddr(dev); - v = readl(priv->mac_reg + EMAC_TX_CTL1); - /* TX_MD Transmission starts after a full frame located in TX DMA FIFO*/ - v |= BIT(1); - writel(v, priv->mac_reg + EMAC_TX_CTL1); + /* transmission starts after the full frame arrived in TX DMA FIFO */ + setbits_le32(priv->mac_reg + EMAC_TX_CTL1, EMAC_TX_CTL1_TX_MD); - v = readl(priv->mac_reg + EMAC_RX_CTL1); - /* RX_MD RX DMA reads data from RX DMA FIFO to host memory after a + /* + * RX DMA reads data from RX DMA FIFO to host memory after a * complete frame has been written to RX DMA FIFO */ - v |= BIT(1); - writel(v, priv->mac_reg + EMAC_RX_CTL1); + setbits_le32(priv->mac_reg + EMAC_RX_CTL1, EMAC_RX_CTL1_RX_MD); - /* DMA */ - writel(8 << 24, priv->mac_reg + EMAC_CTL1); + /* DMA burst length */ + writel(8 << EMAC_CTL1_BURST_LEN_SHIFT, priv->mac_reg + EMAC_CTL1); /* Initialize rx/tx descriptors */ rx_descs_init(priv); tx_descs_init(priv); /* PHY Start Up */ - phy_startup(priv->phydev); + ret = phy_startup(priv->phydev); + if (ret) + return ret; sun8i_adjust_link(priv, priv->phydev); - /* Start RX DMA */ - v = readl(priv->mac_reg + EMAC_RX_CTL1); - v |= BIT(30); - writel(v, priv->mac_reg + EMAC_RX_CTL1); - /* Start TX DMA */ - v = readl(priv->mac_reg + EMAC_TX_CTL1); - v |= BIT(30); - writel(v, priv->mac_reg + EMAC_TX_CTL1); + /* Start RX/TX DMA */ + setbits_le32(priv->mac_reg + EMAC_RX_CTL1, EMAC_RX_CTL1_RX_DMA_EN | + EMAC_RX_CTL1_RX_ERR_FRM | EMAC_RX_CTL1_RX_RUNT_FRM); + setbits_le32(priv->mac_reg + EMAC_TX_CTL1, EMAC_TX_CTL1_TX_DMA_EN); /* Enable RX/TX */ - setbits_le32(priv->mac_reg + EMAC_RX_CTL0, BIT(31)); - setbits_le32(priv->mac_reg + EMAC_TX_CTL0, BIT(31)); + setbits_le32(priv->mac_reg + EMAC_RX_CTL0, EMAC_RX_CTL0_RX_EN); + setbits_le32(priv->mac_reg + EMAC_TX_CTL0, EMAC_TX_CTL0_TX_EN); return 0; } @@ -558,88 +582,71 @@ static int parse_phy_pins(struct udevice *dev) return 0; } -static int _sun8i_eth_recv(struct emac_eth_dev *priv, uchar **packetp) +static int sun8i_emac_eth_recv(struct udevice *dev, int flags, uchar **packetp) { + struct emac_eth_dev *priv = dev_get_priv(dev); u32 status, desc_num = priv->rx_currdescnum; struct emac_dma_desc *desc_p = &priv->rx_chain[desc_num]; - int length = -EAGAIN; - int good_packet = 1; - uintptr_t desc_start = (uintptr_t)desc_p; - uintptr_t desc_end = desc_start + - roundup(sizeof(*desc_p), ARCH_DMA_MINALIGN); - - ulong data_start = (uintptr_t)desc_p->buf_addr; - ulong data_end; + uintptr_t data_start = (uintptr_t)desc_p->buf_addr; + int length; /* Invalidate entire buffer descriptor */ - invalidate_dcache_range(desc_start, desc_end); + cache_inv_descriptor(desc_p); status = desc_p->status; /* Check for DMA own bit */ - if (!(status & BIT(31))) { - length = (desc_p->status >> 16) & 0x3FFF; + if (status & EMAC_DESC_OWN_DMA) + return -EAGAIN; - if (length < 0x40) { - good_packet = 0; - debug("RX: Bad Packet (runt)\n"); - } + length = (status >> 16) & 0x3fff; - data_end = data_start + length; - /* Invalidate received data */ - invalidate_dcache_range(rounddown(data_start, - ARCH_DMA_MINALIGN), - roundup(data_end, - ARCH_DMA_MINALIGN)); - if (good_packet) { - if (length > CONFIG_ETH_RXSIZE) { - printf("Received packet is too big (len=%d)\n", - length); - return -EMSGSIZE; - } - *packetp = (uchar *)(ulong)desc_p->buf_addr; - return length; - } + /* make sure we read from DRAM, not our cache */ + invalidate_dcache_range(data_start, + data_start + roundup(length, ARCH_DMA_MINALIGN)); + + if (status & EMAC_DESC_RX_ERROR_MASK) { + debug("RX: packet error: 0x%x\n", + status & EMAC_DESC_RX_ERROR_MASK); + return 0; } + if (length < 0x40) { + debug("RX: Bad Packet (runt)\n"); + return 0; + } + + if (length > CONFIG_ETH_RXSIZE) { + debug("RX: Too large packet (%d bytes)\n", length); + return 0; + } + + *packetp = (uchar *)(ulong)desc_p->buf_addr; return length; } -static int _sun8i_emac_eth_send(struct emac_eth_dev *priv, void *packet, - int len) +static int sun8i_emac_eth_send(struct udevice *dev, void *packet, int length) { - u32 v, desc_num = priv->tx_currdescnum; + struct emac_eth_dev *priv = dev_get_priv(dev); + u32 desc_num = priv->tx_currdescnum; struct emac_dma_desc *desc_p = &priv->tx_chain[desc_num]; - uintptr_t desc_start = (uintptr_t)desc_p; - uintptr_t desc_end = desc_start + - roundup(sizeof(*desc_p), ARCH_DMA_MINALIGN); - uintptr_t data_start = (uintptr_t)desc_p->buf_addr; uintptr_t data_end = data_start + - roundup(len, ARCH_DMA_MINALIGN); + roundup(length, ARCH_DMA_MINALIGN); - /* Invalidate entire buffer descriptor */ - invalidate_dcache_range(desc_start, desc_end); + desc_p->ctl_size = length | EMAC_DESC_CHAIN_SECOND; - desc_p->st = len; - /* Mandatory undocumented bit */ - desc_p->st |= BIT(24); - - memcpy((void *)data_start, packet, len); + memcpy((void *)data_start, packet, length); /* Flush data to be sent */ flush_dcache_range(data_start, data_end); - /* frame end */ - desc_p->st |= BIT(30); - desc_p->st |= BIT(31); + /* frame begin and end */ + desc_p->ctl_size |= EMAC_DESC_LAST_DESC | EMAC_DESC_FIRST_DESC; + desc_p->status = EMAC_DESC_OWN_DMA; - /*frame begin */ - desc_p->st |= BIT(29); - desc_p->status = BIT(31); - - /*Descriptors st and status field has changed, so FLUSH it */ - flush_dcache_range(desc_start, desc_end); + /* make sure the MAC reads the actual data from DRAM */ + cache_clean_descriptor(desc_p); /* Move to next Descriptor and wrap around */ if (++desc_num >= CONFIG_TX_DESCR_NUM) @@ -647,22 +654,16 @@ static int _sun8i_emac_eth_send(struct emac_eth_dev *priv, void *packet, priv->tx_currdescnum = desc_num; /* Start the DMA */ - v = readl(priv->mac_reg + EMAC_TX_CTL1); - v |= BIT(31);/* mandatory */ - v |= BIT(30);/* mandatory */ - writel(v, priv->mac_reg + EMAC_TX_CTL1); + setbits_le32(priv->mac_reg + EMAC_TX_CTL1, EMAC_TX_CTL1_TX_DMA_START); + + /* + * Since we copied the data above, we return here without waiting + * for the packet to be actually send out. + */ return 0; } -static int sun8i_eth_write_hwaddr(struct udevice *dev) -{ - struct eth_pdata *pdata = dev_get_platdata(dev); - struct emac_eth_dev *priv = dev_get_priv(dev); - - return _sun8i_write_hwaddr(priv, pdata->enetaddr); -} - static int sun8i_emac_board_setup(struct udevice *dev, struct emac_eth_dev *priv) { @@ -760,40 +761,18 @@ static int sun8i_mdio_init(const char *name, struct udevice *priv) return mdio_register(bus); } -static int sun8i_emac_eth_start(struct udevice *dev) -{ - struct eth_pdata *pdata = dev_get_platdata(dev); - - return _sun8i_emac_eth_init(dev->priv, pdata->enetaddr); -} - -static int sun8i_emac_eth_send(struct udevice *dev, void *packet, int length) +static int sun8i_eth_free_pkt(struct udevice *dev, uchar *packet, + int length) { struct emac_eth_dev *priv = dev_get_priv(dev); - - return _sun8i_emac_eth_send(priv, packet, length); -} - -static int sun8i_emac_eth_recv(struct udevice *dev, int flags, uchar **packetp) -{ - struct emac_eth_dev *priv = dev_get_priv(dev); - - return _sun8i_eth_recv(priv, packetp); -} - -static int _sun8i_free_pkt(struct emac_eth_dev *priv) -{ u32 desc_num = priv->rx_currdescnum; struct emac_dma_desc *desc_p = &priv->rx_chain[desc_num]; - uintptr_t desc_start = (uintptr_t)desc_p; - uintptr_t desc_end = desc_start + - roundup(sizeof(u32), ARCH_DMA_MINALIGN); - /* Make the current descriptor valid again */ - desc_p->status |= BIT(31); + /* give the current descriptor back to the MAC */ + desc_p->status |= EMAC_DESC_OWN_DMA; /* Flush Status field of descriptor */ - flush_dcache_range(desc_start, desc_end); + cache_clean_descriptor(desc_p); /* Move to next desc and wrap-around condition. */ if (++desc_num >= CONFIG_RX_DESCR_NUM) @@ -803,24 +782,17 @@ static int _sun8i_free_pkt(struct emac_eth_dev *priv) return 0; } -static int sun8i_eth_free_pkt(struct udevice *dev, uchar *packet, - int length) -{ - struct emac_eth_dev *priv = dev_get_priv(dev); - - return _sun8i_free_pkt(priv); -} - static void sun8i_emac_eth_stop(struct udevice *dev) { struct emac_eth_dev *priv = dev_get_priv(dev); /* Stop Rx/Tx transmitter */ - clrbits_le32(priv->mac_reg + EMAC_RX_CTL0, BIT(31)); - clrbits_le32(priv->mac_reg + EMAC_TX_CTL0, BIT(31)); + clrbits_le32(priv->mac_reg + EMAC_RX_CTL0, EMAC_RX_CTL0_RX_EN); + clrbits_le32(priv->mac_reg + EMAC_TX_CTL0, EMAC_TX_CTL0_TX_EN); - /* Stop TX DMA */ - clrbits_le32(priv->mac_reg + EMAC_TX_CTL1, BIT(30)); + /* Stop RX/TX DMA */ + clrbits_le32(priv->mac_reg + EMAC_TX_CTL1, EMAC_TX_CTL1_TX_DMA_EN); + clrbits_le32(priv->mac_reg + EMAC_RX_CTL1, EMAC_RX_CTL1_RX_DMA_EN); phy_shutdown(priv->phydev); } @@ -855,47 +827,30 @@ static const struct eth_ops sun8i_emac_eth_ops = { .stop = sun8i_emac_eth_stop, }; -static int sun8i_get_ephy_nodes(struct udevice *dev, struct emac_eth_dev *priv) +static int sun8i_handle_internal_phy(struct udevice *dev, struct emac_eth_dev *priv) { - int emac_node, ephy_node, ret, ephy_handle; + struct ofnode_phandle_args phandle; + int ret; - emac_node = fdt_path_offset(gd->fdt_blob, - "/soc/ethernet@1c30000"); - if (emac_node < 0) { - debug("failed to get emac node\n"); - return emac_node; - } - ephy_handle = fdtdec_lookup_phandle(gd->fdt_blob, - emac_node, "phy-handle"); + ret = ofnode_parse_phandle_with_args(dev_ofnode(dev), "phy-handle", + NULL, 0, 0, &phandle); + if (ret) + return ret; - /* look for mdio-mux node for internal PHY node */ - ephy_node = fdt_path_offset(gd->fdt_blob, - "/soc/ethernet@1c30000/mdio-mux/mdio@1/ethernet-phy@1"); - if (ephy_node < 0) { - debug("failed to get mdio-mux with internal PHY\n"); - return ephy_node; - } - - /* This is not the phy we are looking for */ - if (ephy_node != ephy_handle) + /* If the PHY node is not a child of the internal MDIO bus, we are + * using some external PHY. + */ + if (!ofnode_device_is_compatible(ofnode_get_parent(phandle.node), + "allwinner,sun8i-h3-mdio-internal")) return 0; - ret = fdt_node_check_compatible(gd->fdt_blob, ephy_node, - "allwinner,sun8i-h3-mdio-internal"); - if (ret < 0) { - debug("failed to find mdio-internal node\n"); - return ret; - } - - ret = clk_get_by_index_nodev(offset_to_ofnode(ephy_node), 0, - &priv->ephy_clk); + ret = clk_get_by_index_nodev(phandle.node, 0, &priv->ephy_clk); if (ret) { dev_err(dev, "failed to get EPHY TX clock\n"); return ret; } - ret = reset_get_by_index_nodev(offset_to_ofnode(ephy_node), 0, - &priv->ephy_rst); + ret = reset_get_by_index_nodev(phandle.node, 0, &priv->ephy_rst); if (ret) { dev_err(dev, "failed to get EPHY TX reset\n"); return ret; @@ -987,7 +942,7 @@ static int sun8i_emac_eth_ofdata_to_platdata(struct udevice *dev) } if (priv->variant == H3_EMAC) { - ret = sun8i_get_ephy_nodes(dev, priv); + ret = sun8i_handle_internal_phy(dev, priv); if (ret) return ret; } diff --git a/tools/binman/README.entries b/tools/binman/README.entries index c1d436563e..bdb4fd6ee5 100644 --- a/tools/binman/README.entries +++ b/tools/binman/README.entries @@ -385,8 +385,8 @@ You can create config nodes in a similar way: default = "@config-DEFAULT-SEQ"; @config-SEQ { description = "NAME"; - firmware = "uboot"; - loadables = "atf"; + firmware = "atf"; + loadables = "uboot"; fdt = "fdt-SEQ"; }; }; diff --git a/tools/binman/etype/fit.py b/tools/binman/etype/fit.py index de4745c552..1a7cbd7cec 100644 --- a/tools/binman/etype/fit.py +++ b/tools/binman/etype/fit.py @@ -73,8 +73,8 @@ class Entry_fit(Entry): default = "@config-DEFAULT-SEQ"; @config-SEQ { description = "NAME"; - firmware = "uboot"; - loadables = "atf"; + firmware = "atf"; + loadables = "uboot"; fdt = "fdt-SEQ"; }; }; @@ -205,10 +205,10 @@ class Entry_fit(Entry): b'SEQ', tools.ToBytes(str(seq + 1))) fsw.property(pname, val) - # Add data for 'fdt' nodes (but not 'config') - if depth == 1 and in_images: - fsw.property('data', - tools.ReadFile(fname)) + # Add data for 'fdt' nodes (but not 'config') + if depth == 1 and in_images: + fsw.property('data', + tools.ReadFile(fname)) else: if self._fdts is None: if self._fit_list_prop: diff --git a/tools/binman/etype/scp.py b/tools/binman/etype/scp.py new file mode 100644 index 0000000000..93f8787d2d --- /dev/null +++ b/tools/binman/etype/scp.py @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2020 Samuel Holland +# +# Entry-type module for System Control Processor (SCP) firmware blob +# + +from binman.etype.blob_named_by_arg import Entry_blob_named_by_arg + +class Entry_scp(Entry_blob_named_by_arg): + """Entry containing a System Control Processor (SCP) firmware blob + + Properties / Entry arguments: + - scp-path: Filename of file to read into the entry, typically scp.bin + + This entry holds firmware for an external platform-specific coprocessor. + """ + def __init__(self, section, etype, node): + super().__init__(section, etype, node, 'scp') + self.external = True diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index b771b9d5df..75f6ca3a89 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -75,6 +75,7 @@ FSP_M_DATA = b'fsp_m' FSP_S_DATA = b'fsp_s' FSP_T_DATA = b'fsp_t' ATF_BL31_DATA = b'bl31' +SCP_DATA = b'scp' TEST_FDT1_DATA = b'fdt1' TEST_FDT2_DATA = b'test-fdt2' ENV_DATA = b'var1=1\nvar2="2"' @@ -175,6 +176,7 @@ class TestFunctional(unittest.TestCase): TestFunctional._MakeInputFile('compress', COMPRESS_DATA) TestFunctional._MakeInputFile('bl31.bin', ATF_BL31_DATA) + TestFunctional._MakeInputFile('scp.bin', SCP_DATA) # Add a few .dtb files for testing TestFunctional._MakeInputFile('%s/test-fdt1.dtb' % TEST_FDT_SUBDIR, @@ -3578,6 +3580,11 @@ class TestFunctional(unittest.TestCase): data = self._DoReadFile('169_atf_bl31.dts') self.assertEqual(ATF_BL31_DATA, data[:len(ATF_BL31_DATA)]) + def testPackScp(self): + """Test that an image with an SCP binary can be created""" + data = self._DoReadFile('172_scp.dts') + self.assertEqual(SCP_DATA, data[:len(SCP_DATA)]) + def testFitFdt(self): """Test an image with an FIT with multiple FDT images""" def _CheckFdt(seq, expected_data): diff --git a/tools/binman/missing-blob-help b/tools/binman/missing-blob-help index 7cf1c34610..f7bc80ea83 100644 --- a/tools/binman/missing-blob-help +++ b/tools/binman/missing-blob-help @@ -13,3 +13,7 @@ Firmware and build with BL31=/path/to/bl31.bin atf-bl31-sunxi: Please read the section on ARM Trusted Firmware (ATF) in board/sunxi/README.sunxi64 + +scp-sunxi: +SCP firmware is required for system suspend, but is otherwise optional. +Please read the section on SCP firmware in board/sunxi/README.sunxi64 diff --git a/tools/binman/test/172_scp.dts b/tools/binman/test/172_scp.dts new file mode 100644 index 0000000000..354e4ef17d --- /dev/null +++ b/tools/binman/test/172_scp.dts @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <16>; + + scp { + filename = "scp.bin"; + }; + }; +};