The Ultra96-V2 has the output signal from the ZynqMP I / O port connected to the KILL_N input port on the power on / off LSI. If this signal is set to L, the Ultra96-V2 can be powered off.
However, if you build BOOT.BIN and Linux by the usual method, it seems that you cannot control this signal, and even if you shut down from Linux with the poweroff command etc., Ultra96-V2 will not be in the poweroff state. Hmm. By the way, when you turn off the power, you need to press and hold the power button (SW4), but pressing and holding the small button is quite painful for your fingers (this is the motive).
In this article, by modifying the PMUFW (Platform Management Unit Firmware) included in BOOT.BIN, when a shutdown is executed from Linux by the poweroff command etc., this signal is set to L and the Ultra96-V2 is automatically powered off. Here's how to do it.
It also shows how to shut down Linux when you press the power button while Linux is booting.
Fig.1 shows the circuit around the power-off of Ultra96-V2.
Fig.1 Ultra96-V2 MIO34_POWER_KILL_N signal
The KILL_N port of the power-on / off LSI (SLG4G42480V) is connected to the ZynqMP MIO34 port through a level conversion circuit. The output signal from the ZynqMP MIO34 port is pulled up and is fixed at H from the time it is powered up until the MIO34 is configured.
Outputting L from the ZynqMP MIO34 port turns the KILL_N input port of the powering on / off LSI to L to power off the Ultra96-V2.
The INT port of the power-on / off LSI (SLG4G42480V) is connected to the ZynqMP MIO26 port.
Figure 2 shows how the ZynqMP MIO26 and MIO34 ports are configured in Vivado on the Ultra96-V2.
Fig.2 Ultra96-V2 ZynqMP MIO26 / MIO34 port settings
In Ultra96-V2, the MIO26 port of ZynqMP is set to connect to GPI0 of PMU (Platform Management Unit), and the MIO34 port is set to connect to GPO2.
PMU(Platform Management Unit)
The PMU (Platform Management Unit) is a subsystem built into ZynqMP to power up, reset, and monitor resources in ZynqMP using interprocessor interrupts and power management registers. The PMU consists of a MicroBlaze processor, 32KByte ROM and 128KByte RAM. Immediately after powering up the ZynqMP, the PMU first boots to run the stage 0 bootloader inside the ROM. The stage 0 boot loader loads the stage 1 boot loader (FSBL) contained in BOOT.BIN in the storage (SD-Card, etc.) into the internal RAM and transfers control to the APU (Application Processing Unit).
PMUFW(Platform Management Unit Firmware)
The Stage 1 boot loader (FSBL) loads the PMUFW (Platform Management Unit Firmware) contained in BOOT.BIN into the RAM of the PMU. The PMU then powers up, resets, and monitors the resources in ZynqMP according to the PMUFW. It also accepts various requests from APU and RPU as system calls (PM API calls).
When powering off on Linux, you will eventually call the shutdown PM API call for this PMUFW.
Please refer to [Building PMUFW] for the source code of PMUFW.
PmProcessRequest()
When the PMU accepts a PM API call, it calls PMUFW's PmProcessRequest (). PmProcessRequest () is defined in pm_core.c.
The PMU is calling PmSystemShutdown () when the PM API call is a shutdown request.
pm_core.c
/**
* PmProcessApiCall() - Called to process PM API call
* @master Pointer to a requesting master structure
* @pload Pointer to array of integers with the information about the pm call
* (api id + arguments of the api)
*
* @note Called to process PM API call. If specific PM API receives less
* than 4 arguments, extra arguments are ignored.
*/
void PmProcessRequest(PmMaster *const master, const u32 *pload)
{
(Omission)
case PM_SYSTEM_SHUTDOWN:
PmSystemShutdown(master, pload[1], pload[2]);
break;
(Omission)
}
PmSystemShutdown()
PmSystemShutdown () is located in pm_core.c.
pm_core.c
/**
* PmSystemShutdown() - Request system shutdown or restart
* @master Master requesting system shutdown
* @type Shutdown type
* @subtype Shutdown subtype
*/
static void PmSystemShutdown(PmMaster* const master, const u32 type,
const u32 subtype)
{
s32 status = XST_SUCCESS;
PmInfo("%s> SystemShutdown(%lu, %lu)\\r\\n", master->name, type, subtype);
/* For shutdown type the subtype is irrelevant: shut the caller down */
if (PMF_SHUTDOWN_TYPE_SHUTDOWN == type) {
status = PmMasterFsm(master, PM_MASTER_EVENT_FORCE_DOWN);
#if defined(BOARD_SHUTDOWN_PIN) && defined(BOARD_SHUTDOWN_PIN_STATE)
if (PMF_SHUTDOWN_SUBTYPE_SYSTEM == subtype) {
PmKillBoardPower();
}
#endif
goto done;
}
if (PMF_SHUTDOWN_TYPE_RESET != type) {
status = XST_INVALID_PARAM;
goto done;
}
/* Now distinguish the restart scope depending on the subtype */
switch (subtype) {
case PMF_SHUTDOWN_SUBTYPE_SUBSYSTEM:
status = PmMasterRestart(master);
break;
case PMF_SHUTDOWN_SUBTYPE_PS_ONLY:
XPfw_ResetPsOnly();
break;
case PMF_SHUTDOWN_SUBTYPE_SYSTEM:
XPfw_ResetSystem();
break;
default:
PmLog(PM_ERRNO_INVALID_SUBTYPE, subtype, master->name);
status = XST_INVALID_PARAM;
break;
}
done:
IPI_RESPONSE1(master->ipiMask, status);
}
The focus here is on what happens when BOARD_SHUTDOWN_PIN and BOARD_SHUTDOWN_PIN_STATE are defined. In this part, PmKillBoardPower () is called when the type of PM API call is PMF_SHUTDOWN_TYPE_SHUTDOWN and the subtype is PMF_SHUTDOWN_SUBTYPE_SYSTEM.
PmKillBoardPower()
PmKillBoardPower () is located in pm_core.c.
pm_core.c
/**
* PmKillBoardPower() - Power-off board by sending KILL signal to power chip
*/
#if defined(BOARD_SHUTDOWN_PIN) && defined(BOARD_SHUTDOWN_PIN_STATE)
static void PmKillBoardPower(void)
{
u32 reg = XPfw_Read32(PMU_LOCAL_GPO1_READ);
u32 mask = PMU_IOMODULE_GPO1_MIO_0_MASK << BOARD_SHUTDOWN_PIN;
u32 value = BOARD_SHUTDOWN_PIN_STATE << BOARD_SHUTDOWN_PIN;
u32 mioPinOffset;
mioPinOffset = IOU_SLCR_MIO_PIN_34_OFFSET + (BOARD_SHUTDOWN_PIN - 2U)*4U;
reg = (reg & (~mask)) | (mask & value);
XPfw_Write32(PMU_IOMODULE_GPO1, reg);
/* Configure board shutdown pin to be controlled by the PMU */
XPfw_RMW32((IOU_SLCR_BASE + mioPinOffset),
0x000000FEU, 0x00000008U);
}
#endif
This PmKillBoardPower () is where the specified MIO (MIO34 port on Ultra96-V2) is turned L to power off the board during a shutdown PM API call. Specifically, after setting the specified value (0 for Ultra96-V2) to the bit (GPO2 for Ultra96-V2) corresponding to the port of the GPO register, the specified MIO (MIO34 port for Ultra96-V2) is output. I'm in mode.
XPfw_PmWakeHandler()
The input signal from the ZynqMP MIO26 port is connected to GPI0 on the PMU. When this signal is input, the PMU is interrupted. Its interrupt handler XPfw_PmWakeHandler () is located in pm_binding.c.
pm_binding.c
/**
* XPfw_PmWakeHandler() - Call from GPI1 interrupt to process wake request
* @srcMask Value read from GPI1 register which determines interrupt source
*
* @return Status of performing wake-up (XST_INVALID_PARAM if wake is a
* processor wake event but processor is not found, status of
* performing wake otherwise)
*
* @note Call from GPI1 interrupt routine to process wake request. Must not
* clear GPI1 interrupt before this function returns.
* If the wake source is one of GIC wakes, source of the interrupt
* (peripheral that actually generated interrupt to GIC) cannot be
* determined, and target should be immediately woken-up (target is
* processor whose GIC wake bit is set in srcMask). If the wake is the
* FPD GIC Proxy interrupt, the APU needs to be woken up.
*/
s32 XPfw_PmWakeHandler(const u32 srcMask)
{
s32 status = XST_INVALID_PARAM;
#if defined(PMU_MIO_INPUT_PIN) && (PMU_MIO_INPUT_PIN >= 0U) \\
&& (PMU_MIO_INPUT_PIN <= 5U)
if ((PMU_IOMODULE_GPI1_MIO_WAKE_0_MASK << PMU_MIO_INPUT_PIN) == srcMask) {
PmShutdownInterruptHandler();
return XST_SUCCESS;
}
#endif
if (0U != (PMU_IOMODULE_GPI1_GIC_WAKES_ALL_MASK & srcMask)) {
/* Processor GIC wake */
PmProc* proc = PmProcGetByWakeMask(srcMask);
if ((NULL != proc) && (NULL != proc->master)) {
status = PmMasterWakeProc(proc);
} else {
status = XST_INVALID_PARAM;
}
} else if (0U != (PMU_IOMODULE_GPI1_FPD_WAKE_GIC_PROXY_MASK & srcMask)) {
status = PmMasterWake(&pmMasterApu_g);
} else if (0U != (PMU_IOMODULE_GPI1_MIO_WAKE_ALL_MASK & srcMask)) {
status = PmExternWakeMasters();
} else if (0U != (PMU_IOMODULE_GPI1_USB_0_WAKE_MASK & srcMask)) {
status = PmWakeMasterBySlave(&pmSlaveUsb0_g.slv);
} else if (0U != (PMU_IOMODULE_GPI1_USB_1_WAKE_MASK & srcMask)) {
status = PmWakeMasterBySlave(&pmSlaveUsb1_g.slv);
} else {
}
return status;
}
The point to note here is PmShutdownInterruptHandler (), which is called if PMU_MIO_INPUT_PIN is defined and its value is in the range 0-5.
PmShutdownInterruptHandler()
PmShutdownInterruptHandler () is located in pm_core.c.
pm_core.c
/**
* PmShutdownInterruptHandler() - Send suspend request to all active masters
*/
void PmShutdownInterruptHandler(void)
{
#if defined(PMU_MIO_INPUT_PIN) && (PMU_MIO_INPUT_PIN >= 0U) \\
&& (PMU_MIO_INPUT_PIN <= 5U)
/*
* Default status of MIO26 pin is 1. So MIO wake event bit in GPI1
* register is always 1, which is used to identify shutdown event.
*
* GPI event occurs only when any bit of GPI register changes from
* 0 to 1. When any GPI1 event occurs Gpi1InterruptHandler() checks
* GPI1 register and process interrupts for the bits which are 1.
* Because of MIO wake bit is 1 in GPI1 register, shutdown handler
* will be called every time when any of GPI1 event occurs.
*
* There is no way to identify which bit cause GPI1 interrupt.
* So every time Gpi1InterruptHandler() is checking bit which are 1
* And calls respective handlers.
*
* To handle such case avoid power off when any other (other than MIO
* wake)bit in GPI1 register is 1. If no other bit is 1 in GPI1 register
* and still PMU gets GPI1 interrupt means that MIO26 pin state is
* changed from (1 to 0 and 0 to 1). In this case it is confirmed that
* it is event for shutdown only and not because of other events.
* There are chances that some shutdown events are missed (1 out of 50)
* but it should not harm.
*/
if (XPfw_Read32(PMU_IOMODULE_GPI1) !=
(PMU_IOMODULE_GPI1_MIO_WAKE_0_MASK << PMU_MIO_INPUT_PIN)) {
return;
}
#endif
u32 rpu_mode = XPfw_Read32(RPU_RPU_GLBL_CNTL);
if (PM_MASTER_STATE_ACTIVE == PmMasterIsActive(&pmMasterApu_g)) {
PmInitSuspendCb(&pmMasterApu_g,
SUSPEND_REASON_SYS_SHUTDOWN, 1U, 0U, 0U);
}
if (0U == (rpu_mode & RPU_RPU_GLBL_CNTL_SLSPLIT_MASK)) {
if (PM_MASTER_STATE_ACTIVE == PmMasterIsActive(&pmMasterRpu0_g)) {
PmInitSuspendCb(&pmMasterRpu0_g,
SUSPEND_REASON_SYS_SHUTDOWN, 1U, 0U, 0U);
}
if (PM_MASTER_STATE_ACTIVE == PmMasterIsActive(&pmMasterRpu1_g)) {
PmInitSuspendCb(&pmMasterRpu1_g,
SUSPEND_REASON_SYS_SHUTDOWN, 1U, 0U, 0U);
}
} else {
if (PM_MASTER_STATE_ACTIVE == PmMasterIsActive(&pmMasterRpu_g)) {
PmInitSuspendCb(&pmMasterRpu_g,
SUSPEND_REASON_SYS_SHUTDOWN, 1U, 0U, 0U);
}
}
}
PmShutdownInterruptHandler () checks the status of each processor (APU, RPU0, RPU1, RPU) and requests that each processor be suspended if it is active. APU running Linux accepts this suspend request and executes the shutdown sequence.
xpfw_config.h
PMU_MIO_INPUT_PIN, BOARD_SHUTDOWN_PIN and BOARD_SHUTDOWN_PIN_STATE are defined in xpfw_config.h.
xpfw_config.h
(Omission)
/*
* PMU Firmware code include options
*
* PMU Firmware by default disables some functionality and enables some
* Here we are listing all the build flags with the default option.
* User can modify these flags to enable or disable any module/functionality
* - ENABLE_PM : Enables Power Management Module
* - ENABLE_EM : Enables Error Management Module
* - ENABLE_SCHEDULER : Enables the scheduler
* - ENABLE_RECOVERY : Enables WDT based restart of APU sub-system
* - ENABLE_RECOVERY_RESET_SYSTEM : Enables WDT based restart of system
* - ENABLE_RECOVERY_RESET_PS_ONLY : Enables WDT based restart of PS
* - ENABLE_ESCALATION : Enables escalation of sub-system restart to
* SRST/PS-only if the first restart attempt fails
* - ENABLE_WDT : Enables WDT based restart functionality for PMU
* - ENABLE_STL : Enables STL Module
* - ENABLE_RTC_TEST : Enables RTC Event Handler Test Module
* - ENABLE_IPI_CRC_VAL : Enables CRC calculation for IPI messages
* - ENABLE_FPGA_LOAD : Enables FPGA bit stream loading feature
* - ENABLE_SECURE : Enables security features
* - XPU_INTR_DEBUG_PRINT_ENABLE : Enables debug for XMPU/XPPU functionality
*
* - PM_LOG_LEVEL : Enables print based debug functions for PM. Possible
* values are: 1 (alerts), 2 (errors), 3 (warnings),
* 4 (info). Higher numbers include the debug scope of
* lower number, i.e. enabling 3 (warnings) also enables
* 1 (alerts) and 2 (errors).
* - IDLE_PERIPHERALS : Enables idling peripherals before PS or System reset
* - ENABLE_NODE_IDLING : Enables idling and reset of nodes before force
* of a sub-system
* - DEBUG_MODE : This macro enables PM debug prints if XPFW_DEBUG_DETAILED
* macro is also defined
* - ENABLE_POS : Enables Power Off Suspend feature
* - ENABLE_DDR_SR_WR : Enables DDR self refresh over warm restart feature
* - ENABLE_UNUSED_RPU_PWR_DWN : Enables unused RPU power down feature
* - DISABLE_CLK_PERMS : Disable clock permission checking (it is not safe
* to ever disable clock permission checking). Do this at
* your own responsibility.
* - ENABLE_EFUSE_ACCESS : Enables efuse access feature
*
* These macros are specific to ZCU100 design where it uses GPO1[2] as a
* board power line and
* - PMU_MIO_INPUT_PIN : Enables board shutdown related code for ZCU100
* - BOARD_SHUTDOWN_PIN : Tells board shutdown pin. In case of ZCU100,
* GPO1[2] is the board power line.
* - BOARD_SHUTDOWN_PIN_STATE : Tells what should be the state of board power
* line when system shutdown request comes
*/
#define ENABLE_PM_VAL (1U)
#define ENABLE_EM_VAL (0U)
#define ENABLE_SCHEDULER_VAL (0U)
#define ENABLE_RECOVERY_VAL (0U)
#define ENABLE_RECOVERY_RESET_SYSTEM_VAL (0U)
#define ENABLE_RECOVERY_RESET_PS_ONLY_VAL (0U)
#define ENABLE_ESCALATION_VAL (0U)
#define CHECK_HEALTHY_BOOT_VAL (0U)
#define ENABLE_WDT_VAL (0U)
#define ENABLE_CUSTOM_MOD_VAL (0U)
#define ENABLE_STL_VAL (0U)
#define ENABLE_RTC_TEST_VAL (0U)
#define ENABLE_IPI_CRC_VAL (0U)
#define ENABLE_FPGA_LOAD_VAL (1U)
#define ENABLE_SECURE_VAL (1U)
#define ENABLE_EFUSE_ACCESS (0U)
#define XPU_INTR_DEBUG_PRINT_ENABLE_VAL (0U)
#define PM_LOG_LEVEL_VAL (0U)
#define IDLE_PERIPHERALS_VAL (0U)
#define ENABLE_NODE_IDLING_VAL (0U)
#define DEBUG_MODE_VAL (0U)
#define ENABLE_POS_VAL (0U)
#define ENABLE_DDR_SR_WR_VAL (0U)
#define DISABLE_CLK_PERMS_VAL (0U)
#define ENABLE_UNUSED_RPU_PWR_DWN_VAL (1U)
#define PMU_MIO_INPUT_PIN_VAL (0U)
#define BOARD_SHUTDOWN_PIN_VAL (1U)
#define BOARD_SHUTDOWN_PIN_STATE_VAL (1U)
#define PMU_MIO_INPUT_PIN_VAL (0U)
#define BOARD_SHUTDOWN_PIN_VAL (0U)
#define BOARD_SHUTDOWN_PIN_STATE_VAL (0U)
:
(Omission)
:
#if PMU_MIO_INPUT_PIN_VAL
#define PMU_MIO_INPUT_PIN 0U
#endif
#if BOARD_SHUTDOWN_PIN_VAL
#define BOARD_SHUTDOWN_PIN 2U
#endif
#if BOARD_SHUTDOWN_PIN_STATE_VAL
#define BOARD_SHUTDOWN_PIN_STATE 0U
#endif
:
(Omission)
BOARD_SHUTDOWN_PIN is the GPO port number. The Ultra96-V2 is set to 2 to indicate the GPO2 connected to the MIO34 port.
BOARD_SHUTDOWN_PIN_STATE is the value output to the GPO port. Ultra96-V2 outputs 0 (Low).
PMU_MIO_INPUT_PIN is the GPI port number. The Ultra96-V2 is set to 0, which indicates the GPI0 connected to the MIO26 port.
Sample FPGA Design hardware information is required to build the PMUFW. Create target / Ultra96-V2 / build-v2019.1 / fpga / project.sdk / design_1 \ _wrapper.hdf by referring to [Building Simple FPGA Design].
Modify xpfw_config.h when building the PMUFW. Specifically, set PMU_MIO_INPUT_PIN_VAL, BOARD_SHUTDOWN_PIN_VAL and BOARD_SHUTDOWN_PIN_STATE_VAL to 1U.
A good script to build is:
Tcl:target/Ultra96-V2/build-v2019.1/fpga/build_zynqmp_pmufw.hsi
#!/usr/bin/tclsh
set app_name "pmufw"
set app_type "zynqmp_pmufw"
set hwspec_file "design_1_wrapper.hdf"
set proc_name "psu_pmu_0"
set project_name "project"
set project_dir [pwd]
set sdk_workspace [file join $project_dir $project_name.sdk]
set app_dir [file join $sdk_workspace $app_name]
set app_release_dir [file join [pwd] ".." ]
set app_release_elf "zynqmp_pmufw.elf"
set board_shutdown true
set board_power_sw true
set hw_design [hsi::open_hw_design [file join $sdk_workspace $hwspec_file]]
hsi::generate_app -hw $hw_design -os standalone -proc $proc_name -app $app_type -dir $app_dir
if {$board_shutdown || $board_power_sw} {
file copy -force [file join $app_dir "xpfw_config.h"] [file join $app_dir "xpfw_config.h.org"]
set xpfw_config_old [open [file join $app_dir "xpfw_config.h.org"] r]
set xpfw_config_new [open [file join $app_dir "xpfw_config.h.new"] w]
while {[gets $xpfw_config_old line] >= 0} {
if {$board_shutdown && [regexp {^#define\\s+BOARD_SHUTDOWN_PIN_VAL\\s+\\S+} $line]} {
puts $xpfw_config_new "#define BOARD_SHUTDOWN_PIN_VAL (1U)"
} elseif {$board_shutdown && [regexp {^#define\\s+BOARD_SHUTDOWN_PIN_STATE_VAL\\s+\\S+} $line]} {
puts $xpfw_config_new "#define BOARD_SHUTDOWN_PIN_STATE_VAL (1U)"
} elseif {$board_power_sw && [regexp {^#define\\s+PMU_MIO_INPUT_PIN_VAL\\s+\\S+} $line]} {
puts $xpfw_config_new "#define PMU_MIO_INPUT_PIN_VAL (1U)"
} else {
puts $xpfw_config_new $line
}
}
close $xpfw_config_old
close $xpfw_config_new
file rename -force [file join $app_dir "xpfw_config.h.new"] [file join $app_dir "xpfw_config.h"]
}
exec make -C $app_dir all >&@ stdout
file copy -force [file join $app_dir "executable.elf"] [file join $app_release_dir $app_release_elf]
Running build_zynqmp_pmufw.hsi in Vivado will generate target / Ultra96-V2 / build-v2019.1 / zynqmp_pmufw.elf.
vivado% cd target/Ultra96-V2/build-v2019.1/fpga/
vivado% hsi -mode tcl -source build_zynqmp_pmufw.hsi
Note) As of Vivado 2019.2, the Vivado SDK has been deprecated and integrated into Vitis, so the above method will not work. The main difference is that the hsi command has been deprecated and replaced with the xsct command, and the hardware information file (hwspec_file) has a .xsa extension instead of .hdf. For information on how to build FSBL in Vitis, please refer to ["Tcl script to build Zynq FSBL (First Stage Boot Loader) in Vivado (Vitis)"] [Tcl script to build FSBL in Vitis].
Tcl:target/Ultra96-V2/build-v2019.2/fpga/build_zynqmp_pmufw.tcl
:
(target/Ultra96-V2/build-v2019.1/fpga/build_zynqmp_pmufw.Same as hci)
:
set hwspec_file "design_1_wrapper.xsa"
:
(target/Ultra96-V2/build-v2019.1/fpga/build_zynqmp_pmufw.Same as hci)
:
vivado% cd target/Ultra96-V2/build-v2019.2/fpga/
vivado% xsct build_zynqmp_pmufw.tcl
Incorporate zynqmp_pmufw.elf built in the previous section into BOOT.BIN. Specifically, see [Building BOOT.BIN].
Boot Linux using the BOOT.BIN built in the previous section. Shutting down with the poweroff command puts the Ultra96-V2 in a power-off state. When the Ultra96-V2 is powered off, the power-on LED (blue LED) turns off and the air-cooling fan stops.
Also, pressing the power button (SW4) while Linux is booting causes Linux to transition to the shutdown sequence, after which the Ultra96-V2 is powered off.
root@debian-fpga:~# poweroff
[ OK ] Stopped target Sound Card.
[ OK ] Stopped tar■ OK ] Stopped Daily man-db regeneration.
[ OK ] Stopped target Multi-User System.
:
(Omission)
:
[ OK ] Reached target Shutdown.
[ OK ] Reached target Final Step.
[ OK ] Started Power-Off.
[ OK ] Reached target Power-Off.
[ 226.399206] systemd-shutdow: 29 output lines suppressed due to ratelimiting
[ 226.486399] systemd-shutdown[1]: Syncing filesystems and block devices.
[ 226.581506] systemd-shutdown[1]: Sending SIGTERM to remaining processes...
[ 226.596568] systemd-journald[1777]: Received SIGTERM from PID 1 (systemd-shutdow).
[ 226.617967] systemd-shutdown[1]: Sending SIGKILL to remaining processes...
[ 226.631598] systemd-shutdown[1]: Unmounting file systems.
[ 226.639081] [3538]: Remounting '/' read-only in with options '(null)'.
[ 226.678576] EXT4-fs (mmcblk0p2): re-mounted. Opts: (null)
[ 226.698640] systemd-shutdown[1]: All filesystems unmounted.
[ 226.704248] systemd-shutdown[1]: Deactivating swaps.
[ 226.709451] systemd-shutdown[1]: All swaps deactivated.
[ 226.714691] systemd-shutdown[1]: Detaching loop devices.
[ 226.722646] systemd-shutdown[1]: All loop devices detached.
[ 226.728233] systemd-shutdown[1]: Detaching DM devices.
[ 226.745079] usb 1-1: USB disconnect, device number 2
[ 226.750065] usb 1-1.4: USB disconnect, device number 3
[ 226.869502] usb 2-1: USB disconnect, device number 2
[ 226.911441] reboot: Power down
The hardware information created in Building Simple FPGA Design includes psu_init.c for configuring ZynqMP ports. psu_init.c contains a program for the Stage 1 bootloader (FSBL) to set the ZynqMP port. There is a strange part in psu_init.c created for Ultra96-V2.
psu_init.c
/*
* Register : MIO_PIN_33 @ 0XFF180084
* Level 0 Mux Select 0= Level 1 Mux Output 1= gem0, Input, gem0_rgmii_rxd[
* 0]- (RX RGMII data)
* PSU_IOU_SLCR_MIO_PIN_33_L0_SEL 0
* Level 1 Mux Select 0= Level 2 Mux Output 1= pcie, Input, pcie_reset_n- (
* PCIE Reset signal)
* PSU_IOU_SLCR_MIO_PIN_33_L1_SEL 0
* Level 2 Mux Select 0= Level 3 Mux Output 1= pmu, Output, pmu_gpo[1]- (PM
* U GPI) 2= test_scan, Input, test_scan_in[33]- (Test Scan Port) = test_sc
* an, Output, test_scan_out[33]- (Test Scan Port) 3= csu, Input, csu_ext_t
* amper- (CSU Ext Tamper)
* PSU_IOU_SLCR_MIO_PIN_33_L2_SEL 1
* Level 3 Mux Select 0= gpio1, Input, gpio_1_pin_in[7]- (GPIO bank 1) 0= g
* pio1, Output, gpio_1_pin_out[7]- (GPIO bank 1) 1= can1, Input, can1_phy_
* rx- (Can RX signal) 2= i2c1, Input, i2c1_sda_input- (SDA signal) 2= i2c1
* , Output, i2c1_sda_out- (SDA signal) 3= swdt1, Output, swdt1_rst_out- (W
* atch Dog Timer Output clock) 4= spi1, Output, spi1_n_ss_out[2]- (SPI Mas
* ter Selects) 5= ttc3, Output, ttc3_wave_out- (TTC Waveform Clock) 6= ua1
* , Input, ua1_rxd- (UART receiver serial input) 7= trace, Output, tracedq
* [11]- (Trace Port Databus)
* PSU_IOU_SLCR_MIO_PIN_33_L3_SEL 0
* Configures MIO Pin 33 peripheral interface mapping
* (OFFSET, MASK, VALUE) (0XFF180084, 0x000000FEU ,0x00000008U)
*/
PSU_Mask_Write(IOU_SLCR_MIO_PIN_33_OFFSET, 0x000000FEU, 0x00000008U);
/*##################################################################### */
/*
* Register : MIO_PIN_35 @ 0XFF18008C
* Level 0 Mux Select 0= Level 1 Mux Output 1= gem0, Input, gem0_rgmii_rxd[
* 2]- (RX RGMII data)
* PSU_IOU_SLCR_MIO_PIN_35_L0_SEL 0
* Level 1 Mux Select 0= Level 2 Mux Output 1= pcie, Input, pcie_reset_n- (
* PCIE Reset signal)
* PSU_IOU_SLCR_MIO_PIN_35_L1_SEL 0
* Level 2 Mux Select 0= Level 3 Mux Output 1= pmu, Output, pmu_gpo[3]- (PM
* U GPI) 2= test_scan, Input, test_scan_in[35]- (Test Scan Port) = test_sc
* an, Output, test_scan_out[35]- (Test Scan Port) 3= dpaux, Input, dp_hot_
* plug_detect- (Dp Aux Hot Plug)
* PSU_IOU_SLCR_MIO_PIN_35_L2_SEL 0
* Level 3 Mux Select 0= gpio1, Input, gpio_1_pin_in[9]- (GPIO bank 1) 0= g
* pio1, Output, gpio_1_pin_out[9]- (GPIO bank 1) 1= can0, Output, can0_phy
* _tx- (Can TX signal) 2= i2c0, Input, i2c0_sda_input- (SDA signal) 2= i2c
* 0, Output, i2c0_sda_out- (SDA signal) 3= swdt0, Output, swdt0_rst_out- (
* Watch Dog Timer Output clock) 4= spi1, Input, spi1_n_ss_in- (SPI Master
* Selects) 4= spi1, Output, spi1_n_ss_out[0]- (SPI Master Selects) 5= ttc2
* , Output, ttc2_wave_out- (TTC Waveform Clock) 6= ua0, Output, ua0_txd- (
* UART transmitter serial output) 7= trace, Output, tracedq[13]- (Trace Po
* rt Databus)
* PSU_IOU_SLCR_MIO_PIN_35_L3_SEL 0
* Configures MIO Pin 35 peripheral interface mapping
* (OFFSET, MASK, VALUE) (0XFF18008C, 0x000000FEU ,0x00000000U)
*/
PSU_Mask_Write(IOU_SLCR_MIO_PIN_35_OFFSET, 0x000000FEU, 0x00000000U);
/*##################################################################### */
As you can see, the MIO34 port is completely missing. That is, the Stage 1 bootloader (FSBL) does not configure the MIO34 port. The MIO34 port is set by PmKillBoardPower () of PMUFW (Platform Management Unit Firmware) when L is output to the MIO34 port. By that time, the MIO34 port is open and the KILL_N port on the powering on / off LSI is H because it has been pulled up.
This is a matter of course when considering the boot sequence, and while the stage 1 bootloader is running, PMUFW is not running yet, so GPO2 on the PMU defaults to 0. If the MIO34 port is set in this state, 0 (= Low) set in GPO2 of the PMU will be output to the MIO34 port.
In other words, it will be powered off while the stage 1 boot loader is running. To prevent this, psu_init.c probably doesn't configure the MIO34 port.
Looking at the contents of psu_init.c explained in the previous section and the source code of PMUFW (especially xpfw_config.h), it seems that the port when powering off with ZynqMP is fixed to the MIO34 port.
If you are designing a board with ZynqMP and want to power off from ZynqMP, you may want to keep in mind using the MIO34 port.
The Device Tree used to boot Linux on Ultra96 / Ultra96-V2 has the following nodes.
arch/arm64/boot/dts/xilinx/avnet-ultra96v2-rev1.dts
// SPDX-License-Identifier: GPL-2.0+
/*
* dts file for Avnet Ultra96-V2 rev1
*
*/
/dts-v1/;
:
(Omission)
:
ltc2954: ltc2954 { /* U7 */
compatible = "lltc,ltc2954", "lltc,ltc2952";
status = "disabled";
trigger-gpios = <&gpio 26 GPIO_ACTIVE_LOW>; /* INT line - input */
/* If there is HW watchdog on mezzanine this signal should be connected there */
watchdog-gpios = <&gpio 35 GPIO_ACTIVE_HIGH>; /* MIO on PAD */
kill-gpios = <&gpio 34 GPIO_ACTIVE_LOW>; /* KILL signal - output */
};
:
(Omission)
This node appears to define a device driver that controls the LSI that powers it on and off. However, as described in "Configuring MIO26 / MIO34 Ports on ZynqMP", Ultra96-V2 configures the MIO26 / MIO34 ports to be controlled by the PMU and cannot be controlled by an APU running Linux. Therefore, this node is meaningless and should be disabled. Specifically, specify "disabled" in the status property.
[Boot Loader]: https://qiita.com/ikwzm/items/9c78e83298906447fe5d "Building Debian GNU / Linux (v2018.2 version) for Ultra96 (Boot Loader)" @Qiita " [Building Simple FPGA Design]: https://qiita.com/ikwzm/items/0489b02962c6f0455783 "Building Debian GNU / Linux (v2018.2 version) for Ultra96 (Sample FPGA Design)" @Qiita " [Building PMUFW]: https://qiita.com/ikwzm/items/00024adf956696dd51c6 "Building Debian GNU / Linux (v2018.2 version) for Ultra96 (PMUFW edition)" @Qiita " [Building BOOT.BIN]: https://qiita.com/ikwzm/items/af4420edd262e14feef9 "Building Debian GNU / Linux (v2018.2 version) for Ultra96 (BOOT.BIN edition)" @Qiita " [Tcl script to build FSBL in Vivado]: https://qiita.com/ikwzm/items/165481921c3e984a9e70 "Tcl script to build Zynq FSBL (First Stage Boot Loader) in Vivado (Vitis)" @Qiita " [ZynqMP boot and power management]: https://www.slideshare.net/ssuser479fa3/zynq-mp-58490589 "" ZynqMP boot and power management "@Vengineer ZynqMP study session materials (2016/2/20)" [UG1085]: https://www.xilinx.com/support/documentation/user_guides/ug1085-zynq-ultrascale-trm.pdf "「Zynq UltraScale+ MPSoC Technical Reference Manual, UG1085(v1.7) December 22,2017」" [UG1137]: https://www.xilinx.com/support/documentation/user_guides/ug1137-zynq-ultrascale-mpsoc-swdev.pdf "「Zynq UltraScale+ MPSoC Software Developer Guide, UG1137(v5.0) November 15,2017)」"
Recommended Posts