-
Notifications
You must be signed in to change notification settings - Fork 87
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Axel Heider
committed
May 21, 2024
1 parent
a06c6bf
commit b44f4ab
Showing
3 changed files
with
220 additions
and
0 deletions.
There are no files selected for viewing
46 changes: 46 additions & 0 deletions
46
libplatsupport/plat_include/star64/platsupport/plat/serial.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
/* | ||
* Copyright 2023, UNSW | ||
* | ||
* SPDX-License-Identifier: BSD-2-Clause | ||
*/ | ||
|
||
#pragma once | ||
|
||
|
||
#define UART0_PADDR 0x10000000 | ||
#define UART1_PADDR 0x10010000 | ||
#define UART2_PADDR 0x10020000 | ||
#define UART3_PADDR 0x12000000 | ||
#define UART4_PADDR 0x12010000 | ||
#define UART5_PADDR 0x12020000 | ||
|
||
#define UART0_IRQ 32 | ||
#define UART1_IRQ 33 | ||
#define UART2_IRQ 34 | ||
#define UART3_IRQ 45 | ||
#define UART4_IRQ 46 | ||
#define UART5_IRQ 47 | ||
|
||
enum chardev_id { | ||
UART0, | ||
UART1, | ||
UART2, | ||
UART3, | ||
UART4, | ||
UART5, | ||
/* Aliases */ | ||
PS_SERIAL0 = UART0, | ||
PS_SERIAL1 = UART1, | ||
PS_SERIAL2 = UART2, | ||
PS_SERIAL3 = UART3, | ||
PS_SERIAL4 = UART4, | ||
PS_SERIAL5 = UART5, | ||
/* defaults */ | ||
PS_SERIAL_DEFAULT = PS_SERIAL0 | ||
}; | ||
|
||
|
||
/* The default serial device corresponds to the UART available via the GPIO | ||
* pins of the Star64 Model-A. */ | ||
#define DEFAULT_SERIAL_PADDR UART0_PADDR | ||
#define DEFAULT_SERIAL_INTERRUPT UART0_IRQ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
/* | ||
* Copyright 2023, UNSW | ||
* | ||
* SPDX-License-Identifier: BSD-2-Clause | ||
*/ | ||
|
||
#include "../../chardev.h" | ||
#include "../../common.h" | ||
#include <utils/page.h> | ||
|
||
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}; | ||
|
||
/* | ||
* Despite each UART being 0x10000 in size (according to the device tree) we | ||
* only need to map in the first page for the driver to functon. | ||
*/ | ||
#define UART_DEFN(devid) { \ | ||
.id = UART##devid, \ | ||
.paddr = UART##devid##_PADDR, \ | ||
.size = PAGE_SIZE_4K, \ | ||
.irqs = uart##devid##_irqs, \ | ||
.init_fn = &uart_init \ | ||
} | ||
|
||
const struct dev_defn dev_defn[] = { | ||
UART_DEFN(0), | ||
UART_DEFN(1), | ||
UART_DEFN(2), | ||
UART_DEFN(3), | ||
UART_DEFN(4), | ||
UART_DEFN(5), | ||
}; | ||
|
||
struct ps_chardevice* | ||
ps_cdev_init(enum chardev_id id, const ps_io_ops_t* o, struct ps_chardevice* d) { | ||
unsigned int i; | ||
for (i = 0; i < ARRAY_SIZE(dev_defn); i++) { | ||
if (dev_defn[i].id == id) { | ||
return (dev_defn[i].init_fn(dev_defn + i, o, d)) ? NULL : d; | ||
} | ||
} | ||
return NULL; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
/* | ||
* Copyright 2023, UNSW | ||
* Copyright 2022, HENSOLDT Cyber GmbH | ||
* | ||
* SPDX-License-Identifier: BSD-2-Clause | ||
* | ||
* | ||
* The JH7110 SoC use on the Star64 emulates a 16550 compatible UART, where the | ||
* register width is 32-bit. | ||
*/ | ||
|
||
#include <string.h> | ||
#include <stdlib.h> | ||
#include <platsupport/serial.h> | ||
|
||
#define NS16550_WITH_REG32 | ||
#include <platsupport/driver/uart_ns16550.h> | ||
|
||
#include "../../chardev.h" | ||
|
||
static ns16550_regs_t *uart_get_regs(ps_chardevice_t *dev) | ||
{ | ||
return (ns16550_regs_t *)(dev->vaddr); | ||
} | ||
|
||
/* | ||
******************************************************************************* | ||
* UART access API | ||
******************************************************************************* | ||
*/ | ||
|
||
int uart_putchar(ps_chardevice_t *dev, int c) | ||
{ | ||
ns16550_regs_t *regs = uart_get_regs(dev); | ||
|
||
/* There is no way to check for "TX ready", the only thing we have is a | ||
* check for "TX FIFO empty". This is not optimal, as we might wait here | ||
* even if there is space in the FIFO. Seems the 16550 was built based on | ||
* the idea that software keeps track of the FIFO usage. A driver would | ||
* know how much space is left in the FIFO, so it can write new data | ||
* either immediately or buffer it. If the FIFO empty interrupt arrives, | ||
* data can be written from the buffer to fill the FIFO. | ||
* However, since QEMU does not emulate a FIFO, we can just implement a | ||
* simple model here and block - expecting to never block practically. | ||
*/ | ||
while (!ns16550_is_tx_empty(regs)) { | ||
/* busy waiting loop */ | ||
} | ||
|
||
/* Extract the byte to send, drop any flags. */ | ||
uint8_t byte = (uint8_t)c; | ||
|
||
/* If SERIAL_AUTO_CR is enabled, a CR is sent before any LF. */ | ||
if ((byte == '\n') && (dev->flags & SERIAL_AUTO_CR)) { | ||
ns16550_tx_byte(regs, '\r'); | ||
/* Since we have blocked until the FIFO is empty, we don't have to wait | ||
* here. | ||
*/ | ||
} | ||
|
||
ns16550_tx_byte(regs, byte); | ||
|
||
return byte; | ||
} | ||
|
||
int uart_getchar(ps_chardevice_t *dev) | ||
{ | ||
ns16550_regs_t *regs = uart_get_regs(dev); | ||
return ns16550_get_char_or_EOF(regs); | ||
} | ||
|
||
static void uart_handle_irq(ps_chardevice_t *dev) | ||
{ | ||
/* No IRQ handling required here. */ | ||
} | ||
|
||
static void ns16550_init(ns16550_regs_t *regs) | ||
{ | ||
/* disable interrupts */ | ||
regs->dlm_ier = 0; | ||
|
||
|
||
regs->lcr = NS16550_LCR_DLAB; /* baud rate divisor setup */ | ||
// uint16_t clk_divisor = ??? | ||
// regs->dlm_ier = (clk_divisor >> 8) & 0xFF; | ||
// regs->rbr_dll_thr = clk_divisor & 0xFF; | ||
regs->lcr = 0x03; /* set 8N1, clear DLAB to end baud rate divisor setup */ | ||
|
||
/* enable and reset FIFOs, interrupt for each byte */ | ||
regs->iir_fcr = NS16550_FCR_ENABLE_FIFOS | ||
| NS16550_FCR_RESET_RX_FIFO | ||
| NS16550_FCR_RESET_TX_FIFO | ||
| NS16550_FCR_TRIGGER_1; | ||
|
||
/* enable RX interrupts */ | ||
regs->dlm_ier = NS16550_IER_ERBFI; | ||
} | ||
|
||
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; | ||
} | ||
|
||
/* 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; | ||
|
||
/* Initialize the device. */ | ||
ns16550_regs_t *regs = uart_get_regs(dev); | ||
ns16550_init(regs); | ||
|
||
return 0; | ||
} |