Skip to content

Commit a26dda0

Browse files
committed
fix(09): Correct PL011 UART newline and add Pi 5 baud rate
Makes two improvements to the shared bcm2xxx_pl011_uart driver: 1. Conditionally sets the baud rate to 115200 when compiling for 'bsp_rpi5'. 2. Fixes terminal display by implementing '\n' to '\r\n' translation in the 'write_char' function, ensuring correct cursor positioning for all output.
1 parent e98a9c2 commit a26dda0

File tree

1 file changed

+36
-22
lines changed

1 file changed

+36
-22
lines changed

09_privilege_level/src/bsp/device_driver/bcm/bcm2xxx_pl011_uart.rs

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// SPDX-License-Identifier: MIT OR Apache-2.0
22
//
3-
// Copyright (c) 2018-2023 Andre Richter <andre.o.richter@gmail.com>
3+
// Copyright (c) 2018-2025 Andre Richter <andre.o.richter@gmail.com>
44

55
//! PL011 UART driver.
66
//!
@@ -201,22 +201,6 @@ impl PL011UartInner {
201201
}
202202

203203
/// Set up baud rate and characteristics.
204-
///
205-
/// This results in 8N1 and 921_600 baud.
206-
///
207-
/// The calculation for the BRD is (we set the clock to 48 MHz in config.txt):
208-
/// `(48_000_000 / 16) / 921_600 = 3.2552083`.
209-
///
210-
/// This means the integer part is `3` and goes into the `IBRD`.
211-
/// The fractional part is `0.2552083`.
212-
///
213-
/// `FBRD` calculation according to the PL011 Technical Reference Manual:
214-
/// `INTEGER((0.2552083 * 64) + 0.5) = 16`.
215-
///
216-
/// Therefore, the generated baud rate divider is: `3 + 16/64 = 3.25`. Which results in a
217-
/// genrated baud rate of `48_000_000 / (16 * 3.25) = 923_077`.
218-
///
219-
/// Error = `((923_077 - 921_600) / 921_600) * 100 = 0.16%`.
220204
pub fn init(&mut self) {
221205
// Execution can arrive here while there are still characters queued in the TX FIFO and
222206
// actively being sent out by the UART hardware. If the UART is turned off in this case,
@@ -240,9 +224,26 @@ impl PL011UartInner {
240224
// updated on a single write strobe generated by a LCR_H write. So, to internally update the
241225
// contents of IBRD or FBRD, a LCR_H write must always be performed at the end.
242226
//
243-
// Set the baud rate, 8N1 and FIFO enabled.
244-
self.registers.IBRD.write(IBRD::BAUD_DIVINT.val(3));
245-
self.registers.FBRD.write(FBRD::BAUD_DIVFRAC.val(16));
227+
// Set the baud rate.
228+
#[cfg(feature = "bsp_rpi5")]
229+
{
230+
// For Pi 5, use the known-good 115200 baud configuration.
231+
// This is proven to work with the default 48MHz UART clock.
232+
// BAUDDIV = 48,000,000 / (16 * 115200) = 26.0416...
233+
// IBRD = 26, FBRD = 3
234+
self.registers.IBRD.write(IBRD::BAUD_DIVINT.val(26));
235+
self.registers.FBRD.write(FBRD::BAUD_DIVFRAC.val(3));
236+
}
237+
#[cfg(not(feature = "bsp_rpi5"))]
238+
{
239+
// Original configuration for RPi3/4 at 921600 baud.
240+
// BAUDDIV = 48_000,000 / (16 * 921_600) = 3.2552...
241+
// IBRD = 3, FBRD = 16
242+
self.registers.IBRD.write(IBRD::BAUD_DIVINT.val(3));
243+
self.registers.FBRD.write(FBRD::BAUD_DIVFRAC.val(16));
244+
}
245+
246+
// Set 8N1 and FIFO enabled.
246247
self.registers
247248
.LCR_H
248249
.write(LCR_H::WLEN::EightBit + LCR_H::FEN::FifosEnabled);
@@ -255,7 +256,18 @@ impl PL011UartInner {
255256

256257
/// Send a character.
257258
fn write_char(&mut self, c: char) {
258-
// Spin while TX FIFO full is set, waiting for an empty slot.
259+
// If the character is a newline, prepend a carriage return.
260+
if c == '\n' {
261+
// Spin while TX FIFO full is set.
262+
while self.registers.FR.matches_all(FR::TXFF::SET) {
263+
cpu::nop();
264+
}
265+
// Write the carriage return character.
266+
self.registers.DR.set('\r' as u32);
267+
}
268+
269+
// Now, send the original character.
270+
// Spin while TX FIFO full is set.
259271
while self.registers.FR.matches_all(FR::TXFF::SET) {
260272
cpu::nop();
261273
}
@@ -292,7 +304,8 @@ impl PL011UartInner {
292304
// Read one character.
293305
let mut ret = self.registers.DR.get() as u8 as char;
294306

295-
// Convert carrige return to newline.
307+
// Convert carriage return to newline. This is a standard behavior
308+
// for console input.
296309
if ret == '\r' {
297310
ret = '\n'
298311
}
@@ -316,6 +329,7 @@ impl PL011UartInner {
316329
impl fmt::Write for PL011UartInner {
317330
fn write_str(&mut self, s: &str) -> fmt::Result {
318331
for c in s.chars() {
332+
// The `write_char` function now correctly handles `\n` -> `\r\n` conversion.
319333
self.write_char(c);
320334
}
321335

0 commit comments

Comments
 (0)