libplatsupport/bcm2711: PL011 + mini UART support The UART device numbering is now aligned with the device tree. BCM2711_UART0 previously referred to the serial1 device. Signed-off-by: Lukas Graber <lukas.graber@hensoldt-cyber.de>
diff --git a/libplatsupport/plat_include/bcm2711/platsupport/plat/serial.h b/libplatsupport/plat_include/bcm2711/platsupport/plat/serial.h index 9e7861d..3add49f 100644 --- a/libplatsupport/plat_include/bcm2711/platsupport/plat/serial.h +++ b/libplatsupport/plat_include/bcm2711/platsupport/plat/serial.h
@@ -6,8 +6,8 @@ */ #pragma once -#define BUS_ADDR_OFFSET 0x7E000000 -#define PADDDR_OFFSET 0xFE000000 +#define BUS_ADDR_OFFSET 0x7E000000 +#define PADDDR_OFFSET 0xFE000000 /* * The mini UART (UART1) is one of the 3 auxiliary @@ -16,9 +16,9 @@ * /soc/aux@7e215000 * /soc/serial@7e215040 */ -#define UART_BUSADDR 0x7E215000 +#define UART_BUSADDR 0x7E215000 -#define UART_PADDR_0 (UART_BUSADDR-BUS_ADDR_OFFSET+PADDDR_OFFSET) +#define UART_PADDR_1 (UART_BUSADDR - BUS_ADDR_OFFSET + PADDDR_OFFSET) /* * BCM2711 TRM * The mini UART (UART1) is part of AUX @@ -26,17 +26,27 @@ * section 6.3. GIC-400 - VC peripheral IRQs: 96 * => mini UART IRQ: 96 + 29 = 125 */ -#define UART_IRQ_0 (125) +#define UART_IRQ_1 (125) enum chardev_id { BCM2711_UART0, + BCM2711_UART1, + BCM2711_UART2, + BCM2711_UART3, + BCM2711_UART4, + BCM2711_UART5, NUM_CHARDEV, /* Aliases */ PS_SERIAL0 = BCM2711_UART0, + PS_SERIAL1 = BCM2711_UART1, + PS_SERIAL2 = BCM2711_UART2, + PS_SERIAL3 = BCM2711_UART3, + PS_SERIAL4 = BCM2711_UART4, + PS_SERIAL5 = BCM2711_UART5, /* defaults */ - PS_SERIAL_DEFAULT = BCM2711_UART0 + PS_SERIAL_DEFAULT = BCM2711_UART1 }; -#define DEFAULT_SERIAL_PADDR UART_PADDR_0 -#define DEFAULT_SERIAL_INTERRUPT UART_IRQ_0 +#define DEFAULT_SERIAL_PADDR UART_PADDR_1 +#define DEFAULT_SERIAL_INTERRUPT UART_IRQ_1
diff --git a/libplatsupport/src/plat/bcm2711/chardev.c b/libplatsupport/src/plat/bcm2711/chardev.c index dec89ef..fd222d2 100644 --- a/libplatsupport/src/plat/bcm2711/chardev.c +++ b/libplatsupport/src/plat/bcm2711/chardev.c
@@ -16,18 +16,36 @@ #include "serial.h" -static const int uart_irqs_0[] = {-1}; +static const int uart0_irqs[] = {UART0_IRQ, -1}; +static const int uart1_irqs[] = {UART1_IRQ, -1}; +static const int uart2_irqs[] = {UART2_IRQ, -1}; +static const int uart3_irqs[] = {UART3_IRQ, -1}; +static const int uart4_irqs[] = {UART4_IRQ, -1}; +static const int uart5_irqs[] = {UART5_IRQ, -1}; -#define UART_DEFN(devid) { \ +#define PL011_UART_DEFN(devid) { \ .id = BCM2711_UART##devid, \ - .paddr = UART_PADDR_##devid, \ + .paddr = PL011_UART_BASE, \ .size = BIT(12), \ - .irqs = uart_irqs_##devid, \ + .irqs = uart##devid##_irqs, \ + .init_fn = &uart_init \ +} + +#define MINI_UART_DEFN(devid) { \ + .id = BCM2711_UART##devid, \ + .paddr = MINI_UART_BASE, \ + .size = BIT(12), \ + .irqs = uart##devid##_irqs, \ .init_fn = &uart_init \ } static const struct dev_defn dev_defn[] = { - UART_DEFN(0), + PL011_UART_DEFN(0), + MINI_UART_DEFN(1), + PL011_UART_DEFN(2), + PL011_UART_DEFN(3), + PL011_UART_DEFN(4), + PL011_UART_DEFN(5) }; struct ps_chardevice *
diff --git a/libplatsupport/src/plat/bcm2711/mini_serial.c b/libplatsupport/src/plat/bcm2711/mini_serial.c new file mode 100644 index 0000000..1ddc40a --- /dev/null +++ b/libplatsupport/src/plat/bcm2711/mini_serial.c
@@ -0,0 +1,96 @@ +/* + * Copyright (C) 2021, Hensoldt Cyber GmbH + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <platsupport/serial.h> +#include <platsupport/plat/serial.h> +#include "serial.h" +#include "mini_serial.h" + +typedef volatile struct mini_uart_regs_s { + uint32_t mu_io; // 0x40: mini UART I/O Data + uint32_t mu_ier; // 0x44: mini UART interrupt enable + uint32_t mu_iir; // 0x48: mini UART interrupt identify + uint32_t mu_lcr; // 0x4c: mini UART line control + uint32_t mu_mcr; // 0x50: mini UART modem control + uint32_t mu_lsr; // 0x54: mini UART line status + uint32_t mu_msr; // 0x58: mini UART modem status + uint32_t mu_scratch; // 0x5c: mini UART scratch + uint32_t mu_cntl; // 0x60: mini UART extra control + uint32_t mu_stat; // 0x64: mini UART extra status + uint32_t mu_baud; // 0x68: mini UART baudrate +} +mini_uart_regs_t; + +/* This bit is set if the transmit FIFO can accept at least one byte.*/ +#define MU_LSR_TXEMPTY BIT(5) +/* This bit is set if the transmit FIFO is empty and the + * transmitter is idle. (Finished shifting out the last bit). */ +#define MU_LSR_TXIDLE BIT(6) +#define MU_LSR_RXOVERRUN BIT(1) +#define MU_LSR_DATAREADY BIT(0) + +#define MU_LCR_DLAB BIT(7) +#define MU_LCR_BREAK BIT(6) +#define MU_LCR_DATASIZE BIT(0) + +static void mini_uart_handle_irq(ps_chardevice_t *dev) +{ +} + +int mini_uart_init(const struct dev_defn *defn, + const ps_io_ops_t *ops, + ps_chardevice_t *dev) +{ + /* Attempt to map the virtual address, assure this works */ + void *vaddr = chardev_map(defn, ops); + memset(dev, 0, sizeof(*dev)); + if (vaddr == NULL) { + return -1; + } + + // When mapping the virtual address space, the base addresses need to be + // 4k byte aligned. Since the real base addresses of the UART peripherals + // are not 4k byte aligned, we have to add the required offset. + uint32_t addr_offset = 0; + switch (defn->id) { + case 1: + addr_offset = UART1_OFFSET; + break; + default: + ZF_LOGE("Mini UART with ID %d does not exist!", defn->id); + return -1; + break; + } + + /* Set up all the device properties. */ + dev->id = defn->id; + dev->vaddr = (void *)vaddr + addr_offset; // use real base address + dev->read = &uart_read; + dev->write = &uart_write; + dev->handle_irq = &mini_uart_handle_irq; + dev->irqs = defn->irqs; + dev->ioops = *ops; + dev->flags = SERIAL_AUTO_CR; + + return 0; +} + +int mini_uart_getchar(ps_chardevice_t *d) +{ + while (!((mini_uart_regs_t *)d->vaddr)->mu_lsr & MU_LSR_DATAREADY); + return ((mini_uart_regs_t *)d->vaddr)->mu_io; +} + +int mini_uart_putchar(ps_chardevice_t *d, int c) +{ + while (!((mini_uart_regs_t *)d->vaddr)->mu_lsr & MU_LSR_TXIDLE); + ((mini_uart_regs_t *)d->vaddr)->mu_io = (c & 0xff); + + return 0; +}
diff --git a/libplatsupport/src/plat/bcm2711/mini_serial.h b/libplatsupport/src/plat/bcm2711/mini_serial.h new file mode 100644 index 0000000..ede4ff0 --- /dev/null +++ b/libplatsupport/src/plat/bcm2711/mini_serial.h
@@ -0,0 +1,19 @@ +/* + * Copyright (C) 2021, Hensoldt Cyber GmbH + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include "../../chardev.h" +#include <autoconf.h> +#include <platsupport/gen_config.h> + +int mini_uart_init(const struct dev_defn *defn, + const ps_io_ops_t *ops, + ps_chardevice_t *dev); + +int mini_uart_getchar(ps_chardevice_t *d); + +int mini_uart_putchar(ps_chardevice_t *d, int c); \ No newline at end of file
diff --git a/libplatsupport/src/plat/bcm2711/pl011_serial.c b/libplatsupport/src/plat/bcm2711/pl011_serial.c new file mode 100644 index 0000000..2149a40 --- /dev/null +++ b/libplatsupport/src/plat/bcm2711/pl011_serial.c
@@ -0,0 +1,253 @@ +/* + * Copyright (C) 2021, Hensoldt Cyber GmbH + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <platsupport/serial.h> +#include <platsupport/plat/serial.h> +#include "serial.h" +#include "pl011_serial.h" + +#define DEV_OFFSET(devid) UART##devid##_OFFSET + +typedef volatile struct uart_regs_s { + uint32_t dr; // 0x00: data register + uint32_t rsrecr; // 0x04: receive status/error clear register + uint64_t unused0[2]; // 0x08 + uint32_t fr; // 0x18: flag register + uint32_t unused1; // 0x1c + uint32_t ilpr; // 0x20: not in use + uint32_t ibrd; // 0x24: integer baud rate divisor + uint32_t fbrd; // 0x28: fractional baud rate divisor + uint32_t lcrh; // 0x2c: line control register + uint32_t cr; // 0x30: control register + uint32_t ifls; // 0x34: interrupt FIFO level select register + uint32_t imsc; // 0x38: interrupt mask set clear register + uint32_t ris; // 0x3c: raw interrupt status register + uint32_t mis; // 0x40: masked interrupt status register + uint32_t icr; // 0x44: interrupt clear register + uint32_t dmacr; // 0x48: DMA control register +} +uart_regs_t; + +/* LCRH register */ +#define LCRH_SPS (1 << 7) // stick parity select +#define LCRH_WLEN_8BIT (3 << 5) // word length +#define LCRH_WLEN_7BIT (2 << 5) // word length +#define LCRH_WLEN_6BIT (1 << 5) // word length +#define LCRH_WLEN_5BIT (0 << 5) // word length +#define LCRH_FEN (1 << 4) // enable fifos +#define LCRH_STP2 (1 << 3) // two stop bits select +#define LCRH_EPS (1 << 2) // Even parity select +#define LCRH_PEN (1 << 1) // Parity enable +#define LCRH_BRK (1 << 0) // send break + +/* CR register */ +#define CR_RXE (1 << 9) // receive enable +#define CR_TXE (1 << 8) // transmit enable +#define CR_UARTEN (1 << 0) // UART enable + +/* FR register */ +#define FR_TXFE (1 << 7) // Transmit FIFO empty +#define FR_RXFF (1 << 6) // Receive FIFO full +#define FR_TXFF (1 << 5) // Transmit FIFO full +#define FR_RXFE (1 << 4) // Receive FIFO empty +#define FR_BUSY (1 << 3) // UART busy + +static void pl011_uart_handle_irq(ps_chardevice_t *dev) +{ + // clear interrupts + ((uart_regs_t *)dev->vaddr)->icr = 0x7f0; +} + +static int pl011_uart_cr_configure(ps_chardevice_t *dev) +{ + uint32_t val = ((uart_regs_t *)dev->vaddr)->cr; + + val |= CR_TXE; // transmit enable + val |= CR_RXE; // receive enable + + ((uart_regs_t *)dev->vaddr)->cr = val; + + return 0; +} + +static int pl011_uart_lcrh_configure(ps_chardevice_t *dev) +{ + uint32_t val = ((uart_regs_t *)dev->vaddr)->lcrh; + + val |= LCRH_WLEN_8BIT; // character size is 8bit + val &= ~LCRH_STP2; // only one stop bit + val &= ~LCRH_PEN; // no parity + + ((uart_regs_t *)dev->vaddr)->lcrh = val; + + return 0; +} + +/* + * Set UART baud rate divisor value + * + * More information: https://developer.arm.com/documentation/ddi0183/g/programmers-model/register-descriptions/fractional-baud-rate-register--uartfbrd + */ +static int pl011_uart_baudrate_div_configure(ps_chardevice_t *dev) +{ + // Base UART clock according to https://github.com/raspberrypi/firmware/issues/951 + const uint32_t freq_uart_clk = 48000000; + const uint32_t baud_rate = 115200; + + double baud_div = (double) freq_uart_clk / (double)(16.0 * baud_rate); + double frac_div = baud_div - (uint32_t) baud_div; + + // Set IBRD register + uint32_t val = ((uart_regs_t *)dev->vaddr)->ibrd; + val |= (uint32_t) baud_div; + ((uart_regs_t *)dev->vaddr)->ibrd = val; + + // Set FBRD register + val = ((uart_regs_t *)dev->vaddr)->fbrd; + val |= (uint32_t)(frac_div * 64.0 + 0.5); + ((uart_regs_t *)dev->vaddr)->fbrd = val; + + return 0; +} + +/* + * Configure UART registers + * + * This configuration process is based on the description in the BCM2711 TRM + * section 11.5 Register View, more precisely on the information given for the + * CR register. Furthermore, the LCRH and the baudrate divider registers (IBRD, + * FBRD) are configured. For all of these registers the UART must be disabled. + */ +static int pl011_uart_configure(ps_chardevice_t *dev) +{ + /* ---------------------------------------------------------------------- */ + /* ------------------------- Disable UART ------------------------ */ + /* ---------------------------------------------------------------------- */ + // disable UART + uint32_t val = ((uart_regs_t *)dev->vaddr)->cr; + val &= ~CR_UARTEN; + ((uart_regs_t *)dev->vaddr)->cr = val; + + // wait till UART is not busy anymore + while (((uart_regs_t *)dev->vaddr)->fr & FR_BUSY); + + // disable FIFO + val = ((uart_regs_t *)dev->vaddr)->lcrh; + val &= ~LCRH_FEN; + ((uart_regs_t *)dev->vaddr)->lcrh = val; + + /* ---------------------------------------------------------------------- */ + /* -------------------- Configure UART registers ------------------- */ + /* ---------------------------------------------------------------------- */ + // Set control register (CR) + pl011_uart_cr_configure(dev); + + // Set line control register (LCRH) + pl011_uart_lcrh_configure(dev); + + // Set baudrate divider + pl011_uart_baudrate_div_configure(dev); + + /* ---------------------------------------------------------------------- */ + /* ------------------------- Enable UART ------------------------- */ + /* ---------------------------------------------------------------------- */ + // enable FIFO + val = ((uart_regs_t *)dev->vaddr)->lcrh; + val |= LCRH_FEN; + ((uart_regs_t *)dev->vaddr)->lcrh = val; + + // enable UART + val = ((uart_regs_t *)dev->vaddr)->cr; + val |= CR_UARTEN; + ((uart_regs_t *)dev->vaddr)->cr = val; + + // Set Interrupt Mask Set/Clear Register (IMSC) + ((uart_regs_t *)dev->vaddr)->imsc = 0x10; // activate receive interrupt + + return 0; +} + +int pl011_uart_init( + const struct dev_defn *defn, + const ps_io_ops_t *ops, + ps_chardevice_t *dev +) +{ + /* Attempt to map the virtual address, assure this works */ + void *vaddr = chardev_map(defn, ops); + memset(dev, 0, sizeof(*dev)); + if (vaddr == NULL) { + return -1; + } + + // When mapping the virtual address space, the base addresses need to be + // 4k byte aligned. Since the real base addresses of the UART peripherals + // are not 4k byte aligned, we have to add the required offset. + uint32_t addr_offset = 0; + switch (defn->id) { + case 0: + addr_offset = UART0_OFFSET; + break; + // case 1: Mini UART + case 2: + addr_offset = UART2_OFFSET; + break; + case 3: + addr_offset = UART3_OFFSET; + break; + case 4: + addr_offset = UART4_OFFSET; + break; + case 5: + addr_offset = UART5_OFFSET; + break; + default: + ZF_LOGE("PL011 UART with ID %d does not exist!", defn->id); + return -1; + break; + } + + /* Set up all the device properties. */ + dev->id = defn->id; + dev->vaddr = (void *)vaddr + addr_offset; // use real base address + dev->read = &uart_read; + dev->write = &uart_write; + dev->handle_irq = &pl011_uart_handle_irq; + dev->irqs = defn->irqs; + dev->ioops = *ops; + dev->flags = SERIAL_AUTO_CR; + + pl011_uart_configure(dev); + + return 0; +} + +int pl011_uart_getchar(ps_chardevice_t *d) +{ + int ch = EOF; + + // only if receive fifo is not empty + if ((((uart_regs_t *)d->vaddr)->fr & FR_RXFE) == 0) { + ch = ((uart_regs_t *)d->vaddr)->dr & MASK(8); + } + return ch; +} + +int pl011_uart_putchar(ps_chardevice_t *d, int c) +{ + // only if transmit fifo is not full + while ((((uart_regs_t *)d->vaddr)->fr & FR_TXFF) != 0); + + ((uart_regs_t *)d->vaddr)->dr = c; + if (c == '\n' && (d->flags & SERIAL_AUTO_CR)) { + uart_putchar(d, '\r'); + } + + return c; +}
diff --git a/libplatsupport/src/plat/bcm2711/pl011_serial.h b/libplatsupport/src/plat/bcm2711/pl011_serial.h new file mode 100644 index 0000000..a3758b2 --- /dev/null +++ b/libplatsupport/src/plat/bcm2711/pl011_serial.h
@@ -0,0 +1,19 @@ +/* + * Copyright (C) 2021, Hensoldt Cyber GmbH + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include "../../chardev.h" +#include <autoconf.h> +#include <platsupport/gen_config.h> + +int pl011_uart_init(const struct dev_defn *defn, + const ps_io_ops_t *ops, + ps_chardevice_t *dev); + +int pl011_uart_getchar(ps_chardevice_t *d); + +int pl011_uart_putchar(ps_chardevice_t *d, int c);
diff --git a/libplatsupport/src/plat/bcm2711/serial.c b/libplatsupport/src/plat/bcm2711/serial.c index fabe84b..b3f1854 100644 --- a/libplatsupport/src/plat/bcm2711/serial.c +++ b/libplatsupport/src/plat/bcm2711/serial.c
@@ -6,77 +6,164 @@ */ #include <stdlib.h> +#include <platsupport/gpio.h> +#include <platsupport/plat/gpio.h> #include <platsupport/serial.h> #include <platsupport/plat/serial.h> + #include "serial.h" -#include <string.h> +#include "mini_serial.h" +#include "pl011_serial.h" -#define REG_PTR(base, off) ((volatile uint32_t *)((base) + (off))) +typedef struct { + int (*uart_init)(const struct dev_defn *defn, + const ps_io_ops_t *ops, + ps_chardevice_t *dev); + int (*uart_getchar)(ps_chardevice_t *d); + int (*uart_putchar)(ps_chardevice_t *d, int c); +} uart_funcs_t; -/* When DLAB=1, MU_IO is a baud rate register. - * Otherwise, write to TX, read to RX */ -#define MU_IO 0x40 -/* When DLAB=1, MU_IIR is a baud rate register. - * Otherwise IRQ enable */ -#define MU_IIR 0x44 -#define MU_IER 0x48 -#define MU_LCR 0x4C -#define MU_MCR 0x50 -#define MU_LSR 0x54 -#define MU_MSR 0x58 -#define MU_SCRATCH 0x5C -#define MU_CNTL 0x60 +uart_funcs_t uart_funcs; -/* This bit is set if the transmit FIFO can accept at least one byte.*/ -#define MU_LSR_TXEMPTY BIT(5) -/* This bit is set if the transmit FIFO is empty and the - * transmitter is idle. (Finished shifting out the last bit). */ -#define MU_LSR_TXIDLE BIT(6) -#define MU_LSR_RXOVERRUN BIT(1) -#define MU_LSR_DATAREADY BIT(0) - -#define MU_LCR_DLAB BIT(7) -#define MU_LCR_BREAK BIT(6) -#define MU_LCR_DATASIZE BIT(0) - -static void uart_handle_irq(ps_chardevice_t *d UNUSED) +int uart_getchar(ps_chardevice_t *d) { + uart_funcs.uart_getchar(d); } int uart_putchar(ps_chardevice_t *d, int c) { - while (!(*REG_PTR(d->vaddr, MU_LSR) & MU_LSR_TXIDLE)); - *REG_PTR(d->vaddr, MU_IO) = (c & 0xff); - - return 0; + uart_funcs.uart_putchar(d, c); } -int uart_getchar(ps_chardevice_t *d UNUSED) +/* + * Configure UART GPIO pins. + * + * On the RPi4, there are 6 UARTs: One miniUART (UART1) and 5 PL011 UARTs (UART0 + * + UART2-5). There is also a possibility to configure these pins via the + * config.txt file statically. Since these pins might be used for other + * functionality than UART, it might be best to configure these pins here + * dynamically. + * + * BCM2711 TRM, Section 11.3. Primary UART Inputs and Outputs. + */ +int uart_gpio_configure(enum chardev_id id, const ps_io_ops_t *o) { - while (!(*REG_PTR(d->vaddr, MU_LSR) & MU_LSR_DATAREADY)); - return *REG_PTR(d->vaddr, MU_IO); + gpio_sys_t gpio_sys; + gpio_t gpio; + + // We only configure the TX/RX pins for each UART peripheral and ignore flow + // control (CTS/RTS pins) for now. + int tx_pin = -1; + int rx_pin = -1; + int alt_function = -1; + + /* + * GPIO pin configuration + * + * GPIO pins 14/15 are mapped accordingly: https://pinout.xyz/pinout/pin8_gpio14# + * + * This means: + * UART0: these pins are RX/TX lines; pins must be configured for ALT0 + * UART1: these pins are RX/TX lines; pins must be configured for ALT5 + * UART5: these pins are CTS/RTS lines; we ignore cts/rts for now + */ + switch (id) { + case 0: + // UART 0 uses GPIO pins 14-15 + tx_pin = 14; + rx_pin = 15; + alt_function = BCM2711_GPIO_FSEL_ALT0; + case 1: + // UART 1 uses GPIO pins 14-15 + tx_pin = 14; + rx_pin = 15; + alt_function = BCM2711_GPIO_FSEL_ALT5; + case 2: + // UART 2 uses GPIO pins 0-3 + tx_pin = 0; + rx_pin = 1; + alt_function = BCM2711_GPIO_FSEL_ALT4; + break; + case 3: + // UART 3 uses GPIO pins 4-7 + tx_pin = 4; + rx_pin = 5; + alt_function = BCM2711_GPIO_FSEL_ALT4; + break; + case 4: + // UART 4 uses GPIO pins 8-11 + tx_pin = 8; + rx_pin = 9; + alt_function = BCM2711_GPIO_FSEL_ALT4; + break; + case 5: + // UART 5 uses GPIO pins 12-13 + tx_pin = 12; + rx_pin = 13; + alt_function = BCM2711_GPIO_FSEL_ALT4; + break; + default: + ZF_LOGD("No pin configuration required!"); + return 0; + break; + } + + if (tx_pin < 0 || rx_pin < 0) { + ZF_LOGE("TX/RX pins wrongly configured!"); + return -1; + } + + if (alt_function < 0) { + ZF_LOGE("ALT function wrongly configured!"); + return -1; + } + + // GPIO initialization + int ret = gpio_sys_init((ps_io_ops_t *)o, &gpio_sys); + if (ret) { + ZF_LOGE("gpio_sys_init() failed: ret = %i", ret); + return -1; + } + + // configure tx pin + gpio_sys.init(&gpio_sys, tx_pin, 0, &gpio); + bcm2711_gpio_fsel(&gpio, alt_function); + + // configure rx pin + gpio_sys.init(&gpio_sys, rx_pin, 0, &gpio); + bcm2711_gpio_fsel(&gpio, alt_function); + + return 0; } int uart_init(const struct dev_defn *defn, const ps_io_ops_t *ops, ps_chardevice_t *dev) { - /* Attempt to map the virtual address, assure this works */ - void *vaddr = chardev_map(defn, ops); - memset(dev, 0, sizeof(*dev)); - if (vaddr == NULL) { + switch (defn->id) { + case 1: + uart_funcs.uart_init = &mini_uart_init; + uart_funcs.uart_getchar = &mini_uart_getchar; + uart_funcs.uart_putchar = &mini_uart_putchar; + break; + case 0: + case 2: + case 3: + case 4: + case 5: + uart_funcs.uart_init = &pl011_uart_init; + uart_funcs.uart_getchar = &pl011_uart_getchar; + uart_funcs.uart_putchar = &pl011_uart_putchar; + break; + default: + ZF_LOGE("UART with id %d does not exist!", defn->id); return -1; + break; } - /* Set up all the device properties. */ - dev->id = defn->id; - dev->vaddr = (void *)vaddr; - dev->read = &uart_read; - dev->write = &uart_write; - dev->handle_irq = &uart_handle_irq; - dev->irqs = defn->irqs; - dev->ioops = *ops; - dev->flags = SERIAL_AUTO_CR; + uart_gpio_configure(defn->id, ops); + + uart_funcs.uart_init(defn, ops, dev); return 0; }
diff --git a/libplatsupport/src/plat/bcm2711/serial.h b/libplatsupport/src/plat/bcm2711/serial.h index e9a40d5..96ec4d5 100644 --- a/libplatsupport/src/plat/bcm2711/serial.h +++ b/libplatsupport/src/plat/bcm2711/serial.h
@@ -5,6 +5,34 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#pragma once + #include "../../chardev.h" +#include <autoconf.h> +#include <platsupport/gen_config.h> + +#define UART0_IRQ 153 +#define UART1_IRQ 125 +#define UART2_IRQ 153 +#define UART3_IRQ 153 +#define UART4_IRQ 153 +#define UART5_IRQ 153 + +#define PL011_UART_BASE 0xfe201000 +#define MINI_UART_BASE 0xfe215000 + +#define UART0_OFFSET 0x0 // UART0: PL011 +#define UART1_OFFSET 0x40 // UART1: Mini UART +#define UART2_OFFSET 0x400 // UART2: PL011 +#define UART3_OFFSET 0x600 // UART3: PL011 +#define UART4_OFFSET 0x800 // UART4: PL011 +#define UART5_OFFSET 0xa00 // UART5: PL011 + +#define UART0_PADDR (PL011_UART_BASE + UART0_OFFSET) // 0xfe201000 +#define UART1_PADDR (MINI_UART_BASE + UART1_OFFSET) // 0xfe215040 +#define UART2_PADDR (PL011_UART_BASE + UART2_OFFSET) // 0xfe201400 +#define UART3_PADDR (PL011_UART_BASE + UART3_OFFSET) // 0xfe201600 +#define UART4_PADDR (PL011_UART_BASE + UART4_OFFSET) // 0xfe201800 +#define UART5_PADDR (PL011_UART_BASE + UART5_OFFSET) // 0xfe201a00 int uart_init(const struct dev_defn *defn, const ps_io_ops_t *ops, ps_chardevice_t *dev);