Skip to content

Commit

Permalink
libplatsupport: separate drivers from platforms
Browse files Browse the repository at this point in the history
Separate serial drivers

Signed-off-by: Axel Heider <axel.heider@hensoldt.net>
  • Loading branch information
Axel Heider committed Jan 11, 2024
1 parent 43430f8 commit 4b4971a
Show file tree
Hide file tree
Showing 7 changed files with 334 additions and 187 deletions.
118 changes: 118 additions & 0 deletions libplatsupport/include/platsupport/driver/ns16550/ns16550.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
* Copyright 2022, HENSOLDT Cyber GmbH
*
* SPDX-License-Identifier: BSD-2-Clause
*
* Driver for a 16550 compatible UART.
*/

#pragma once

#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <utils/arith.h>

#define NS16550_IER_ERBFI BIT(0) /* Enable Received Data Available Interrupt */
#define NS16550_IER_ETBEI BIT(1) /* Enable Transmitter Holding Register Empty Interrupt */
#define NS16550_IER_ELSI BIT(2) /* Enable Receiver Line Status Interrupt */
#define NS16550_IER_EDSSI BIT(3) /* Enable MODEM Status Interrupt */

#define NS16550_FCR_ENABLE_FIFOS BIT(0)
#define NS16550_FCR_RESET_RX_FIFO BIT(1)
#define NS16550_FCR_RESET_TX_FIFO BIT(2)
#define NS16550_FCR_TRIGGER_1 (0u << 6)
#define NS16550_FCR_TRIGGER_4 (1u << 6)
#define NS16550_FCR_TRIGGER_8 (2u << 6)
#define NS16550_FCR_TRIGGER_14 (3u << 6)

#define NS16550_LCR_DLAB BIT(7) /* Divisor Latch Access */

#define NS16550_LSR_DR BIT(0) /* Data Ready */
#define NS16550_LSR_THRE BIT(5) /* Transmitter Holding Register Empty */

/* There are different NS16550 hardware implementations. The classic size of
* each register is just one byte, but some implementations stated to use 32-bit
* registers, as this fits better with the natural alignment
*/
#if defined(NS16550_WITH_REG32)
typedef volatile uint32_t ns16550_reg_t;
#elif defined(NS16550_WITH_REG8)
typedef volatile uint8_t ns16550_reg_t;
#else
#error "define NS16550_WITH_REG[8|32]"
#endif

typedef struct {
/* 0x00 */
ns16550_reg_t rbr_dll_thr; /* Receiver Buffer Register (Read Only)
* Divisor Latch (LSB)
* Transmitter Holding Register (Write Only)
*/
/* 0x01 or 0x04 */
ns16550_reg_t dlm_ier; /* Divisor Latch (MSB)
* Interrupt Enable Register
*/
/* 0x02 or 0x08 */
ns16550_reg_t iir_fcr; /* Interrupt Identification Register (Read Only)
* FIFO Control Register (Write Only)
*/
/* 0x03 or 0x0c */
ns16550_reg_t lcr; /* Line Control Register */
/* 0x04 or 0x10 */
ns16550_reg_t mcr; /* MODEM Control Register */
/* 0x05 or 0x14 */
ns16550_reg_t lsr; /* Line Status Register */
/* 0x06 or 0x18 */
ns16550_reg_t msr; /* MODEM Status Register */
/* 0x07 or 0x1c */
} ns16550_regs_t;


/*
*******************************************************************************
* UART access primitives
*******************************************************************************
*/

static bool ns16550_is_tx_empty(ns16550_regs_t *regs)
{
/* The THRE bit is set when the FIFO is fully empty. There seems no way to
* detect if the FIFO is partially empty only, so we can't implement a
* "tx_ready" check.
*/
return (0 != (regs->lsr & NS16550_LSR_THRE));
}

static void ns16550_tx_byte(ns16550_regs_t *regs, uint8_t byte)
{
/* Caller has to ensure TX FIFO is ready */
regs->rbr_dll_thr = byte;
}

static bool ns16550_is_rx_empty(ns16550_regs_t *regs)
{
return (0 == (regs->lsr & NS16550_LSR_DR));
}


static int ns16550_rx_byte(ns16550_regs_t *regs)
{
/* Caller has to ensure RX FIFO has data */
return regs->rbr_dll_thr;
}


/*
*******************************************************************************
* UART access helpers
*******************************************************************************
*/

/*
* Returns a char from the TX FIFO or EOF if the FIFO is empty.
*/
static int ns16550_get_char_or_EOF(ns16550_regs_t *regs)
{
return ns16550_is_rx_empty(regs) ? EOF : ns16550_rx_byte(regs);
}
115 changes: 115 additions & 0 deletions libplatsupport/include/platsupport/driver/pl011/pl011.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*
* Copyright 2022, HENSOLDT Cyber GmbH
* Copyright 2017, Data61, CSIRO (ABN 41 687 119 230)
*
* SPDX-License-Identifier: BSD-2-Clause
*
* Driver for a ARM PL011 UART.
*/

#pragma once

#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <utils/arith.h>

#define PL011_FR_TXFF BIT(5)
#define PL011_FR_RXFE BIT(4)

typedef volatile struct {
uint32_t dr; /* 0x00 */
uint32_t _rfu_04; /* 0x04 */
uint32_t _rfu_08; /* 0x08 */
uint32_t _rfu_0c; /* 0x0c */
uint32_t _rfu_10; /* 0x10 */
uint32_t _rfu_14; /* 0x14 */
uint32_t fr; /* 0x18 */
uint32_t _rfu_1c; /* 0x1c */
uint32_t _rfu_20; /* 0x20 */
uint32_t _rfu_24; /* 0x24 */
uint32_t _rfu_28; /* 0x28 */
uint32_t _rfu_2c; /* 0x2c */
uint32_t _rfu_30; /* 0x30 */
uint32_t _rfu_34; /* 0x34 */
uint32_t imsc; /* 0x38 */
uint32_t _rfu_3c; /* 0x3c */
uint32_t _rfu_40; /* 0x40 */
uint32_t icr; /* 0x44 */
uint32_t _rfu_48; /* 0x48 */
uint32_t _rfu_4c; /* 0x4c */
} pl011_regs_t;


/*
*******************************************************************************
* UART access primitives
*******************************************************************************
*/

static bool pl011_is_rx_fifo_empty(pl011_regs_t *regs)
{
return (0 != (regs->fr & PL011_FR_RXFE));
}

static bool pl011_is_tx_fifo_full(pl011_regs_t *regs)
{
return (0 != (regs->fr & PL011_FR_TXFF));
}

static void pl011_write_char(pl011_regs_t *regs, uint8_t c)
{
/* Caller has to ensure TX FIFO has space */
regs->dr = c;
}

static uint8_t pl011_read_char(pl011_regs_t *regs)
{
return (uint8_t)(regs->dr & 0xFF);
}

static void pl011_clear_interrupt(pl011_regs_t *regs)
{
regs->icr = 0x7f0;
}

static void pl011_init(pl011_regs_t *regs)
{
regs->imsc = 0x50;
}


/*
*******************************************************************************
* UART access helpers
*******************************************************************************
*/

/*
* Returns a char from the TX FIFO or EOF if the FIFO is empty.
*/
int pl011_get_char_or_EOF(pl011_regs_t *regs)
{
return pl011_is_rx_fifo_empty(regs) ? EOF : pl011_read_char(regs);
}

/*
* Block until there is space in the TX FIFO, then outputs the char. Optionally
* output a CR (\r) first in case of LF (\n) to support terminal use case.
*/
void pl011_put_char_blocking(pl011_regs_t *regs, uint8_t c, bool is_auto_cr)
{

/* output CR (\r) before LF (\n) automatically if UART is a terminal */
if ((c == '\n') && is_auto_cr) {
while (pl011_is_tx_fifo_full(regs)) {
/* busy loop */
}
pl011_write_char(regs, '\r');
}

while (pl011_is_tx_fifo_full(regs)) {
/* busy loop */
}
pl011_write_char(regs, c);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
* Copyright 2022, HENSOLDT Cyber GmbH
*
* SPDX-License-Identifier: BSD-2-Clause
*
*
* QEMU RISC-V virt emulates a 16550 compatible UART.
*
*/

#pragma once

#include <autoconf.h>

/* This information is taken from the device tree. */
#define UART0_PADDR 0x10000000
#define UART0_IRQ 10
Expand All @@ -22,40 +24,3 @@ enum chardev_id {

#define DEFAULT_SERIAL_PADDR UART0_PADDR
#define DEFAULT_SERIAL_INTERRUPT UART0_IRQ

/* QEMU RISC-V virt emulates a 16550 compatible UART. */

#define UART_IER_ERBFI BIT(0) /* Enable Received Data Available Interrupt */
#define UART_IER_ETBEI BIT(1) /* Enable Transmitter Holding Register Empty Interrupt */
#define UART_IER_ELSI BIT(2) /* Enable Receiver Line Status Interrupt */
#define UART_IER_EDSSI BIT(3) /* Enable MODEM Status Interrupt */

#define UART_FCR_ENABLE_FIFOS BIT(0)
#define UART_FCR_RESET_RX_FIFO BIT(1)
#define UART_FCR_RESET_TX_FIFO BIT(2)
#define UART_FCR_TRIGGER_1 (0u << 6)
#define UART_FCR_TRIGGER_4 (1u << 6)
#define UART_FCR_TRIGGER_8 (2u << 6)
#define UART_FCR_TRIGGER_14 (3u << 6)

#define UART_LCR_DLAB BIT(7) /* Divisor Latch Access */

#define UART_LSR_DR BIT(0) /* Data Ready */
#define UART_LSR_THRE BIT(5) /* Transmitter Holding Register Empty */

typedef volatile struct {
uint8_t rbr_dll_thr; /* 0x00 Receiver Buffer Register (Read Only)
* Divisor Latch (LSB)
* Transmitter Holding Register (Write Only)
*/
uint8_t dlm_ier; /* 0x04 Divisor Latch (MSB)
* Interrupt Enable Register
*/
uint8_t iir_fcr; /* 0x08 Interrupt Identification Register (Read Only)
* FIFO Control Register (Write Only)
*/
uint8_t lcr; /* 0xC Line Control Register */
uint8_t mcr; /* 0x10 MODEM Control Register */
uint8_t lsr; /* 0x14 Line Status Register */
uint8_t msr; /* 0x18 MODEM Status Register */
} uart_regs_t;
51 changes: 22 additions & 29 deletions libplatsupport/src/plat/fvp/serial.c
Original file line number Diff line number Diff line change
@@ -1,61 +1,51 @@
/*
* Copyright 2022, HENSOLDT Cyber GmbH
* Copyright 2019, Data61, CSIRO (ABN 41 687 119 230)
*
* SPDX-License-Identifier: BSD-2-Clause
*
*
* FVP emulates PL011 UARTs.
*
*/

/* Mostly copy/paste from the HiKey plat.
* Should be moved to a common driver file for PL011 */

#include <string.h>
#include <stdlib.h>
#include <platsupport/serial.h>
#include <platsupport/driver/pl011/pl011.h>
#include "../../chardev.h"

#define RHR_MASK MASK(8)
#define UARTDR 0x000
#define UARTFR 0x018
#define UARTIMSC 0x038
#define UARTICR 0x044
#define PL011_UARTFR_TXFF BIT(5)
#define PL011_UARTFR_RXFE BIT(4)

#define REG_PTR(base, off) ((volatile uint32_t *)((base) + (off)))
static pl011_regs_t *get_pl011_regs(ps_chardevice_t *dev)
{
return (pl011_regs_t *)(dev->vaddr);
}

int uart_getchar(ps_chardevice_t *d)
{
int ch = EOF;

if ((*REG_PTR(d->vaddr, UARTFR) & PL011_UARTFR_RXFE) == 0) {
ch = *REG_PTR(d->vaddr, UARTDR) & RHR_MASK;
}
return ch;
pl011_regs_t *regs = get_pl011_regs(d);
return pl011_get_char_or_EOF(regs);
}

int uart_putchar(ps_chardevice_t *d, int c)
{
if (c == '\n' && (d->flags & SERIAL_AUTO_CR)) {
uart_putchar(d, '\r');
}

while ((*REG_PTR(d->vaddr, UARTFR) & PL011_UARTFR_TXFF) != 0) {
/* busy loop */
}
*REG_PTR(d->vaddr, UARTDR) = c;

pl011_regs_t *regs = get_pl011_regs(d);
pl011_put_char_blocking_auto_cr(regs, c, d->flags & SERIAL_AUTO_CR);
return c;
}

static void uart_handle_irq(ps_chardevice_t *dev)
{
*REG_PTR(dev->vaddr, UARTICR) = 0x7f0;
pl011_regs_t *regs = get_pl011_regs(dev);
pl011_clear_interrupt(regs);
}

int uart_init(const struct dev_defn *defn,
const ps_io_ops_t *ops,
ps_chardevice_t *dev)
{
memset(dev, 0, sizeof(*dev));

/* Map device. */
void *vaddr = chardev_map(defn, ops);
if (vaddr == NULL) {
return -1;
Expand All @@ -71,6 +61,9 @@ int uart_init(const struct dev_defn *defn,
dev->ioops = *ops;
dev->flags = SERIAL_AUTO_CR;

*REG_PTR(dev->vaddr, UARTIMSC) = 0x50;
/* Initialize the device. */
pl011_regs_t *regs = get_pl011_regs(dev);
pl011_init(regs);

return 0;
}
Loading

0 comments on commit 4b4971a

Please sign in to comment.