summaryrefslogtreecommitdiff
path: root/md/writeup/raspberry5_baremetal_uart.md
blob: f6500da83837dff9891a25bed47f162fdcb804ab (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
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