Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/test-build-aarch64.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ jobs:
- name: Select config
run: |
cp ${{inputs.config-file}} .config
if [ -n "$EXTRA_CFLAGS" ]; then
printf 'CFLAGS_EXTRA+=%s\n' "$EXTRA_CFLAGS" >> .config
fi

- name: Build tools
run: |
Expand Down
7 changes: 7 additions & 0 deletions .github/workflows/test-configs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -774,6 +774,13 @@ jobs:
make-args: ZYNQMP_FSBL=1 ZYNQMP_PSU_INIT_DIR=hal/board/zynqmp DISK_SDCARD=1
psu-init-stub: true

zynqmp_phy_test:
uses: ./.github/workflows/test-build-aarch64.yml
with:
arch: aarch64
config-file: ./config/examples/zynqmp.config
make-args: CFLAGS_EXTRA=-DWOLFBOOT_ZYNQMP_PHY_INIT

zynq7000_test:
uses: ./.github/workflows/test-build.yml
with:
Expand Down
20 changes: 20 additions & 0 deletions config/examples/zynqmp.config
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,23 @@ CFLAGS_EXTRA+=-DWOLFBOOT_SHA_BLOCK_SIZE=4096

# QSPI force IO mode (default is faster DMA mode)
#CFLAGS_EXTRA+=-DGQSPI_MODE_IO

# Optional: Ethernet PHY init over GEM MDIO.
# Replays a board-specific "mii"/"mw" register sequence (normally run from
# U-Boot) so the PHY is configured before the OS starts. wolfBoot has no
# network stack; this drives only the MDIO management plane.
#CFLAGS_EXTRA+=-DWOLFBOOT_ZYNQMP_PHY_INIT
# Defaults (hal/zynq.h) target the ZCU102 PHY (DP83867 at MDIO 0x0C on GEM3)
# and just read the PHY ID. For another board, keep its values in a small
# header and select it with one line (this keeps the efficient step array):
#CFLAGS_EXTRA+=-DZYNQMP_PHY_INIT_HEADER='"myboard_phy.h"'
# where myboard_phy.h #defines any of:
# ZYNQMP_GEM_BASE GEM register base (default 0xFF0E0000 = GEM3)
# ZYNQMP_PHY_ADDR PHY MDIO address (default 0x0C)
# ZYNQMP_PHY_GPIO_ADDR PL register poked by a GPIO step; 0 disables it
# ZYNQMP_GEM_MDC_DIV NWCFG MDC divisor selector (default 5 = pclk/96)
# ZYNQMP_PHY_INIT_STEPS the {op,arg0,arg1} sequence rows
# Simple scalars can also be set directly, e.g.:
#CFLAGS_EXTRA+=-DZYNQMP_GEM_BASE=0xFF0B0000UL
# A PHY behind the PL only responds once the FPGA bitstream is loaded, so the
# boot image (BOOT.BIN) must include it (bootgen [destination_device=pl]).
22 changes: 22 additions & 0 deletions config/examples/zynqmp_sdcard.config
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,25 @@ WOLFBOOT_SECTOR_SIZE=0x1000
#CFLAGS_EXTRA+=-DDEBUG_GPT
# Disk read/write test at boot
#CFLAGS_EXTRA+=-DDISK_TEST

# ============================================================================
# Optional: Ethernet PHY init over GEM MDIO
# ============================================================================
# Replays a board-specific "mii"/"mw" register sequence (normally run from
# U-Boot) so the PHY is configured before the OS starts. wolfBoot has no
# network stack; this drives only the MDIO management plane.
#CFLAGS_EXTRA+=-DWOLFBOOT_ZYNQMP_PHY_INIT
# Defaults (hal/zynq.h) target the ZCU102 PHY (DP83867 at MDIO 0x0C on GEM3)
# and just read the PHY ID. For another board, keep its values in a small
# header and select it with one line (this keeps the efficient step array):
#CFLAGS_EXTRA+=-DZYNQMP_PHY_INIT_HEADER='"myboard_phy.h"'
# where myboard_phy.h #defines any of:
# ZYNQMP_GEM_BASE GEM register base (default 0xFF0E0000 = GEM3)
# ZYNQMP_PHY_ADDR PHY MDIO address (default 0x0C)
# ZYNQMP_PHY_GPIO_ADDR PL register poked by a GPIO step; 0 disables it
# ZYNQMP_GEM_MDC_DIV NWCFG MDC divisor selector (default 5 = pclk/96)
# ZYNQMP_PHY_INIT_STEPS the {op,arg0,arg1} sequence rows
# Simple scalars can also be set directly, e.g.:
#CFLAGS_EXTRA+=-DZYNQMP_GEM_BASE=0xFF0B0000UL
# A PHY behind the PL only responds once the FPGA bitstream is loaded, so the
# boot image (BOOT.BIN) must include it (bootgen [destination_device=pl]).
4 changes: 4 additions & 0 deletions docs/Targets.md
Original file line number Diff line number Diff line change
Expand Up @@ -3795,6 +3795,10 @@ Key configuration options:
- `HASH=SHA3` - SHA3-384 hashing
- `ELF=1` - ELF loading support

### Ethernet PHY init (optional)

Opt-in (off by default), wolfBoot can replay a board's U-Boot Ethernet PHY register sequence over the GEM MDIO management plane so the PHY is ready before the OS runs. Enable with `CFLAGS_EXTRA+=-DWOLFBOOT_ZYNQMP_PHY_INIT`. The default targets the ZCU102 on-board PHY (TI DP83867 at MDIO `0x0C` on GEM3, `0xFF0E0000`) and just reads the PHY ID as a diagnostic (printed with `DEBUG_UART=1`). A board supplies its own sequence by keeping its values in a small header selected with one line, `CFLAGS_EXTRA+=-DZYNQMP_PHY_INIT_HEADER='"myboard_phy.h"'`, where that header `#define`s any of `ZYNQMP_GEM_BASE`, `ZYNQMP_PHY_ADDR`, `ZYNQMP_PHY_GPIO_ADDR`, `ZYNQMP_GEM_MDC_DIV`, and the `{op, arg0, arg1}` step array `ZYNQMP_PHY_INIT_STEPS`; scalars can also be set directly with `-D`, and anything omitted falls back to the ZCU102 defaults (see `hal/zynq.h` and the commented example in `config/examples/zynqmp.config`). Where the PHY is behind the PL, the boot image must include the FPGA bitstream (bootgen `[destination_device=pl] system.bit`) or the transactions are no-ops.

### Building with Xilinx tools (Vitis IDE)

See [IDE/XilinxSDK/README.md](/IDE/XilinxSDK/README.md) for using Xilinx IDE
Expand Down
104 changes: 104 additions & 0 deletions hal/zynq.c
Original file line number Diff line number Diff line change
Expand Up @@ -2076,6 +2076,106 @@ static void zynqmp_enable_cci(void)
}
#endif /* ZYNQMP_ENABLE_CCI */

#ifdef WOLFBOOT_ZYNQMP_PHY_INIT
/* Minimal GEM MDIO management engine, used only to run a board-specific PHY
* init sequence before boot. See hal/zynq.h for the register/config macros. */

static int gem_mdio_wait_idle(void)
{
uint32_t spin;
for (spin = 0; spin < 100000; spin++) {
if (GEM_NWSR & GEM_NWSR_PHY_IDLE) {
return 0;
}
}
return -1;
}

static int gem_mdio_write(uint8_t phy, uint8_t reg, uint16_t val)
{
if (gem_mdio_wait_idle() != 0) {
return -1;
}
GEM_PHYMNTNC = GEM_PHYMNTNC_CLAUSE22 | GEM_PHYMNTNC_OP_W
| (((uint32_t)phy & 0x1FU) << 23)
| (((uint32_t)reg & 0x1FU) << 18)
| (uint32_t)val;
if (gem_mdio_wait_idle() != 0) {
return -2;
}
return 0;
}

static int gem_mdio_read(uint8_t phy, uint8_t reg, uint16_t *out)
{
if (gem_mdio_wait_idle() != 0) {
return -1;
}
GEM_PHYMNTNC = GEM_PHYMNTNC_CLAUSE22 | GEM_PHYMNTNC_OP_R
| (((uint32_t)phy & 0x1FU) << 23)
| (((uint32_t)reg & 0x1FU) << 18);
if (gem_mdio_wait_idle() != 0) {
return -2;
}
*out = (uint16_t)(GEM_PHYMNTNC & 0xFFFFU);
return 0;
}

/* Run the configurable PHY init sequence (default: the board's U-Boot flow).
* Best-effort: MDIO errors are reported but never block boot. */
static void zynq_phy_init(void)
{
static const struct {
uint8_t op;
uint8_t arg0;
uint16_t arg1;
} steps[] = { ZYNQMP_PHY_INIT_STEPS };
uint32_t i;
uint16_t rval;
int ret;

wolfBoot_printf("ZynqMP PHY init (addr 0x%02x)\n", (int)ZYNQMP_PHY_ADDR);

/* Enable the MDIO management port with a safe MDC divisor. */
GEM_NWCFG = (GEM_NWCFG & ~(0x7UL << GEM_NWCFG_MDCDIV_SHIFT))
| (((uint32_t)ZYNQMP_GEM_MDC_DIV & 0x7U) << GEM_NWCFG_MDCDIV_SHIFT);
GEM_NWCTRL |= GEM_NWCTRL_MDEN;

for (i = 0; i < (sizeof(steps) / sizeof(steps[0])); i++) {
switch (steps[i].op) {
case ZYNQMP_PHY_OP_GPIO:
if (ZYNQMP_PHY_GPIO_ADDR != 0) {
*((volatile uint32_t*)ZYNQMP_PHY_GPIO_ADDR) =
(uint32_t)steps[i].arg1;
hal_delay_ms(1);
}
break;
case ZYNQMP_PHY_OP_WR:
ret = gem_mdio_write(ZYNQMP_PHY_ADDR, steps[i].arg0, steps[i].arg1);
if (ret != 0) {
wolfBoot_printf("PHY write reg 0x%02x failed (%d)\n",
(int)steps[i].arg0, ret);
}
break;
case ZYNQMP_PHY_OP_RD:
rval = 0;
ret = gem_mdio_read(ZYNQMP_PHY_ADDR, steps[i].arg0, &rval);
if (ret != 0) {
wolfBoot_printf("PHY read reg 0x%02x failed (%d)\n",
(int)steps[i].arg0, ret);
}
else {
wolfBoot_printf("PHY reg 0x%02x = 0x%04x\n",
(int)steps[i].arg0, (int)rval);
}
break;
default:
break;
}
}
}
#endif /* WOLFBOOT_ZYNQMP_PHY_INIT */

/* public HAL functions */
void hal_init(void)
{
Expand Down Expand Up @@ -2110,6 +2210,10 @@ void hal_init(void)
wolfBoot_printf("Build: %s %s\n", __DATE__, __TIME__);
#endif

#ifdef WOLFBOOT_ZYNQMP_PHY_INIT
zynq_phy_init();
#endif

#if defined(EXT_FLASH) && (EXT_FLASH == 1)
qspi_init();
#endif
Expand Down
79 changes: 79 additions & 0 deletions hal/zynq.h
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,85 @@
#define SD_CONFIG_REG2_SD0_SLOTTYPE_MASK 0x00003000UL


#ifdef WOLFBOOT_ZYNQMP_PHY_INIT
/* Ethernet PHY init over the GEM MDIO management interface.
*
* wolfBoot has no Ethernet stack; this brings up only the MDIO management
* plane so a board-specific register sequence (normally run from U-Boot's
* "mii"/"mw" commands) can configure the PHY before the OS starts. Every
* magic value below is behind an #ifndef so a board can override it from
* CFLAGS_EXTRA. Register layout mirrors the Cadence GEM driver in the
* separate wolfIP project (src/port/amd/common/gem_regs.h there).
*
* The defaults target the ZCU102 on-board PHY (TI DP83867 at MDIO address
* 0x0C on GEM3) and just read the PHY ID as a diagnostic.
*
* A board keeps its own values (GEM base, PHY address, PL poke, and the
* ZYNQMP_PHY_INIT_STEPS sequence) in a small header and selects it with one
* config line, which keeps the efficient step array without editing this file:
* CFLAGS_EXTRA+=-DZYNQMP_PHY_INIT_HEADER='"myboard_phy.h"'
* That header just #defines the macros it wants to change; anything it omits
* falls back to the ZCU102 defaults below. Individual scalars can also be set
* directly with -D. Where the PHY sits behind the PL, the boot image must
* include the FPGA bitstream so the FSBL configures it before this runs,
* otherwise the transactions are no-ops. */

#ifdef ZYNQMP_PHY_INIT_HEADER
#include ZYNQMP_PHY_INIT_HEADER
#endif

/* GEM instance used for MDIO. Default is GEM3 (ZCU102 on-board PHY). */
#ifndef ZYNQMP_GEM_BASE
#define ZYNQMP_GEM_BASE 0xFF0E0000UL /* GEM3 */
#endif
#define GEM_NWCTRL (*((volatile uint32_t*)(ZYNQMP_GEM_BASE + 0x000)))
#define GEM_NWCFG (*((volatile uint32_t*)(ZYNQMP_GEM_BASE + 0x004)))
#define GEM_NWSR (*((volatile uint32_t*)(ZYNQMP_GEM_BASE + 0x008)))
#define GEM_PHYMNTNC (*((volatile uint32_t*)(ZYNQMP_GEM_BASE + 0x034)))

#define GEM_NWCTRL_MDEN (1UL << 4) /* enable MDIO management port */
#define GEM_NWSR_PHY_IDLE (1UL << 2) /* MDIO logic idle */
#define GEM_NWCFG_MDCDIV_SHIFT 18 /* NWCFG[20:18] MDC clock divisor */
#define GEM_PHYMNTNC_CLAUSE22 0x40020000UL
#define GEM_PHYMNTNC_OP_R (2UL << 28)
#define GEM_PHYMNTNC_OP_W (1UL << 28)

/* MDC divisor selector. 5 => pclk/96, keeping MDC below the 2.5 MHz max. */
#ifndef ZYNQMP_GEM_MDC_DIV
#define ZYNQMP_GEM_MDC_DIV 5
#endif

/* PHY MDIO address (clause-22). Default 0x0C = ZCU102 on-board DP83867. */
#ifndef ZYNQMP_PHY_ADDR
#define ZYNQMP_PHY_ADDR 0x0C
#endif

/* Optional PL register address poked by a ZYNQMP_PHY_OP_GPIO step. Default 0
* disables the poke (ZCU102 has no such register). A board can set this to its
* own PL address; note a PL address only responds once the FPGA bitstream is
* loaded. */
#ifndef ZYNQMP_PHY_GPIO_ADDR
#define ZYNQMP_PHY_GPIO_ADDR 0
#endif

/* Op codes for the configurable init sequence. */
#define ZYNQMP_PHY_OP_GPIO 0 /* write arg1 to ZYNQMP_PHY_GPIO_ADDR */
#define ZYNQMP_PHY_OP_WR 1 /* MDIO write: reg=arg0, val=arg1 */
#define ZYNQMP_PHY_OP_RD 2 /* MDIO read: reg=arg0 (arg1 ignored) */

/* Default sequence: read the PHY ID registers (2, 3) as a diagnostic. With
* DEBUG_UART=1 this prints the ID and confirms MDIO reached the PHY. Override
* ZYNQMP_PHY_INIT_STEPS with {op, arg0, arg1} rows to replay a board's own
* "mii"/"mw" sequence (ZYNQMP_PHY_OP_WR writes, then an optional
* ZYNQMP_PHY_OP_GPIO poke of ZYNQMP_PHY_GPIO_ADDR). */
#ifndef ZYNQMP_PHY_INIT_STEPS
#define ZYNQMP_PHY_INIT_STEPS \
{ ZYNQMP_PHY_OP_RD, 0x02, 0 }, \
{ ZYNQMP_PHY_OP_RD, 0x03, 0 }
#endif
#endif /* WOLFBOOT_ZYNQMP_PHY_INIT */


/* Configuration Security Unit (CSU) */
/* Triple-Dedundant MicroBlaze processor */
/* 128 KB CSU ROM (immutable) */
Expand Down
Loading