raspberrypi / firmware

This repository contains pre-compiled binaries of the current Raspberry Pi kernel and modules, userspace libraries, and bootloader/GPU firmware.
5.15k stars 1.68k forks source link

Raspberry Pi 3 model B Serial console does not use correct baudrate #553

Closed josn0 closed 8 years ago

josn0 commented 8 years ago

Serial console seems to give a incorrect baudrate. Overriding frequencies to previous Raspberries in /boot/config.txt helps: everything works as normal. After some iterations, I found that setting 'gpu_freq=300' solves the problem, although that is the default setting on a rpi 3. Probably someone reads that value to initialise the baudrate generator, and uses the old default 'gpu_freq=250' if the value is not set. This is almost certainly done in the closed-source /boot/start*.elf files. All those files do contain the texts 'config.tst' as well as 'gpu_freq'. Please repair this issue..

pelwell commented 8 years ago

An issue with gpu_freq/core_freq handling was discovered and fixed in an rpi-update release made on the 29th, but that hasn't made its way to a Raspbian build yet. You can run "sudo rpi-update" to get the latest firmware, wait for a Raspbian update, or use a workaround: set the frequency using core_freq instead of gpu_freq.

josn0 commented 8 years ago

Thanks for the response. I tried a rpi-update; it did not help. Then tried adding 'core_freq=300'; that did not help either. Put back my 'gpu_freq=300', which again solved the problem. So it seems the issue is not fixed correctly.

pelwell commented 8 years ago

It looks like there might be a clocking issue - we will investigate.

pelwell commented 8 years ago

My Pi3 serial port (ttyS0) has been rock solid with force_turbo=1 and a 2.5A power supply. I think you may be suffering from under-voltage. The gpu_freq=300 setting will have the effect of scaling back all of the non-ARM clocks, which will save power.

TheDJVG commented 8 years ago

@pelwell, I don't get if this issue will be resolved or not. I'm using a 2.5A power supply and force_turbo which makes it work again but I think some firmware needs to be updated to make it work the right way (without force_turbo) or am I missing something?

pelwell commented 8 years ago

See my answer in this thread: https://github.com/RPi-Distro/repo/issues/22

gejanssen commented 8 years ago

have the same problem. unfortunately the gpu_freq setting does not work. Also the new firmware of today works partly. booting is garbled and after that the login prompt is showing. 21B�b #) 2lL`~Bn �nn bnbLlbr2nbBbl Raspbian GNU/Linux 8 rpib3 ttyS0

rpib3 login: gej

now running version 30fe8178d61c1ff9bc168edaafdbcb101aa6245e (05-02)

pelwell commented 8 years ago

If the red power LED flickers then your power supply isn't sufficient. You need a good 2.5A supply. There are extra current demands at boot time, with WiFi and Bluetooth starting and increased parallelism thanks to systemd.

gejanssen commented 8 years ago

@pelwell The powersupply used is a 5A powersupply and the red led is not flickering. Also found on another posting that if I use force_turbo=1 arm_freq=1200 core_freq=250 and fixing the cpu frequence, gej@rpib3:~ $ cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq 1200000 gej@rpib3:~ $ cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq 1200000 gej@rpib3:~ $ cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq 1200000 the serial works perfectly also the bluethooth works gej@rpib3:~$ sudo hcitool scan Scanning ... 10:3B:59:F5:65:6A GT-I9305N gej@rpib3:~$

pelwell commented 8 years ago

It's the core_freq=250 that makes the difference - if your maximum core freq is the same as the minimum core freq then it never changes.

TheDJVG commented 8 years ago

@pelwell Maybe I just don't get it but I'm trying to make it clear. I'm now using a power supply that can deliver up to 12A of current but I still need force_turbo to make the serial output work (= readable). Peak current is around 2.3A at boot. Is the need of force_turbo a workaround for an unresolved bug in the firmware or what is causing this? I don't have this problem with all the other models, that's why I'm asking and trying to understand.

gejanssen commented 8 years ago

I can confirm that only the core_freq=250 works. gej@rpib3:~$ cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq 1200000 gej@rpib3:~$ gej@rpib3:~$ cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq 600000 gej@rpib3:~$ sudo hcitool scan Scanning ... C8:29:2A:1B:68:74 LGE DTV BCM20702A1 gej@rpib3:~$ At full load an minimal load the bluetooth and serial works.

pelwell commented 8 years ago

The answers are in the other thread (https://github.com/RPi-Distro/repo/issues/22), but the short version is that the clock is also reduced if the chip is getting too hot. By setting the core VPU clock to the minimum value (250) you avoid this problem.

swarren commented 8 years ago

I pulled down the latest firmware, and I still see some issues re: serial console. Let me know if I should file this as a separate bug. I think it's within the realm of this bug though.

On the RPi 0/1/2, whatever is loaded as kernel.img can immediately start writing to the UART's TX FIFO, and that data will appear on the GPIO header pin at the expected baud rate.

However, on the RPi 3, the following steps are required before this works. I believe the firmware should provide the same guarantees/initial-conditions on all system, so none of these steps should be necessary:

1) Configure GPIO14/15 pinmux to be ALT5 to select the mini UART. 2) Write to the AUX block's ENABLES register to enable the mini UART. 3) Configure the mini UART for 8-bits (it defaults to 7). 4) Program the UART baud-rate divider.

For reference, see the most recent commit at https://github.com/swarren/u-boot/commits/rpi_dev.

(For the RPi 0/1/2, we do actually do step 4 too. However, Eric Anholt has submitted a patch to skip this so that U-Boot honors whatever the firmware programmed, which means it follows whatever the user requested via config.txt, which makes for a nicer user experience).

One particularly nice benefit of the firmware doing this is that step 4 can be skipped, which means U-Boot wouldn't have to query the core clock rate from the firmware in order to calculate the divider. (Assuming the core clock rate hasn't changed, but there really isn't anything we can do about that.)

pelwell commented 8 years ago

Before (considering) undertaking this work, I'd like to clarify something.

I had considered the fact that the firmware leaves uart0 mapped to GPIOs 14 and 15 to be anomalous - it seems to go against the DT philosophy of only enabling that which is requested in the DTB. Are you saying that having a UART enabled at boot is the preferred behaviour?

swarren commented 8 years ago

DT isn't meant to prescribe a certain HW configuration; it's simply there to represent the actual HW setup. So, whether the GPIO pins are "pre-muxed" to the UART or not is entirely unrelated to DT.

Most pins on the GPIO expansion header are set up as GPIO in by default solely because there's no way to know ahead of time what the user will use those pins for. This means that if the RPi was to actively drive those pins to a particular output state at boot, then one/both of the following could happen:

The only way to prevent those potential issues is to have the RPi not actively drive the signals by default, until explicitly instructed (via HAT EEPROM content for example) how those signals will be used (or should be configured).

However, some of the GPIO expansion header signals have a known dedicated purpose. For example, two of the pins are used for the HAT EEPROM I2C bus. For pins with such a dedicated purpose, it is entirely safe to configure them at boot, since it's known which are input vs. output etc.

I would argue that the console UART pins fall into this category; they have a dedicated purpose. I don't sure if the HAT specification defines those pins with a fixed purpose, but the Raspbian images certainly use those pins for serial console by default without explicit acknowledgement-from/interaction-with the end-user, so at the very least they have a de-facto dedicated purpose.

Even if the user were to connect the UART pins to some other external device (i.e. not a host's serial port) in an external circuit, they'd have to do so with the full knowledge that a standard RPi SW stack would use the TX pin as an output, and toggle it by default with serial data, and hence they would have to design their external circuit to be compatible with this fact.

My expectation is that the FW would configure all fixed-purpose pins at boot but obviously leave all the undedicated pins at the SoC default (GPIO in w/ pull). IIUC, this is exactly what it does on RPi 0/1/2.

At least, that's the reasoning NVIDIA applied when choosing the default pinmux setup for the Jetson TX1 Developer Kit's GPIO expansion header, which happens to have the same pinout as the RPi 40-pin connector.

BTW, what do config.txt's init_uart_baud and init_uart_clock options do on the RPi 3? Do they still affect the PL01x UART? I suppose that is backwards compatible in a sense, but if you consider those options to semanticalyl be related to configuration of the console (rather than configuration of a particular UART HW module), then they should configure the mini UART.

pelwell commented 8 years ago

Thanks for the clarification.

Currently both init_uart_clock and init_uart_baud still apply to the PL01x. init_uart_clock is meaningless for the mini-UART since the source clock is the core clock, but I think it would be reasonable to pre-configure the mini-UART using the value in init_uart_baud.

swarren commented 8 years ago

pelwell, did you get a chance to think about this? I'd like to get the U-Boot support for RPi 3 upstream ASAP (i.e. within the next ~3 weeks, before the current merge window closes), and it'd be nice not to have to include all the extra pinmux and UART setup code if at all possible. Thanks!

pelwell commented 8 years ago

I'm optimistic I can look at this on Thursday, but probably not before.

swarren commented 8 years ago

Hmm. The existence of pi3-disable-bt-overlay.dts and pi3-miniuart-bt-overlay.dts will complicated this rather. How is code other than the DT-aware Linux kernel (e.g. consider U-Boot, bare metal code, RiscOS?) meant to know whether the mini UART or the regular UART is supposed to be used? I'm confused why there are overlays to promote all kinds of different HW setups rather than just standardizing on one working configuration.

pelwell commented 8 years ago

The overlays use DT aliases (serial0 and serial1) to indicate which UART is the primary in the sense that it has the console. The firmware already reads these aliases, using them to rewrite cmdline.txt replacing serial0 and serial1 with the appropriate UART for the platform, so it should simply be a matter of writing some initialisation code for the mini UART and calling it as necessary.

Some of our users rely on the more capable (larger FIFOs, 8+1 bits) UART for their own applications and are prepared to accept lower BT performance (or forego it completely). Other OSs are free to implement any policy they like - forcing a particular hardware configuration, for example - but as long as Linux has the flexibility why wouldn't we give users the choice?

popcornmix commented 8 years ago

@swarren there is a test firmware here: https://dl.dropboxusercontent.com/u/3669512/temp/firmware_uart.zip

It includes this from @pelwell

This is my patch to fulfil Stephen Warren's request of initialising the mini-UART on Pi3. It is smart enough to read the DT after the overlays have been applied, and only initialise the mini-UART when the UARTs are swapped.

It will appear in next official firmware update (probably in the next few days).

swarren commented 8 years ago

@popcornmix I'm afraid that firmware doesn't work for me; I just get a rainbow square on HDMI and nothing else. Switching back to the latest files in firmware.git/boot/ works fine, and I changed nothing else either changing to or from the FW in that .zip file.

swarren commented 8 years ago

The situation is a bit more complicated with that firmware than simply "doesn't work":

On the RPi3, in either 32- or 64-bit mode, with U-Boot hard-coded to use the mini UART, everything works, with or without U-Boot initializing the pinmux or baud rate of the mini UART. That last part is good since it means the enhancements work.

On the RPi 3 with U-Boot hard-coded to use the PL01x (and without any DT overlay specified in config.txt to request the FW/kernel do the same), I see the HDMI rainbow. I guess when the ARM touches the unclocked/reset PL01x, there's some system-/bus-level error which triggers the rainbow?

On the RPi2, with U-Boot hard-coded to use either the PL01x or mini UART, I see the HDMI rainbow.

On an RPi1 B+, with U-Boot hard-coded to use a PL01x, I see the HDMI rainbow.

On an RPi1 B+, with U-Boot hard-coded to use the mini UART, it seems like the system is rapidly repeatedly rebooting without fully achieving HDMI sync, since my HDMI monitor keeps repeating the "no signal" display in a way that I think means sync is appearing and disappearing. Or, perhaps the Pi is simply hanging with no HDMI output at all.

So I guess that firmware was tested on the RPi3 but not elsewhere, where the DT defaults or alias values for the UART selection are different?

swarren commented 8 years ago

Sorry, I think the issues I saw with the RPi2 and RPi1 B+ were because I'd forgotten to disable the config.txt options I used to make AArch64 booting work on the RPi3. I've now seen both of those boards work with the new firmware. But now I'm not convinced the new FW is actually enabling the mini UART in the aux module, setting its baud rate, and setting up the pinmux, although I swear this did work before. I think I'm going to give up on testing this tonight and come back some other time.

swarren commented 8 years ago

@pelwell, can you please confirm exactly what the latest firmware is doing w.r.t. UART initialization? I was expecting the following logic in the update FW for this issue:

if (DT indicates PL01x is the console) { do whatever the previous FW revs always used to do to initialize the PL01x (likely: program its baud rate, enable it in UART CR reg, program pinmux to route it to GPIO header) } else { new code to do whatever it takes to initialize the mini UART (likely: enable it in the AUX regs, set baud rate divider, set any enable bits in the UART regs, program pinmux to route it to GPIO header) }

... and furthermore, the "if" block should trigger on any RPi 0/1/2, and the "else" block should trigger on the RPi 3, all assuming no overlays are loaded to select a different UART than is default in the DT files.

I've been testing on a B+ with U-Boot build configuration "rpi_1", with empty config.txt:

With FW commit c230b2b9b5a3 (most recent but one), I find that the PL01x is initialized by VC FW as expected. The UART works fine irrespective of whether U-Boot programs the baud rate divider or toggles the enable bits in the UART CR register.

However, with the most recent FW commit 4bf906cdd221, I see:

This implies to me that something about the VC FW's PL01x initialization isn't happening as expected. Could you please double-check that.

I have not yet attempted to re-test all this on an RPi 2 or 3; it took me rather a long time to convince myself all of the above was true and not randomness, my imagination, or me making mistakes in the config file again:-(

popcornmix commented 8 years ago

Is the rainbow drawn initially and simply left on screen if subsequent ARM SW doesn't draw over the top of it, or does the VC FW draw it in response to some error condition it detects?

The former. The rainbow screen is drawn by firmware before loading the kernel. It will only disappear when the arm has configured the framebuffer through the mailbox interface. If it remains on screen it suggests the arm code didn't load and run as expected.

swarren commented 8 years ago

The hang I mentioned in U-Boot is probably due to the UART not being enabled and hence the TX FIFO never draining, so U-Boot waits forever for space in the FIFO when printing its first message, so never gets to the code that initializes the FB. So, both my observations with the most recent FW can be explained simply by it now not performing the UART initialization.

pelwell commented 8 years ago

I will get back to you tomorrow after further testing,

pelwell commented 8 years ago

I have tried on a B+, a 2 and a 3, and the new firmware works as expected provided the object you are loading is marked as DT-capable and you give it a valid device tree. If you don't, and I suspect you aren't tagging the U-Boot image with the RPTL trailer with the DTOK flag set - why would you? - then it will initialise the mini-UART. This is not the intended behaviour, and will be fixed in the next release.

popcornmix commented 8 years ago

@swarren If you want to test the upcoming firmware early then try: https://dl.dropboxusercontent.com/u/3669512/temp/firmware_uart2.zip

pelwell commented 8 years ago

TL;DR: If you want to use the UART console during booting on a Pi3 then you need to add enable_uart=1 to config.txt. Also, you can use the normal UART pins for other functions without pinctrl getting in the way.

A new firmware package has been released to both rpi-update branches - master and next. In time it will roll out to apt-get upgrade and a full Raspbian release. It includes the following key changes:

  1. The automatic UART initialisation should work on all RPis, whether or not the loaded kernel/U-Boot is DT-capable. The choice of UART to initialise is controlled by the "serial" aliases in the DTB, as set by the pi3-miniuart-bt and pi3-disable-bt overlays, but the default is the mini-UART on Pi3 and PL011 on other models. Note that if a custom dt-blob is in use that remaps pins 14 & 15 to something other than TXD0/RXD0 (even on a Pi3 - don't hard-code TXD1/RXD1) then the pin function is left unchanged to allow, for example, the VGA666 interface to be used.
  2. A new config.txt setting has been introduced - enable_uart - the purpose of which is to control the initialisation of the UART(*). The default value varies according to the primary UART on the platform - if the PL011 is the primary UART (that used for the console and boot messages) then enable_uart defaults to 1, and if the mini-UART is the primary then it defaults to 0.

    The reason for the additional setting is that making use of the mini-UART requires the core frequency to be fixed, so setting enable_uart will limit the core to the minimum (unless force_turbo is set, since that will fix the core frequency to the maximum, provided the Pi is adequately powered and cooled). The previous default behaviour, which rendered much of the output illegible, was not useful, but we didn't want to restrict all users (even those who don't care about the UART) to the minimum core frequency. We also wanted to be able to remove the restriction when the PL011 is chosen on a Pi3, hence the need for a dedicated setting indicating the reason for the frequency limit.

  3. Now that the firmware initialises either UART as needed, it is no longer necessary to claim the UART pins (GPIOs 14 & 15) using pinctrl, so the base per-platform DTBs no longer do this. This makes it easy to use an overlay to change the pin functions without first having to change the pins claimed by one of the two UARTs.

(*) Failing to configure a UART can lead to a lock-up due to polling indefinitely for some operation to complete. Instead, always initialise the UART, but leave the pins set to be inputs (unless overridden by a custom dt-blob.bin).

clivem commented 8 years ago

@pelwell Condensing these paragraphs down to one sentence......

If you wish to use the serial console via GPIO header on Pi3B, (where mini-UART is the default), you need to set "enable_uart=1" in /boot config.txt or the UART will not be initialised. The downside of doing so is that core freq will be fixed to 250MHz, unless you also enable "force_turbo", which will result in core freq being fixed at 400MHz..... Is that about the sum of it in a single sentence?

clivem commented 8 years ago

$ cat /boot/config.txt | grep enable_uart enable_uart=1 $ vcgencmd version Mar 21 2016 18:53:14 Copyright (c) 2012 Broadcom version 7a1f4e062023db8cacbca7ee8ff77d5b185c2a63 (clean) (release) $ vcgencmd get_config int | grep -e core_freq -e enable_uart core_freq=400

Is this latest firmware observing the enable_uart parameter?

swarren commented 8 years ago

@pelwell thanks - the new FW revision is much better. I've tested on a RPI B+ and RPi 2 without any issues, including validating the default state of enable_uart when not present in config.txt and also that if U-Boot does actually re-program the PL011 baud rate divider, everything works:-) Thanks for the quick response.

There's one small issue on the RPi3 though (I've only tested in 32-bit mode so far, but I imagine that is not relevant) when the mini UART is used. The baud rate divider appears to be programmed assuming the core clock is 400MHz (FW programs 0x1d1, and 0x1d1 * 115200 * 8 == 400MHz), which yields a corrupted UART. If I make U-Boot re-program the divider assuming a 250MHz core clock (0x10e * 115200 * 8 == 250MHz), then the UART works fine. I guess the divider is calculated before the core clock is clipped to the low rate? My config.txt for the RPi3 in 32-bit mode contains only enable_uart=1 and nothing else.

The RPi 3 (well, mini UART) pinmux and AUX enable reigster programming seem to be working just fine though:-)

I guess I didn't test with an explicit enable_uart=0. I'll also try to get 64-bit U-Boot building against latest mainline. I'll go do those now but won't report back unless something is wrong.

swarren commented 8 years ago

It seems like enable_uart=0 does not have the expected effect on either the RPi2 or the RPi3, and if I don't put any enable_uart= line in config.txt on the RPi3, then the UART is still initialized, leading me to suspect the default is 1 even on the RPi3 (or even with the mini UART as the primary UART).

pelwell commented 8 years ago

Thanks for the feedback. The version hash returned from both firmware builds from last night shows that they were freshly built, but without the most recent firmware commits. That means that section 2) of my extensive post does not yet apply, but 1) and 3) do. Sorry for the false start.

Both branches should be rebuilt in the next few hours, after which your observations re: the core clock and the divisor should no longer apply.

pelwell commented 8 years ago

I've personally tested both branches and can confirm that both include the commit required to implement enable_uart.

pi@raspberrypi:~$ vcgencmd get_config int | grep -e core_freq -e enable_uart -e force_turbo
core_freq=250
enable_uart=1

and

raspberrypi:~$ vcgencmd get_config int | grep -e core_freq -e enable_uart -e force_turbo
core_freq=400
enable_uart=1
force_turbo=1

(run over the ttyS0 console...)

lurch commented 8 years ago

What implications does this new enable_uart config.txt parameter have for raspi-config and Raspbian? Will turning the serial console on and off in raspi-config also set the enable_uart=1 parameter?

Up until now Raspbian has always shipped with the serial console enabled by default, but I guess you'd also want Raspbian to ship with the 400MHz core_freq (on Pi3) by default, since that'll be more useful to most people than having the serial console enabled by default; so does that mean that Raspbian should only output bootup messages over the serial console if vcgencmd get_config enable_uart returns 1? (otherwise the output would be corrupted) Or will 'out of the box' Raspbian on a Pi3 always output corrupt bootup messages, unless you explicitly add enable_uart=1 to config.txt?

Or am I misunderstanding things?

pelwell commented 8 years ago

The only implication for Raspbian is that raspi-config should toggle enable_uart when the serial console is configured. It is safe to continue to send output to the UART when it is "disabled" because the UARTs are configured but disconnected from the GPIO pins. From today, a clean Raspbian install that has been rpi-updated will output to the serial port by default unless it is a Pi3, in which it is disabled unless enable_uart is set; it should never output corrupted data (unless you get over-temperature or under-voltage with force_turbo=1).

I'm in favour of making enable_uart=0 the default on all Pis, once people have had time to get used to the idea and we've not thought of any gotchas.

lurch commented 8 years ago

It is safe to continue to send output to the UART when it is "disabled" because the UARTs are configured but disconnected from the GPIO pins.

Ahh, that's the bit I was missing. Makes sense, thank you!

lurch commented 8 years ago

If the firmware is running in "NOOBS mode" i.e. it's loaded recovery.elf instead of start.elf, would it make sense to default to enable_uart=1 even on a Pi3 (i.e. force core_freq to 250), since NOOBS doesn't have any config.txt file by default, and I guess there's no real benefit to the higher core_freq when running NOOBS?

And once the Pi3A and CM3 are released, maybe the same 'enable_uart=1 by default' logic should apply to https://github.com/raspberrypi/tools/tree/master/test_code / https://github.com/raspberrypi/tools/tree/master/usbboot as well?

swarren commented 8 years ago

@pelwell I believe there's one more issue with firmware_uart2.zip: The UART RX FIFO is full of 0x00 characters right from the very start. U-Boot thinks that the user is typing a character to interrupt to automatic boot process so doesn't automatically continue to load boot scripts from the SD card etc. This only happens on the RPi3 (or perhaps more likely, only when the mini UART is the console UART). I suspect the UART is being enabled before the pinmux is programmed, so the UART input is clamped to 0 which leads to repeated reception of 0x00 characters until the pinmux is programmed.

pelwell commented 8 years ago

Start bits are 1 and stop bits are 0, so I would have thought that a fixed input of either 0 or 1 shouldn't cause any characters to be received. However, stepping through the code I see the FIFO continuously fill until the pinmux is configured. I have a patch that shuffles the initialisation order, which will be in the next firmware release.

swarren commented 8 years ago

Great. Note that the mini UART HW docs state that it has some implementation limitations, IIRC including not validating the value of stop bits.

pelwell commented 8 years ago

Indeed. From the ARM Peripherals doc:

The mini UART does not check if the stop bit is high or wait for the stop bit to appear. As a result of this a UART1_RX input line which is continuously low (a break condition or an error in connection or GPIO setup) causes the receiver to continuously receive 0x00 symbols.

The transistors saved might even run into double figures.

abossenbroek commented 8 years ago

I am not sure whether this is related but I am trying to connect using a serial USB cable to my RPI 3 with ArchLinux. I get all the boot messages which seem to come from GRUB but after that I cannot interact with ttyAMA0.

/opt/vc/bin/vcgencmd version
Mar 22 2016 11:40:00 
Copyright (c) 2012 Broadcom
version b9fa71d5dd50e5616ec9e908a1bb434872983939 (clean) (release)
/opt/vc/bin/vcgencmd  get_config int | grep -e core_freq -e enable_uart
core_freq=400
enable_uart=1
clivem commented 8 years ago

RPi3... Unless you changed it, default is mini UART, so you need to be accessing /dev/ttyS0, not /dev/ttyAMA0.

abossenbroek commented 8 years ago

To test I changed cmdline.txt from AMA0 to S0, rebooted but this caused no messages being shown on the remote computer at all.

swarren commented 8 years ago

@pelwell in the latest firmware in firmware.git, I see:

However, if I add the following into config.txt, I thought that it should allow code to use PL011 for the console. Instead, I see garbage output on the serial port and U-Boot hangs:

dtoverlay=pi3-miniuart-bt

In this case, I can tell U-Boot to enable PL011 (set up the baud rate divider and set any enable bits in the PL011) and U-Boot will boot (visible on HDMI) but still no serial IO works; I guess the pinmux isn't programmed either. Manual writes to either UART TX FIFO doesn't send data over the UART.