- sun8i emac changes (Andre)
- SCP firmware (Samuel)
This commit is contained in:
Tom Rini 2020-10-22 20:32:02 -04:00
commit 18261b8552
19 changed files with 442 additions and 283 deletions

View File

@ -200,7 +200,7 @@ config SYS_MALLOC_F_LEN
default 0x2000 if (ARCH_IMX8 || ARCH_IMX8M || ARCH_MX7 || \ default 0x2000 if (ARCH_IMX8 || ARCH_IMX8M || ARCH_MX7 || \
ARCH_MX7ULP || ARCH_MX6 || ARCH_MX5 || \ ARCH_MX7ULP || ARCH_MX6 || ARCH_MX5 || \
ARCH_LS1012A || ARCH_LS1021A || ARCH_LS1043A || \ ARCH_LS1012A || ARCH_LS1021A || ARCH_LS1043A || \
ARCH_LS1046A || ARCH_QEMU) ARCH_LS1046A || ARCH_QEMU || ARCH_SUNXI)
default 0x400 default 0x400
help help
Before relocation, memory is very limited on many platforms. Still, Before relocation, memory is very limited on many platforms. Still,

View File

@ -1332,6 +1332,7 @@ cmd_binman = $(srctree)/tools/binman/binman $(if $(BINMAN_DEBUG),-D) \
-I arch/$(ARCH)/dts -a of-list=$(CONFIG_OF_LIST) \ -I arch/$(ARCH)/dts -a of-list=$(CONFIG_OF_LIST) \
-a atf-bl31-path=${BL31} \ -a atf-bl31-path=${BL31} \
-a default-dt=$(default_dt) \ -a default-dt=$(default_dt) \
-a scp-path=$(SCP) \
$(BINMAN_$(@F)) $(BINMAN_$(@F))
OBJCOPYFLAGS_u-boot.ldr.hex := -I binary -O ihex OBJCOPYFLAGS_u-boot.ldr.hex := -I binary -O ihex
@ -1441,11 +1442,13 @@ else
MKIMAGEFLAGS_u-boot.itb = -E MKIMAGEFLAGS_u-boot.itb = -E
endif endif
ifdef U_BOOT_ITS
u-boot.itb: u-boot-nodtb.bin \ u-boot.itb: u-boot-nodtb.bin \
$(if $(CONFIG_OF_SEPARATE)$(CONFIG_OF_EMBED)$(CONFIG_OF_HOSTFILE),dts/dt.dtb) \ $(if $(CONFIG_OF_SEPARATE)$(CONFIG_OF_EMBED)$(CONFIG_OF_HOSTFILE),dts/dt.dtb) \
$(U_BOOT_ITS) FORCE $(U_BOOT_ITS) FORCE
$(call if_changed,mkfitimage) $(call if_changed,mkfitimage)
$(BOARD_SIZE_CHECK) $(BOARD_SIZE_CHECK)
endif
u-boot-spl.kwb: u-boot.img spl/u-boot-spl.bin FORCE u-boot-spl.kwb: u-boot.img spl/u-boot-spl.bin FORCE
$(call if_changed,mkimage) $(call if_changed,mkimage)

View File

@ -1,5 +1,13 @@
#include <config.h> #include <config.h>
#ifdef CONFIG_MACH_SUN50I_H6
#define BL31_ADDR 0x104000
#define SCP_ADDR 0x114000
#else
#define BL31_ADDR 0x44000
#define SCP_ADDR 0x50000
#endif
/ { / {
aliases { aliases {
mmc1 = &mmc2; mmc1 = &mmc2;
@ -14,9 +22,11 @@
u-boot-sunxi-with-spl { u-boot-sunxi-with-spl {
filename = "u-boot-sunxi-with-spl.bin"; filename = "u-boot-sunxi-with-spl.bin";
pad-byte = <0xff>; pad-byte = <0xff>;
blob { blob {
filename = "spl/sunxi-spl.bin"; filename = "spl/sunxi-spl.bin";
}; };
#ifdef CONFIG_ARM64 #ifdef CONFIG_ARM64
fit { fit {
description = "Configuration to load ATF before U-Boot"; description = "Configuration to load ATF before U-Boot";
@ -27,6 +37,7 @@
uboot { uboot {
description = "U-Boot (64-bit)"; description = "U-Boot (64-bit)";
type = "standalone"; type = "standalone";
os = "u-boot";
arch = "arm64"; arch = "arm64";
compression = "none"; compression = "none";
load = <0x4a000000>; load = <0x4a000000>;
@ -34,24 +45,35 @@
u-boot-nodtb { u-boot-nodtb {
}; };
}; };
atf { atf {
description = "ARM Trusted Firmware"; description = "ARM Trusted Firmware";
type = "firmware"; type = "firmware";
os = "arm-trusted-firmware";
arch = "arm64"; arch = "arm64";
compression = "none"; compression = "none";
/* TODO: Do this with an overwrite in this board's dtb? */ load = <BL31_ADDR>;
#ifdef CONFIG_MACH_SUN50I_H6 entry = <BL31_ADDR>;
load = <0x104000>;
entry = <0x104000>;
#else
load = <0x44000>;
entry = <0x44000>;
#endif
atf-bl31 { atf-bl31 {
filename = "bl31.bin";
missing-msg = "atf-bl31-sunxi"; missing-msg = "atf-bl31-sunxi";
}; };
}; };
scp {
description = "SCP firmware";
type = "firmware";
arch = "or1k";
compression = "none";
load = <SCP_ADDR>;
scp {
filename = "scp.bin";
missing-msg = "scp-sunxi";
};
};
@fdt-SEQ { @fdt-SEQ {
description = "NAME"; description = "NAME";
type = "flat_dt"; type = "flat_dt";
@ -61,10 +83,11 @@
configurations { configurations {
default = "config-1"; default = "config-1";
@config-SEQ { @config-SEQ {
description = "NAME"; description = "NAME";
firmware = "uboot"; firmware = "atf";
loadables = "atf"; loadables = "scp", "uboot";
fdt = "fdt-SEQ"; fdt = "fdt-SEQ";
}; };
}; };

View File

@ -16,6 +16,7 @@
#define SOCID_A64 0x1689 #define SOCID_A64 0x1689
#define SOCID_H3 0x1680 #define SOCID_H3 0x1680
#define SOCID_V3S 0x1681
#define SOCID_H5 0x1718 #define SOCID_H5 0x1718
#define SOCID_R40 0x1701 #define SOCID_R40 0x1701

View File

@ -63,6 +63,8 @@ enum {
MBUS_PORT_CSI = 5, MBUS_PORT_CSI = 5,
MBUS_PORT_NAND = 6, MBUS_PORT_NAND = 6,
MBUS_PORT_SS = 7, MBUS_PORT_SS = 7,
MBUS_PORT_DE_V3S = 8,
MBUS_PORT_DE_CFD_V3S = 9,
MBUS_PORT_TS = 8, MBUS_PORT_TS = 8,
MBUS_PORT_DI = 9, MBUS_PORT_DI = 9,
MBUS_PORT_DE = 10, 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); 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) static void mctl_set_master_priority_a64(void)
{ {
struct sunxi_mctl_com_reg * const mctl_com = struct sunxi_mctl_com_reg * const mctl_com =
@ -231,6 +256,9 @@ static void mctl_set_master_priority(uint16_t socid)
case SOCID_H3: case SOCID_H3:
mctl_set_master_priority_h3(); mctl_set_master_priority_h3();
return; return;
case SOCID_V3S:
mctl_set_master_priority_v3s();
return;
case SOCID_A64: case SOCID_A64:
mctl_set_master_priority_a64(); mctl_set_master_priority_a64();
return; 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) static void mctl_set_cr(uint16_t socid, struct dram_para *para)
{ {
struct sunxi_mctl_com_reg * const mctl_com = 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_DIV(1) |
CCM_DRAMCLK_CFG_SRC_PLL11 | CCM_DRAMCLK_CFG_SRC_PLL11 |
CCM_DRAMCLK_CFG_UPD); 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); clock_set_pll5(CONFIG_DRAM_CLK * 2 * 1000000, false);
clrsetbits_le32(&ccm->dram_clk_cfg, clrsetbits_le32(&ccm->dram_clk_cfg,
CCM_DRAMCLK_CFG_DIV_MASK | 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 */ /* dphy & aphy phase select 270 degree */
clrsetbits_le32(&mctl_ctl->pgcr[2], (0x3 << 10) | (0x3 << 8), clrsetbits_le32(&mctl_ctl->pgcr[2], (0x3 << 10) | (0x3 << 8),
(0x1 << 10) | (0x2 << 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) { } else if (socid == SOCID_A64 || socid == SOCID_H5) {
/* dphy & aphy phase select ? */ /* dphy & aphy phase select ? */
clrsetbits_le32(&mctl_ctl->pgcr[2], (0x3 << 10) | (0x3 << 8), 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); mctl_set_bit_delays(para);
udelay(50); 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_h3_zq_calibration_quirk(para);
mctl_phy_init(PIR_PLLINIT | PIR_DCAL | PIR_PHYRST | 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); udelay(10);
/* set PGCR3, CKE polarity */ /* set PGCR3, CKE polarity */
if (socid == SOCID_H3) if (socid == SOCID_H3 || socid == SOCID_V3S)
writel(0x00aa0060, &mctl_ctl->pgcr[3]); writel(0x00aa0060, &mctl_ctl->pgcr[3]);
else if (socid == SOCID_A64 || socid == SOCID_H5 || socid == SOCID_R40) else if (socid == SOCID_A64 || socid == SOCID_H5 || socid == SOCID_R40)
writel(0xc0aa0060, &mctl_ctl->pgcr[3]); 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, 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 \ #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 }, \
{ 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_read_delays = SUN8I_H3_DX_READ_DELAYS,
.dx_write_delays = SUN8I_H3_DX_WRITE_DELAYS, .dx_write_delays = SUN8I_H3_DX_WRITE_DELAYS,
.ac_delays = SUN8I_H3_AC_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) #elif defined(CONFIG_MACH_SUN8I_R40)
.dx_read_delays = SUN8I_R40_DX_READ_DELAYS, .dx_read_delays = SUN8I_R40_DX_READ_DELAYS,
.dx_write_delays = SUN8I_R40_DX_WRITE_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 */ /* Currently we cannot support R40 with dual rank memory */
para.dual_rank = 0; para.dual_rank = 0;
#elif defined(CONFIG_MACH_SUN8I_V3S) #elif defined(CONFIG_MACH_SUN8I_V3S)
/* TODO: set delays and mbus priority for V3s */ uint16_t socid = SOCID_V3S;
uint16_t socid = SOCID_H3;
#elif defined(CONFIG_MACH_SUN50I) #elif defined(CONFIG_MACH_SUN50I)
uint16_t socid = SOCID_A64; uint16_t socid = SOCID_A64;
#elif defined(CONFIG_MACH_SUN50I_H5) #elif defined(CONFIG_MACH_SUN50I_H5)

View File

@ -14,8 +14,12 @@ Quick Start / Overview
- Build the ARM Trusted Firmware binary (see "ARM Trusted Firmware (ATF)" below) - Build the ARM Trusted Firmware binary (see "ARM Trusted Firmware (ATF)" below)
$ cd /src/arm-trusted-firmware $ cd /src/arm-trusted-firmware
$ make PLAT=sun50i_a64 DEBUG=1 bl31 $ 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) - Build U-Boot (see "SPL/U-Boot" below)
$ export BL31=/path/to/bl31.bin $ export BL31=/path/to/bl31.bin
$ export SCP=/src/crust/build/scp/scp.bin
$ make pine64_plus_defconfig && make -j5 $ make pine64_plus_defconfig && make -j5
- Transfer to an uSD card (see "microSD card" below) - Transfer to an uSD card (see "microSD card" below)
$ dd if=u-boot-sunxi-with-spl.bin of=/dev/sdx bs=8k seek=1 $ 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 Building the firmware
===================== =====================
The Allwinner A64/H5 firmware consists of three parts: U-Boot's SPL, an The Allwinner A64/H5/H6 firmware consists of several parts: U-Boot's SPL,
ARM Trusted Firmware (ATF) build and the U-Boot proper. ARM Trusted Firmware (ATF), optional System Control Processor (SCP) firmware
The SPL will load both ATF and U-Boot proper along with the right device (e.g. Crust), and the U-Boot proper.
tree blob (.dtb) and will pass execution to ATF (in EL3), which in turn will
drop into the U-Boot proper (in EL2). The SPL will load all of the other firmware binaries into RAM, along with the
As the ATF binary will become part of the U-Boot image file, you will need right device tree blob (.dtb), and will pass execution to ATF (in EL3). If SCP
to build it first. 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) 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 or if you can't be bothered with building ATF, there are known working
binaries in the firmware repository[3], purely for convenience reasons. 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 SPL/U-Boot
------------ ------------
Both U-Boot proper and the SPL are using the 64-bit mode. As the boot ROM Both U-Boot proper and the SPL are using the 64-bit mode. As the boot ROM

View File

@ -465,9 +465,7 @@ config SPL_FIT_IMAGE_TINY
Enable this to reduce the size of the FIT image loading code Enable this to reduce the size of the FIT image loading code
in SPL, if space for the SPL binary is very tight. in SPL, if space for the SPL binary is very tight.
This removes the detection of image types (which forces the This skips the recording of each loaded payload
first image to be treated as having a U-Boot style calling
convention) and skips the recording of each loaded payload
(i.e. loadable) into the FDT (modifying the loaded FDT to (i.e. loadable) into the FDT (modifying the loaded FDT to
ensure this information is available to the next image ensure this information is available to the next image
invoked). invoked).

View File

@ -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) 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) #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 #else
return fit_image_get_os(fit, noffset, os); return fit_image_get_os(fit, noffset, os);
#endif #endif

View File

@ -19,6 +19,8 @@ CONFIG_CMD_USB_MASS_STORAGE=y
CONFIG_SCSI_AHCI=y CONFIG_SCSI_AHCI=y
CONFIG_DFU_RAM=y CONFIG_DFU_RAM=y
CONFIG_FASTBOOT_CMD_OEM_FORMAT=y CONFIG_FASTBOOT_CMD_OEM_FORMAT=y
CONFIG_PHY_MICREL=y
CONFIG_PHY_MICREL_KSZ90X1=y
CONFIG_PHY_REALTEK=y CONFIG_PHY_REALTEK=y
CONFIG_ETH_DESIGNWARE=y CONFIG_ETH_DESIGNWARE=y
CONFIG_RGMII=y CONFIG_RGMII=y

View File

@ -11,5 +11,6 @@ CONFIG_SPL_SPI_SUNXI=y
CONFIG_DEFAULT_DEVICE_TREE="sun50i-h6-pine-h64" CONFIG_DEFAULT_DEVICE_TREE="sun50i-h6-pine-h64"
# CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set
CONFIG_SUN8I_EMAC=y CONFIG_SUN8I_EMAC=y
CONFIG_MACPWR="PC16"
CONFIG_USB_EHCI_HCD=y CONFIG_USB_EHCI_HCD=y
CONFIG_USB_OHCI_HCD=y CONFIG_USB_OHCI_HCD=y

View File

@ -7,6 +7,7 @@ CONFIG_DRAM_ZQ=3881949
CONFIG_MMC_SUNXI_SLOT_EXTRA=2 CONFIG_MMC_SUNXI_SLOT_EXTRA=2
CONFIG_USB1_VBUS_PIN="PL7" CONFIG_USB1_VBUS_PIN="PL7"
CONFIG_I2C0_ENABLE=y CONFIG_I2C0_ENABLE=y
CONFIG_PREBOOT="setenv usb_pgood_delay 2000; usb start"
CONFIG_DEFAULT_DEVICE_TREE="sun50i-a64-teres-i" CONFIG_DEFAULT_DEVICE_TREE="sun50i-a64-teres-i"
CONFIG_DM_REGULATOR=y CONFIG_DM_REGULATOR=y
CONFIG_DM_REGULATOR_FIXED=y CONFIG_DM_REGULATOR_FIXED=y

View File

@ -36,7 +36,7 @@ alias marex Marek Vasut <marex@denx.de>
alias mariosix Mario Six <mario.six@gdsys.cc> alias mariosix Mario Six <mario.six@gdsys.cc>
alias masahiro Masahiro Yamada <yamada.masahiro@socionext.com> alias masahiro Masahiro Yamada <yamada.masahiro@socionext.com>
alias mateusz Mateusz Kulikowski <mateusz.kulikowski@gmail.com> alias mateusz Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
alias maxime Maxime Ripard <maxime.ripard@free-electrons.com> alias maxime Maxime Ripard <mripard@kernel.org>
alias mbrugger Matthias Brugger <mbrugger@suse.com> alias mbrugger Matthias Brugger <mbrugger@suse.com>
alias monstr Michal Simek <monstr@monstr.eu> alias monstr Michal Simek <monstr@monstr.eu>
alias prom Minkyu Kang <mk7.kang@samsung.com> alias prom Minkyu Kang <mk7.kang@samsung.com>

View File

@ -29,6 +29,7 @@
#include <net.h> #include <net.h>
#include <reset.h> #include <reset.h>
#include <dt-bindings/pinctrl/sun4i-a10.h> #include <dt-bindings/pinctrl/sun4i-a10.h>
#include <wait_bit.h>
#if CONFIG_IS_ENABLED(DM_GPIO) #if CONFIG_IS_ENABLED(DM_GPIO)
#include <asm-generic/gpio.h> #include <asm-generic/gpio.h>
#endif #endif
@ -40,6 +41,11 @@
#define MDIO_CMD_MII_PHY_REG_ADDR_SHIFT 4 #define MDIO_CMD_MII_PHY_REG_ADDR_SHIFT 4
#define MDIO_CMD_MII_PHY_ADDR_MASK 0x0001f000 #define MDIO_CMD_MII_PHY_ADDR_MASK 0x0001f000
#define MDIO_CMD_MII_PHY_ADDR_SHIFT 12 #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_TX_DESCR_NUM 32
#define CONFIG_RX_DESCR_NUM 32 #define CONFIG_RX_DESCR_NUM 32
@ -84,15 +90,32 @@
/* H3/A64 EMAC Register's offset */ /* H3/A64 EMAC Register's offset */
#define EMAC_CTL0 0x00 #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 0x04
#define EMAC_CTL1_SOFT_RST BIT(0)
#define EMAC_CTL1_BURST_LEN_SHIFT 24
#define EMAC_INT_STA 0x08 #define EMAC_INT_STA 0x08
#define EMAC_INT_EN 0x0c #define EMAC_INT_EN 0x0c
#define EMAC_TX_CTL0 0x10 #define EMAC_TX_CTL0 0x10
#define EMAC_TX_CTL0_TX_EN BIT(31)
#define EMAC_TX_CTL1 0x14 #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_FLOW_CTL 0x1c
#define EMAC_TX_DMA_DESC 0x20 #define EMAC_TX_DMA_DESC 0x20
#define EMAC_RX_CTL0 0x24 #define EMAC_RX_CTL0 0x24
#define EMAC_RX_CTL0_RX_EN BIT(31)
#define EMAC_RX_CTL1 0x28 #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_RX_DMA_DESC 0x34
#define EMAC_MII_CMD 0x48 #define EMAC_MII_CMD 0x48
#define EMAC_MII_DATA 0x4c #define EMAC_MII_DATA 0x4c
@ -104,6 +127,13 @@
#define EMAC_RX_DMA_STA 0xc0 #define EMAC_RX_DMA_STA 0xc0
#define EMAC_RX_CUR_DESC 0xc4 #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; DECLARE_GLOBAL_DATA_PTR;
enum emac_variant { enum emac_variant {
@ -116,7 +146,7 @@ enum emac_variant {
struct emac_dma_desc { struct emac_dma_desc {
u32 status; u32 status;
u32 st; u32 ctl_size;
u32 buf_addr; u32 buf_addr;
u32 next; u32 next;
} __aligned(ARCH_DMA_MINALIGN); } __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 udevice *dev = bus->priv;
struct emac_eth_dev *priv = dev_get_priv(dev); struct emac_eth_dev *priv = dev_get_priv(dev);
ulong start; u32 mii_cmd;
u32 miiaddr = 0; int ret;
int timeout = CONFIG_MDIO_TIMEOUT;
miiaddr &= ~MDIO_CMD_MII_WRITE; mii_cmd = (reg << MDIO_CMD_MII_PHY_REG_ADDR_SHIFT) &
miiaddr &= ~MDIO_CMD_MII_PHY_REG_ADDR_MASK;
miiaddr |= (reg << MDIO_CMD_MII_PHY_REG_ADDR_SHIFT) &
MDIO_CMD_MII_PHY_REG_ADDR_MASK; MDIO_CMD_MII_PHY_REG_ADDR_MASK;
mii_cmd |= (addr << MDIO_CMD_MII_PHY_ADDR_SHIFT) &
miiaddr &= ~MDIO_CMD_MII_PHY_ADDR_MASK;
miiaddr |= (addr << MDIO_CMD_MII_PHY_ADDR_SHIFT) &
MDIO_CMD_MII_PHY_ADDR_MASK; 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); writel(mii_cmd, priv->mac_reg + EMAC_MII_CMD);
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);
};
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, 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 udevice *dev = bus->priv;
struct emac_eth_dev *priv = dev_get_priv(dev); struct emac_eth_dev *priv = dev_get_priv(dev);
ulong start; u32 mii_cmd;
u32 miiaddr = 0;
int ret = -1, timeout = CONFIG_MDIO_TIMEOUT;
miiaddr &= ~MDIO_CMD_MII_PHY_REG_ADDR_MASK; mii_cmd = (reg << MDIO_CMD_MII_PHY_REG_ADDR_SHIFT) &
miiaddr |= (reg << MDIO_CMD_MII_PHY_REG_ADDR_SHIFT) &
MDIO_CMD_MII_PHY_REG_ADDR_MASK; MDIO_CMD_MII_PHY_REG_ADDR_MASK;
mii_cmd |= (addr << MDIO_CMD_MII_PHY_ADDR_SHIFT) &
miiaddr &= ~MDIO_CMD_MII_PHY_ADDR_MASK;
miiaddr |= (addr << MDIO_CMD_MII_PHY_ADDR_SHIFT) &
MDIO_CMD_MII_PHY_ADDR_MASK; 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(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); return wait_for_bit_le32(priv->mac_reg + EMAC_MII_CMD,
while (get_timer(start) < timeout) { MDIO_CMD_MII_BUSY, false,
if (!(readl(priv->mac_reg + EMAC_MII_CMD) & CONFIG_MDIO_TIMEOUT, true);
MDIO_CMD_MII_BUSY)) {
ret = 0;
break;
}
udelay(10);
};
return ret;
} }
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; u32 macid_lo, macid_hi;
macid_lo = mac_id[0] + (mac_id[1] << 8) + (mac_id[2] << 16) + 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); v = readl(priv->mac_reg + EMAC_CTL0);
if (phydev->duplex) if (phydev->duplex)
v |= BIT(0); v |= EMAC_CTL0_FULL_DUPLEX;
else else
v &= ~BIT(0); v &= ~EMAC_CTL0_FULL_DUPLEX;
v &= ~0x0C; v &= ~EMAC_CTL0_SPEED_MASK;
switch (phydev->speed) { switch (phydev->speed) {
case 1000: case 1000:
v |= EMAC_CTL0_SPEED_1000;
break; break;
case 100: case 100:
v |= BIT(2); v |= EMAC_CTL0_SPEED_100;
v |= BIT(3);
break; break;
case 10: case 10:
v |= BIT(3); v |= EMAC_CTL0_SPEED_10;
break; break;
} }
writel(v, priv->mac_reg + EMAC_CTL0); 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; 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) static void rx_descs_init(struct emac_eth_dev *priv)
{ {
struct emac_dma_desc *desc_table_p = &priv->rx_chain[0]; struct emac_dma_desc *desc_table_p = &priv->rx_chain[0];
char *rxbuffs = &priv->rxbuffer[0]; char *rxbuffs = &priv->rxbuffer[0];
struct emac_dma_desc *desc_p; struct emac_dma_desc *desc_p;
u32 idx; int i;
/* flush Rx buffers */ /*
flush_dcache_range((uintptr_t)rxbuffs, (ulong)rxbuffs + * Make sure we don't have dirty cache lines around, which could
RX_TOTAL_BUFSIZE); * 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++) { for (i = 0; i < CONFIG_RX_DESCR_NUM; i++) {
desc_p = &desc_table_p[idx]; desc_p = &desc_table_p[i];
desc_p->buf_addr = (uintptr_t)&rxbuffs[idx * CONFIG_ETH_BUFSIZE] desc_p->buf_addr = (uintptr_t)&rxbuffs[i * CONFIG_ETH_BUFSIZE];
; desc_p->next = (uintptr_t)&desc_table_p[i + 1];
desc_p->next = (uintptr_t)&desc_table_p[idx + 1]; desc_p->ctl_size = CONFIG_ETH_RXSIZE;
desc_p->st |= CONFIG_ETH_RXSIZE; desc_p->status = EMAC_DESC_OWN_DMA;
desc_p->status = BIT(31);
} }
/* Correcting the last pointer of the chain */ /* 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]; struct emac_dma_desc *desc_table_p = &priv->tx_chain[0];
char *txbuffs = &priv->txbuffer[0]; char *txbuffs = &priv->txbuffer[0];
struct emac_dma_desc *desc_p; struct emac_dma_desc *desc_p;
u32 idx; int i;
for (idx = 0; idx < CONFIG_TX_DESCR_NUM; idx++) { for (i = 0; i < CONFIG_TX_DESCR_NUM; i++) {
desc_p = &desc_table_p[idx]; desc_p = &desc_table_p[i];
desc_p->buf_addr = (uintptr_t)&txbuffs[idx * CONFIG_ETH_BUFSIZE] desc_p->buf_addr = (uintptr_t)&txbuffs[i * CONFIG_ETH_BUFSIZE];
; desc_p->next = (uintptr_t)&desc_table_p[i + 1];
desc_p->next = (uintptr_t)&desc_table_p[idx + 1]; desc_p->ctl_size = 0;
desc_p->status = (1 << 31); desc_p->status = 0;
desc_p->st = 0;
} }
/* Correcting the last pointer of the chain */ /* Correcting the last pointer of the chain */
desc_p->next = (uintptr_t)&desc_table_p[0]; desc_p->next = (uintptr_t)&desc_table_p[0];
/* Flush all Tx buffer descriptors */ /* Flush the first TX buffer descriptor we will tell the MAC about. */
flush_dcache_range((uintptr_t)priv->tx_chain, cache_clean_descriptor(desc_table_p);
(uintptr_t)priv->tx_chain +
sizeof(priv->tx_chain));
writel((uintptr_t)&desc_table_p[0], priv->mac_reg + EMAC_TX_DMA_DESC); writel((uintptr_t)&desc_table_p[0], priv->mac_reg + EMAC_TX_DMA_DESC);
priv->tx_currdescnum = 0; 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; struct emac_eth_dev *priv = dev_get_priv(dev);
int timeout = 100; int ret;
reg = readl((priv->mac_reg + EMAC_CTL1)); /* Soft reset MAC */
writel(EMAC_CTL1_SOFT_RST, priv->mac_reg + EMAC_CTL1);
if (!(reg & 0x1)) { ret = wait_for_bit_le32(priv->mac_reg + EMAC_CTL1,
/* Soft reset MAC */ EMAC_CTL1_SOFT_RST, false, 10, true);
setbits_le32((priv->mac_reg + EMAC_CTL1), 0x1); if (ret) {
do { printf("%s: Timeout\n", __func__);
reg = readl(priv->mac_reg + EMAC_CTL1); return ret;
} while ((reg & 0x01) != 0 && (--timeout));
if (!timeout) {
printf("%s: Timeout\n", __func__);
return -1;
}
} }
/* Rewrite mac address after reset */ /* Rewrite mac address after reset */
_sun8i_write_hwaddr(priv, enetaddr); sun8i_eth_write_hwaddr(dev);
v = readl(priv->mac_reg + EMAC_TX_CTL1); /* transmission starts after the full frame arrived in TX DMA FIFO */
/* TX_MD Transmission starts after a full frame located in TX DMA FIFO*/ setbits_le32(priv->mac_reg + EMAC_TX_CTL1, EMAC_TX_CTL1_TX_MD);
v |= BIT(1);
writel(v, priv->mac_reg + EMAC_TX_CTL1);
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 * complete frame has been written to RX DMA FIFO
*/ */
v |= BIT(1); setbits_le32(priv->mac_reg + EMAC_RX_CTL1, EMAC_RX_CTL1_RX_MD);
writel(v, priv->mac_reg + EMAC_RX_CTL1);
/* DMA */ /* DMA burst length */
writel(8 << 24, priv->mac_reg + EMAC_CTL1); writel(8 << EMAC_CTL1_BURST_LEN_SHIFT, priv->mac_reg + EMAC_CTL1);
/* Initialize rx/tx descriptors */ /* Initialize rx/tx descriptors */
rx_descs_init(priv); rx_descs_init(priv);
tx_descs_init(priv); tx_descs_init(priv);
/* PHY Start Up */ /* PHY Start Up */
phy_startup(priv->phydev); ret = phy_startup(priv->phydev);
if (ret)
return ret;
sun8i_adjust_link(priv, priv->phydev); sun8i_adjust_link(priv, priv->phydev);
/* Start RX DMA */ /* Start RX/TX DMA */
v = readl(priv->mac_reg + EMAC_RX_CTL1); setbits_le32(priv->mac_reg + EMAC_RX_CTL1, EMAC_RX_CTL1_RX_DMA_EN |
v |= BIT(30); EMAC_RX_CTL1_RX_ERR_FRM | EMAC_RX_CTL1_RX_RUNT_FRM);
writel(v, priv->mac_reg + EMAC_RX_CTL1); setbits_le32(priv->mac_reg + EMAC_TX_CTL1, EMAC_TX_CTL1_TX_DMA_EN);
/* Start TX DMA */
v = readl(priv->mac_reg + EMAC_TX_CTL1);
v |= BIT(30);
writel(v, priv->mac_reg + EMAC_TX_CTL1);
/* Enable RX/TX */ /* Enable RX/TX */
setbits_le32(priv->mac_reg + EMAC_RX_CTL0, BIT(31)); setbits_le32(priv->mac_reg + EMAC_RX_CTL0, EMAC_RX_CTL0_RX_EN);
setbits_le32(priv->mac_reg + EMAC_TX_CTL0, BIT(31)); setbits_le32(priv->mac_reg + EMAC_TX_CTL0, EMAC_TX_CTL0_TX_EN);
return 0; return 0;
} }
@ -558,88 +582,71 @@ static int parse_phy_pins(struct udevice *dev)
return 0; 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; u32 status, desc_num = priv->rx_currdescnum;
struct emac_dma_desc *desc_p = &priv->rx_chain[desc_num]; struct emac_dma_desc *desc_p = &priv->rx_chain[desc_num];
int length = -EAGAIN; uintptr_t data_start = (uintptr_t)desc_p->buf_addr;
int good_packet = 1; int length;
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;
/* Invalidate entire buffer descriptor */ /* Invalidate entire buffer descriptor */
invalidate_dcache_range(desc_start, desc_end); cache_inv_descriptor(desc_p);
status = desc_p->status; status = desc_p->status;
/* Check for DMA own bit */ /* Check for DMA own bit */
if (!(status & BIT(31))) { if (status & EMAC_DESC_OWN_DMA)
length = (desc_p->status >> 16) & 0x3FFF; return -EAGAIN;
if (length < 0x40) { length = (status >> 16) & 0x3fff;
good_packet = 0;
debug("RX: Bad Packet (runt)\n");
}
data_end = data_start + length; /* make sure we read from DRAM, not our cache */
/* Invalidate received data */ invalidate_dcache_range(data_start,
invalidate_dcache_range(rounddown(data_start, data_start + roundup(length, ARCH_DMA_MINALIGN));
ARCH_DMA_MINALIGN),
roundup(data_end, if (status & EMAC_DESC_RX_ERROR_MASK) {
ARCH_DMA_MINALIGN)); debug("RX: packet error: 0x%x\n",
if (good_packet) { status & EMAC_DESC_RX_ERROR_MASK);
if (length > CONFIG_ETH_RXSIZE) { return 0;
printf("Received packet is too big (len=%d)\n",
length);
return -EMSGSIZE;
}
*packetp = (uchar *)(ulong)desc_p->buf_addr;
return length;
}
} }
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; return length;
} }
static int _sun8i_emac_eth_send(struct emac_eth_dev *priv, void *packet, static int sun8i_emac_eth_send(struct udevice *dev, void *packet, int length)
int len)
{ {
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]; 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_start = (uintptr_t)desc_p->buf_addr;
uintptr_t data_end = data_start + uintptr_t data_end = data_start +
roundup(len, ARCH_DMA_MINALIGN); roundup(length, ARCH_DMA_MINALIGN);
/* Invalidate entire buffer descriptor */ desc_p->ctl_size = length | EMAC_DESC_CHAIN_SECOND;
invalidate_dcache_range(desc_start, desc_end);
desc_p->st = len; memcpy((void *)data_start, packet, length);
/* Mandatory undocumented bit */
desc_p->st |= BIT(24);
memcpy((void *)data_start, packet, len);
/* Flush data to be sent */ /* Flush data to be sent */
flush_dcache_range(data_start, data_end); flush_dcache_range(data_start, data_end);
/* frame end */ /* frame begin and end */
desc_p->st |= BIT(30); desc_p->ctl_size |= EMAC_DESC_LAST_DESC | EMAC_DESC_FIRST_DESC;
desc_p->st |= BIT(31); desc_p->status = EMAC_DESC_OWN_DMA;
/*frame begin */ /* make sure the MAC reads the actual data from DRAM */
desc_p->st |= BIT(29); cache_clean_descriptor(desc_p);
desc_p->status = BIT(31);
/*Descriptors st and status field has changed, so FLUSH it */
flush_dcache_range(desc_start, desc_end);
/* Move to next Descriptor and wrap around */ /* Move to next Descriptor and wrap around */
if (++desc_num >= CONFIG_TX_DESCR_NUM) 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; priv->tx_currdescnum = desc_num;
/* Start the DMA */ /* Start the DMA */
v = readl(priv->mac_reg + EMAC_TX_CTL1); setbits_le32(priv->mac_reg + EMAC_TX_CTL1, EMAC_TX_CTL1_TX_DMA_START);
v |= BIT(31);/* mandatory */
v |= BIT(30);/* mandatory */ /*
writel(v, priv->mac_reg + EMAC_TX_CTL1); * Since we copied the data above, we return here without waiting
* for the packet to be actually send out.
*/
return 0; 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, static int sun8i_emac_board_setup(struct udevice *dev,
struct emac_eth_dev *priv) struct emac_eth_dev *priv)
{ {
@ -760,40 +761,18 @@ static int sun8i_mdio_init(const char *name, struct udevice *priv)
return mdio_register(bus); return mdio_register(bus);
} }
static int sun8i_emac_eth_start(struct udevice *dev) static int sun8i_eth_free_pkt(struct udevice *dev, uchar *packet,
{ int length)
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)
{ {
struct emac_eth_dev *priv = dev_get_priv(dev); 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; u32 desc_num = priv->rx_currdescnum;
struct emac_dma_desc *desc_p = &priv->rx_chain[desc_num]; 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 */ /* give the current descriptor back to the MAC */
desc_p->status |= BIT(31); desc_p->status |= EMAC_DESC_OWN_DMA;
/* Flush Status field of descriptor */ /* 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. */ /* Move to next desc and wrap-around condition. */
if (++desc_num >= CONFIG_RX_DESCR_NUM) if (++desc_num >= CONFIG_RX_DESCR_NUM)
@ -803,24 +782,17 @@ static int _sun8i_free_pkt(struct emac_eth_dev *priv)
return 0; 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) static void sun8i_emac_eth_stop(struct udevice *dev)
{ {
struct emac_eth_dev *priv = dev_get_priv(dev); struct emac_eth_dev *priv = dev_get_priv(dev);
/* Stop Rx/Tx transmitter */ /* Stop Rx/Tx transmitter */
clrbits_le32(priv->mac_reg + EMAC_RX_CTL0, BIT(31)); clrbits_le32(priv->mac_reg + EMAC_RX_CTL0, EMAC_RX_CTL0_RX_EN);
clrbits_le32(priv->mac_reg + EMAC_TX_CTL0, BIT(31)); clrbits_le32(priv->mac_reg + EMAC_TX_CTL0, EMAC_TX_CTL0_TX_EN);
/* Stop TX DMA */ /* Stop RX/TX DMA */
clrbits_le32(priv->mac_reg + EMAC_TX_CTL1, BIT(30)); 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); phy_shutdown(priv->phydev);
} }
@ -855,47 +827,30 @@ static const struct eth_ops sun8i_emac_eth_ops = {
.stop = sun8i_emac_eth_stop, .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, ret = ofnode_parse_phandle_with_args(dev_ofnode(dev), "phy-handle",
"/soc/ethernet@1c30000"); NULL, 0, 0, &phandle);
if (emac_node < 0) { if (ret)
debug("failed to get emac node\n"); return ret;
return emac_node;
}
ephy_handle = fdtdec_lookup_phandle(gd->fdt_blob,
emac_node, "phy-handle");
/* look for mdio-mux node for internal PHY node */ /* If the PHY node is not a child of the internal MDIO bus, we are
ephy_node = fdt_path_offset(gd->fdt_blob, * using some external PHY.
"/soc/ethernet@1c30000/mdio-mux/mdio@1/ethernet-phy@1"); */
if (ephy_node < 0) { if (!ofnode_device_is_compatible(ofnode_get_parent(phandle.node),
debug("failed to get mdio-mux with internal PHY\n"); "allwinner,sun8i-h3-mdio-internal"))
return ephy_node;
}
/* This is not the phy we are looking for */
if (ephy_node != ephy_handle)
return 0; return 0;
ret = fdt_node_check_compatible(gd->fdt_blob, ephy_node, ret = clk_get_by_index_nodev(phandle.node, 0, &priv->ephy_clk);
"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);
if (ret) { if (ret) {
dev_err(dev, "failed to get EPHY TX clock\n"); dev_err(dev, "failed to get EPHY TX clock\n");
return ret; return ret;
} }
ret = reset_get_by_index_nodev(offset_to_ofnode(ephy_node), 0, ret = reset_get_by_index_nodev(phandle.node, 0, &priv->ephy_rst);
&priv->ephy_rst);
if (ret) { if (ret) {
dev_err(dev, "failed to get EPHY TX reset\n"); dev_err(dev, "failed to get EPHY TX reset\n");
return ret; return ret;
@ -987,7 +942,7 @@ static int sun8i_emac_eth_ofdata_to_platdata(struct udevice *dev)
} }
if (priv->variant == H3_EMAC) { if (priv->variant == H3_EMAC) {
ret = sun8i_get_ephy_nodes(dev, priv); ret = sun8i_handle_internal_phy(dev, priv);
if (ret) if (ret)
return ret; return ret;
} }

View File

@ -385,8 +385,8 @@ You can create config nodes in a similar way:
default = "@config-DEFAULT-SEQ"; default = "@config-DEFAULT-SEQ";
@config-SEQ { @config-SEQ {
description = "NAME"; description = "NAME";
firmware = "uboot"; firmware = "atf";
loadables = "atf"; loadables = "uboot";
fdt = "fdt-SEQ"; fdt = "fdt-SEQ";
}; };
}; };

View File

@ -73,8 +73,8 @@ class Entry_fit(Entry):
default = "@config-DEFAULT-SEQ"; default = "@config-DEFAULT-SEQ";
@config-SEQ { @config-SEQ {
description = "NAME"; description = "NAME";
firmware = "uboot"; firmware = "atf";
loadables = "atf"; loadables = "uboot";
fdt = "fdt-SEQ"; fdt = "fdt-SEQ";
}; };
}; };
@ -205,10 +205,10 @@ class Entry_fit(Entry):
b'SEQ', tools.ToBytes(str(seq + 1))) b'SEQ', tools.ToBytes(str(seq + 1)))
fsw.property(pname, val) fsw.property(pname, val)
# Add data for 'fdt' nodes (but not 'config') # Add data for 'fdt' nodes (but not 'config')
if depth == 1 and in_images: if depth == 1 and in_images:
fsw.property('data', fsw.property('data',
tools.ReadFile(fname)) tools.ReadFile(fname))
else: else:
if self._fdts is None: if self._fdts is None:
if self._fit_list_prop: if self._fit_list_prop:

19
tools/binman/etype/scp.py Normal file
View File

@ -0,0 +1,19 @@
# SPDX-License-Identifier: GPL-2.0+
# Copyright 2020 Samuel Holland <samuel@sholland.org>
#
# 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

View File

@ -75,6 +75,7 @@ FSP_M_DATA = b'fsp_m'
FSP_S_DATA = b'fsp_s' FSP_S_DATA = b'fsp_s'
FSP_T_DATA = b'fsp_t' FSP_T_DATA = b'fsp_t'
ATF_BL31_DATA = b'bl31' ATF_BL31_DATA = b'bl31'
SCP_DATA = b'scp'
TEST_FDT1_DATA = b'fdt1' TEST_FDT1_DATA = b'fdt1'
TEST_FDT2_DATA = b'test-fdt2' TEST_FDT2_DATA = b'test-fdt2'
ENV_DATA = b'var1=1\nvar2="2"' ENV_DATA = b'var1=1\nvar2="2"'
@ -175,6 +176,7 @@ class TestFunctional(unittest.TestCase):
TestFunctional._MakeInputFile('compress', COMPRESS_DATA) TestFunctional._MakeInputFile('compress', COMPRESS_DATA)
TestFunctional._MakeInputFile('bl31.bin', ATF_BL31_DATA) TestFunctional._MakeInputFile('bl31.bin', ATF_BL31_DATA)
TestFunctional._MakeInputFile('scp.bin', SCP_DATA)
# Add a few .dtb files for testing # Add a few .dtb files for testing
TestFunctional._MakeInputFile('%s/test-fdt1.dtb' % TEST_FDT_SUBDIR, TestFunctional._MakeInputFile('%s/test-fdt1.dtb' % TEST_FDT_SUBDIR,
@ -3578,6 +3580,11 @@ class TestFunctional(unittest.TestCase):
data = self._DoReadFile('169_atf_bl31.dts') data = self._DoReadFile('169_atf_bl31.dts')
self.assertEqual(ATF_BL31_DATA, data[:len(ATF_BL31_DATA)]) 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): def testFitFdt(self):
"""Test an image with an FIT with multiple FDT images""" """Test an image with an FIT with multiple FDT images"""
def _CheckFdt(seq, expected_data): def _CheckFdt(seq, expected_data):

View File

@ -13,3 +13,7 @@ Firmware and build with BL31=/path/to/bl31.bin
atf-bl31-sunxi: atf-bl31-sunxi:
Please read the section on ARM Trusted Firmware (ATF) in Please read the section on ARM Trusted Firmware (ATF) in
board/sunxi/README.sunxi64 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

View File

@ -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";
};
};
};