How to power off Linux with Ultra96-V2

Introduction

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.

Power-off mechanism

Circuit around power off

Fig.1 shows the circuit around the power-off of Ultra96-V2.

Fig.1 Ultra96-V2 の MIO34_POWER_KILL_N 信号

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.

ZynqMP MIO26 / MIO34 port configuration

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 ポートの設定

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.

Modify BOOT.BIN

Fix PMUFW

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

Building BOOT.BIN

Incorporate zynqmp_pmufw.elf built in the previous section into BOOT.BIN. Specifically, see [Building BOOT.BIN].

result

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

Digression

The wonder of psu_init.c

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.

Is power off only 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.

Device Tree LTC2954 node

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.

reference

[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

How to power off Linux with Ultra96-V2
How to get started with laravel (Linux)
How to make Linux compatible with Japanese keyboard
How to update with SQLAlchemy?
How to cast with Theano
How to Alter with SQLAlchemy?
How to separate strings with','
How to RDP with Fedora31
How to Delete with SQLAlchemy?
How to keep conda off
[Linux] How to deal with garbled characters when viewing files
How to cancel RT with tweepy
Python: How to use async with
How to deal with imbalanced data
How to install python-pip with ubuntu20.04LTS
How to install wkhtmltopdf (Amazon Linux2)
How to deal with imbalanced data
How to install VMware-Tools on Linux
How to install MBDyn (Linux Ubuntu)
How to get started with Scrapy
How to get started with Python
How to deal with DistributionNotFound errors
How to get started with Django
How to Data Augmentation with PyTorch
How to use FTP with Python
How to calculate date with python
How to install mysql-connector with pip3
How to check Linux OS version
How to INNER JOIN with SQLAlchemy
How to install Anaconda with pyenv
How to authenticate with Django Part 2
How to authenticate with Django Part 3
How to get all traffic through VPN with OpenVPN on Linux
How to do arithmetic with Django template
How to build my own Linux server
[Blender] How to set shape_key with script
How to get parent id with sqlalchemy
How to add a package with PyCharm
I want to know how LINUX works!
How to install DLIB with 2020 / CUDA enabled
How to use ManyToManyField with Django's Admin
How to use OpenVPN with Ubuntu 18.04.3 LTS
How to use Cmder with PyCharm (Windows)
[Linux] How to use the echo command
How to work with BigQuery in Python
How to use the Linux grep command
How to update php on Amazon linux 2
How to use Ass / Alembic with HtoA
How to deal with enum compatibility errors
How to display emoji on Manjaro Linux
How to use Japanese with NLTK plot
How to do portmanteau test with python
How to search Google Drive with Google Colaboratory
How to display python Japanese with lolipop
How to install packages on Alpine Linux
How to download youtube videos with youtube-dl
Connect to GNU / Linux with Remote Desktop
How to use jupyter notebook with ABCI
How to install Anisble on Amazon Linux 2
How to operate Linux from the console
How to install Windows Subsystem For Linux