- 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 || \
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,

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) \
-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)

View file

@ -1,5 +1,13 @@
#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 {
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 = <BL31_ADDR>;
entry = <BL31_ADDR>;
atf-bl31 {
filename = "bl31.bin";
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 {
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";
};
};

View file

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

View file

@ -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)

View file

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

View file

@ -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).

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)
{
#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

View file

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

View file

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

View file

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

View file

@ -36,7 +36,7 @@ alias marex Marek Vasut <marex@denx.de>
alias mariosix Mario Six <mario.six@gdsys.cc>
alias masahiro Masahiro Yamada <yamada.masahiro@socionext.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 monstr Michal Simek <monstr@monstr.eu>
alias prom Minkyu Kang <mk7.kang@samsung.com>

View file

@ -29,6 +29,7 @@
#include <net.h>
#include <reset.h>
#include <dt-bindings/pinctrl/sun4i-a10.h>
#include <wait_bit.h>
#if CONFIG_IS_ENABLED(DM_GPIO)
#include <asm-generic/gpio.h>
#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;
}

View file

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

View file

@ -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:

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_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):

View file

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

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