Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

libplatsupport: separate drivers from platforms #157

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions libplatsupport/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ if(KernelPlatformQEMUArmVirt OR KernelPlatformQuartz64)
endif()
endif()

if(KernelPlatformHikey OR KernelPlatformFVP)
list(APPEND deps "src/driver/sp804/sp804_ltimer.c")
endif()

if(KernelPlatformExynos5422)
list(APPEND deps src/mach/${LibPlatSupportMach}/clock/exynos_5422_clock.c)
elseif(KernelPlatformExynos4 OR KernelPlatformExynos5410 OR KernelPlatformExynos5250)
Expand Down
118 changes: 118 additions & 0 deletions libplatsupport/include/platsupport/driver/sp804/sp804.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
* Copyright 2022, HENSOLDT Cyber GmbH
* Copyright 2019, Data61, CSIRO (ABN 41 687 119 230)
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once

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


#define TCLR_ONESHOT BIT(0)
#define TCLR_VALUE_32 BIT(1)
/* Bit 2 and 3: Prescaler
* 00 = clock is divided by 1 (default)
* 01 = clock is divided by 16
* 10 = clock is divided by 256
* 11 = reserved, do not use.
* Bit 4: reserved
*/
#define TCLR_INTENABLE BIT(5)
#define TCLR_AUTORELOAD BIT(6)
#define TCLR_STARTTIMER BIT(7)

/*
* The sp840 contains two identical timers:
*
* 0x000 - 0x01f sp804_regs_t timer1;
* 0x020 - 0x03f sp804_regs_t timer2;
* 0x040 - 0xefc Reserved
* 0xf00 TimerITCR
* 0xf04 TimerITOP
* 0xf08 - 0xfdf Reserved
* 0xfe0 - 0xfef TimerPeriphID[4]
* 0xff0 - 0xfff TimerPCellID[4]
*/

#define SP804_TIMER2_OFFSET 0x20

typedef volatile struct {
uint32_t load; /* 0x00 */
uint32_t value; /* 0x04 */
uint32_t control; /* 0x08 TCLR */
uint32_t intclr; /* 0x0c Interrupt Clear */
uint32_t ris; /* 0x10 Raw interrupt Status */
uint32_t mis; /* 0x14 Masked Interrupt Status */
uint32_t bgload; /* 0x18 Background Load */
uint32_t _rfu; /* 0x1c (unused) */
} sp804_regs_t;

compile_time_assert(sp804_timer_size , sizeof(sp804_regs_t) <= SP804_TIMER2_OFFSET);

#define SP804_TIMER1_REGS(base) ((sp804_regs_t *)(base))
#define SP804_TIMER2_REGS(base) ((sp804_regs_t *)((uintptr_t)(base) + SP804_TIMER2_OFFSET))

static void sp804_reset(sp804_regs_t *regs)
{
regs->control = 0;
}

static void sp804_stop(sp804_regs_t *regs)
{
regs->control &= ~TCLR_STARTTIMER;
}

static void sp804_start(sp804_regs_t *regs)
{
regs->control |= TCLR_STARTTIMER;
}

static bool sp804_is_irq_pending(sp804_regs_t *regs)
{
/* return the raw interrupt status and not the masted interrupt status */
return !!regs->ris;
}

static bool sp804_clear_intr(sp804_regs_t *regs)
{
regs->intclr = 0x1;
}

static uint32_t sp804_get_ticks(sp804_regs_t *regs)
{
return regs->value;
}

static void sp804_set_timeout(sp804_regs_t *regs, uint32_t ticks,
bool is_periodic, bool enable_intr)
{
regs->control = 0; /* stop timer */

/* If 'ticks' is 0, then writing to 'load' will generate an interrupt
* immediately.
*
* The "Hikey Application Processor Function Description" says in
* section 2.3:
* The minimum valid value of TIMERN_LOAD is 1. If 0 is written to
* TIMERN_LOAD, a timing interrupt is generated immediately.
* TIMERN_BGLOAD is an initial count value register in periodic mode. In
* periodic mode, when the value of TIMERN_BGLOAD is updated, the
* value of TIMERN_LOAD is changed to that of TIMERN_BGLOAD. However,
* the timer counter does not restart counting. After the counter
* decreases to 0, the value of TIMERN_LOAD (that is,
* the value of TIMERN_BGLOAD) is reloaded to the counter.
*
* In other words, for periodic mode, load BGLOAD first, then write to
* LOAD. For oneshot mode, only write to LOAD. For good measure, write 0
* to BGLOAD.
*/
regs->bgload = is_periodic ? ticks : 0;
regs->load = ticks;

/* The TIMERN_VALUE register is read-only. */
regs->control = TCLR_STARTTIMER | TCLR_VALUE_32
| (is_periodic ? TCLR_AUTORELOAD : TCLR_ONESHOT)
| (enable_intr ? TCLR_INTENABLE : 0);
}
17 changes: 17 additions & 0 deletions libplatsupport/include/platsupport/driver/sp804/sp804_ltimer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Copyright 2022, HENSOLDT Cyber GmbH
* Copyright 2019, Data61, CSIRO (ABN 41 687 119 230)
*
* SPDX-License-Identifier: BSD-2-Clause
*/

#pragma once

#include <platsupport/io.h>
#include <platsupport/ltimer.h>

#include "../../ltimer.h"

int ltimer_sp804_init(ltimer_t *ltimer, const char *ftd_path, uint64_t freq,
ps_io_ops_t ops, ltimer_callback_fn_t callback,
void *callback_token);
117 changes: 117 additions & 0 deletions libplatsupport/include/platsupport/driver/uart_ns16550.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
* 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 started 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);
}
128 changes: 128 additions & 0 deletions libplatsupport/include/platsupport/driver/uart_pl011.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/*
* 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_BUSY BIT(3) /* UART busy */
#define PL011_FR_RXFE BIT(4) /* Receive FIFO empty */
#define PL011_FR_TXFF BIT(5) /* Transmit FIFO full */
#define PL011_FR_RXFF BIT(6) /* Receive FIFO full */
#define PL011_FR_TXFE BIT(7) /* Transmit FIFO empty */

#define PL011_IMSC_RXIM BIT(4) /* RX interrupt */
#define PL011_IMSC_TXIM BIT(5) /* TX interrupt */
#define PL011_IMSC_RTIM BIT(6) /* RX timeout interrupt */
#define PL011_IMSC_OEIM BIT(10) /* Overrun timeout */

#define PL011_CR_UARTEN BIT(0) /* UART enable */
#define PL011_CR_TXE BIT(8) /* Transmit enable */
#define PL011_CR_RXE BIT(9) /* Receive enable */

// 0111 1111 0000
#define PL011_ICR_RXIC BIT(4)
#define PL011_ICR_TXIC BIT(5)

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_tx_byte(pl011_regs_t *regs, uint8_t c)
{
/* Caller has to ensure TX FIFO has space */
regs->dr = c;
}

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

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

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

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

/*
* Block until there is space in the TX FIFO, then outputs the char.
*/
static void pl011_put_char_blocking(pl011_regs_t *regs, uint8_t c)
{
while (pl011_is_tx_fifo_full(regs)) {
/* busy loop */
}
pl011_tx_byte(regs, c);
}

/*
* 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 the terminal use case.
*/
static void pl011_put_char_blocking_auto_cr(pl011_regs_t *regs, uint8_t c,
bool is_auto_cr)
{
if ((c == '\n') && is_auto_cr) {
pl011_put_char_blocking(regs, '\r');
}
pl011_put_char_blocking(regs, c);
}
Loading
Loading