The issue turned out to be incorrect configuration of the console UART. It seems that if the wrong UART is selected then bl31 gets stuck (and of course no console output appears in this case).
By default, ATF defines IMX_BOOT_UART_BASE=0x30890000
which is the address for UART2. This aligns with the block diagram supplied by Phytec 1, which incorrectly shows the serial debug console wired to UART2. In fact, the console is wired to UART1 (0x30860000).
Setting IMX_BOOT_UART_BASE=0x30860000
enables ATF to access the console and allows the boot process to continue.
Thanks to @Frant for the helpful suggestions - while the issue turned out to be something else, the suggestion to print the contents of x0 on the UART led me down the right path to find the real problem.