summaryrefslogtreecommitdiff
path: root/md/writeup/raspberry5_baremetal_uart.md
diff options
context:
space:
mode:
Diffstat (limited to 'md/writeup/raspberry5_baremetal_uart.md')
-rw-r--r--md/writeup/raspberry5_baremetal_uart.md255
1 files changed, 255 insertions, 0 deletions
diff --git a/md/writeup/raspberry5_baremetal_uart.md b/md/writeup/raspberry5_baremetal_uart.md
new file mode 100644
index 0000000..f6500da
--- /dev/null
+++ b/md/writeup/raspberry5_baremetal_uart.md
@@ -0,0 +1,255 @@
+title: Raspberry 5 baremetal UART example
+keywords:raspi5,asm,c,kernel,arm64,uart,tty,serial
+
+# Raspberry 5 bare metal UART example
+
+
+## Intro
+
+As base can check the [/writeup/raspberry5_baremetal_helloworld.md](/writeup/raspberry5_baremetal_helloworld.md)
+
+
+Getting basic communication for Raspberry 5 UART bare metal.
+
+## Enabling UART
+
+To find out MMIO base for UART, first step to set flag so
+PCIe bus doesn't reset UART,
+
+```
+enable_rp1_uart=1
+```
+
+if flag is set then UART will output string
+```
+RP1_UART 0000001c00030000
+```
+
+where MMIO base is
+
+```c
+#define MMIO_BASE 0x1c00000000UL
+```
+
+and UART base
+```c
+#define PL011_REG_OFFSET MMIO_BASE+0x30000
+```
+
+## Configure UART
+
+Disable UART before configure it and then set desired settings.
+This case just to set to 8 bit mode. After configuration done
+enable RX/TX and UART.
+
+```c
+ //disable UART
+ pl011_write32(PL011_CR,0x0);
+ //config uart
+ pl011_write32(PL011_LCRH, PL011_LCRH_WLEN_8BIT);
+ //enable UART
+ pl011_write32(
+ PL011_CR,
+ PL011_CR_UARTEN|PL011_CR_TXE|PL011_CR_RXE
+ );
+```
+
+## Sending first characters
+
+Sending characters is done by writing 8 bits to register DR
+
+```c
+#define PL011_DR 0x00 /* Data read or written from the interface. */
+```
+
+if just write then raspi5 will do it faster then UART can process and
+characters will be lost in basic case only first 2 characters where out.
+So before writing to DR check that TX FIFO is not full and wait if its full
+
+```c
+void pl011_putc(const uint8_t c) {
+ while (pl011_read32(PL011_FR)&PL011_FR_TXFF);
+ pl011_write32(PL011_DR,c);
+}
+```
+
+## Receiving characters
+
+to receive characters read register DR and to check if RX is empty
+check registers FR RX FIFO Empty flag.
+
+```c
+uint8_t pl011_getc() {
+ return pl011_read8(PL011_DR);
+}
+```
+
+## Basic command line
+
+Here is basic echo terminal loop that echo's back characters sent over UART.
+Loop checks FR register to see if RX FIFO isn't empty, and read character.
+Extra processing set for Enter to output "\r\n" and move to new line
+
+```c
+while (1) {
+ uint32_t rx_status = pl011_read32(PL011_FR);
+ //empty? if not printout
+ if (!(rx_status&PL011_FR_RXFE)) {
+ uint8_t c = pl011_read8(PL011_DR);
+ if (c == 0xD) {
+ pl011_puts("\r\n");
+ } else {
+ pl011_write8(PL011_DR,c);
+ }
+ }
+ }
+```
+
+## Source code
+For base setup check hello world example [/writeup/raspberry5_baremetal_helloworld.md](/writeup/raspberry5_baremetal_helloworld.md)
+
+### pl011.h
+```c
+#ifndef __PL011_H
+#define __PL011_H
+
+//#include <stdint.h>
+
+typedef unsigned char uint8_t;
+typedef unsigned short uint16_t;
+typedef unsigned int uint32_t;
+
+//static for now for raspi5
+#define MMIO_BASE 0x1c00000000UL
+#define PL011_REG_OFFSET MMIO_BASE+0x30000
+
+
+#define PL011_DR 0x00 /* Data read or written from the interface. */
+
+#define PL011_FR 0x18 /* Flag register (Read only). */
+#define PL011_IBRD 0x24 /* Integer baud rate divisor register. */
+#define PL011_FBRD 0x28 /* Fractional baud rate divisor register. */
+#define PL011_LCRH 0x2c /* Line control register. */
+#define PL011_CR 0x30 /* Control register. */
+#define PL011_IFLS 0x34 /* Interrupt fifo level select. */
+#define PL011_IMSC 0x38 /* Interrupt mask. */
+#define PL011_RIS 0x3c /* Raw interrupt status. */
+#define PL011_MIS 0x40 /* Masked interrupt status. */
+#define PL011_ICR 0x44 /* Interrupt clear register. */
+#define PL011_DMACR 0x48 /* DMA control register. */
+
+#define PL011_FR_RXFE (1 << 4)/* RX FIFO Empty */
+#define PL011_FR_TXFF (1 << 5)/* TX FIFO full */
+
+#define PL011_LCRH_WLEN_8BIT (3 << 5) /* 8bit */
+
+#define PL011_CR_CTSEN (1 << 15) /* CTS hardware flow control enable */
+#define PL011_CR_RTSEN (1 << 14) /* RTS hardware flow control enable */
+#define PL011_CR_RTS (1 << 11) /* Request to send */
+#define PL011_CR_DTR (1 << 10) /* Data transmit ready. */
+#define PL011_CR_RXE (1 << 9) /* Receive enable */
+#define PL011_CR_TXE (1 << 8) /* Transmit enable */
+#define PL011_CR_LBE (1 << 7) /* Loopback enable */
+#define PL011_CR_UARTEN (1 << 0) /* UART Enable */
+
+#define pl011_read8(offset) (*(volatile uint8_t *)(PL011_REG_OFFSET+offset))
+#define pl011_write8(offset,val) (*(volatile uint8_t *)(PL011_REG_OFFSET+offset)) = val
+#define pl011_read32(offset) (*(volatile uint32_t *)(PL011_REG_OFFSET+offset))
+#define pl011_write32(offset,val) (*(volatile uint32_t *)(PL011_REG_OFFSET+offset)) = val
+
+void pl011_init();
+void pl011_putc(const uint8_t c);
+void pl011_puts(const char *str);
+uint8_t pl011_getc();
+
+```
+### pl011.c
+```c
+
+#include "pl011.h"
+
+void pl011_init() {
+ //disable UART
+ pl011_write32(PL011_CR,0x0);
+ //config uart
+ pl011_write32(PL011_LCRH, PL011_LCRH_WLEN_8BIT);
+ //enable UART
+ pl011_write32(
+ PL011_CR,
+ PL011_CR_UARTEN|PL011_CR_TXE|PL011_CR_RXE
+ );
+}
+
+void pl011_putc(const uint8_t c) {
+ while (pl011_read32(PL011_FR)&PL011_FR_TXFF);
+ pl011_write32(PL011_DR,c);
+}
+
+void pl011_puts(const char *str) {
+ uint16_t cnt = 0;
+ while (cnt<256) {
+ if (str[cnt] != 0) {
+ pl011_putc(str[cnt]);
+ } else {
+ break;
+ }
+ cnt++;
+ }
+}
+uint8_t pl011_getc() {
+ return pl011_read8(PL011_DR);
+
+}
+```
+
+### kernel.c
+```c
+#include <pl011.h>
+
+void main()
+{
+ pl011_init();
+ pl011_puts("Raspberry Pi5 UART demo?!\r\n");
+ while (1) {
+ uint32_t rx_status = pl011_read32(PL011_FR);
+ //empty? if not printout
+ if (!(rx_status&PL011_FR_RXFE)) {
+ uint8_t c = pl011_read8(PL011_DR);
+ if (c == 0xD) {
+ pl011_puts("\r\n");
+ } else {
+ pl011_write8(PL011_DR,c);
+ }
+ }
+ }
+
+}
+```
+
+Creating Makefile and linker script is left as exercise
+
+## Links
+https://github.com/BarrelfishOS/barrelfish/blob/master/kernel/arch/arm/pl011.c
+https://github.com/DSERIOUSGUY/HobOS/blob/main/uart.c
+https://github.com/rsta2/circle/blob/master/lib/serial.cpp
+https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/tty/serial/amba-pl011.c?h=v6.16-rc4
+https://developer.arm.com/documentation/ddi0183/latest/
+[/writeup/raspberry5_baremetal_helloworld.md](/writeup/raspberry5_baremetal_helloworld.md)
+https://wiki.osdev.org/Detecting_Raspberry_Pi_Board
+https://github.com/dwelch67/raspberrypi/tree/master/uart01
+https://github.com/bztsrc/raspi3-tutorial/blob/master/03_uart1/uart.c
+https://www.rpi4os.com/part3-helloworld/
+https://www.valvers.com/open-software/raspberry-pi/bare-metal-programming-in-c-part-5/#aux-peripheral
+https://github.com/valvers/arm-tutorial-rpi/tree/master/part-5
+https://jsandler18.github.io/tutorial/boot.html
+https://github.com/ARM-software/arm-trusted-firmware/blob/master/plat/rpi/rpi5/include/rpi_hw.h
+https://github.com/ARM-software/arm-trusted-firmware/blob/master/drivers/arm/pl011/aarch64/pl011_console.S
+https://elixir.bootlin.com/linux/v6.15.5/source/include/linux/amba/serial.h#L39
+https://github.com/ARM-software/arm-trusted-firmware/blob/master/include/drivers/arm/pl011.h
+[/writeup/raspberry5_baremetal_helloworld.md](/writeup/raspberry5_baremetal_helloworld.md)
+https://krinkinmu.github.io/2020/11/29/PL011.html
+https://forums.raspberrypi.com/viewtopic.php?t=34112
+https://datasheets.raspberrypi.com/bcm2711/bcm2711-peripherals.pdf
+https://datasheets.raspberrypi.com/rp1/rp1-peripherals.pdf
+https://github.com/raspberrypi/linux/blob/rpi-6.12.y/arch/arm64/boot/dts/broadcom/bcm2712.dtsi
+https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf \ No newline at end of file