Skip to content

Commit

Permalink
wip JH7110 (VisionFive, Star64)
Browse files Browse the repository at this point in the history
  • Loading branch information
Axel Heider committed May 21, 2024
1 parent a06c6bf commit b44f4ab
Show file tree
Hide file tree
Showing 3 changed files with 220 additions and 0 deletions.
46 changes: 46 additions & 0 deletions libplatsupport/plat_include/star64/platsupport/plat/serial.h
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
48 changes: 48 additions & 0 deletions libplatsupport/src/plat/star64/chardev.c
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;
}
126 changes: 126 additions & 0 deletions libplatsupport/src/plat/star64/serial.c
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;
}

0 comments on commit b44f4ab

Please sign in to comment.