Bring up minos on the Allwinner D1 (Lichee RV) over FEL/xfel and add bidirectional UART so the console can read keystrokes, not just print. D1 / Lichee RV boot: - riscv arch.c: select UART0 base by -DBOARD_D1 (0x02500000 vs QEMU 0x10000000) and add a reg-shift abstraction (uart_rd/uart_wr): the D1's Synopsys DW-8250 uses 32-bit registers at 4-byte spacing, which is what made the first hardware boot silent. - d1_uart0_init(): gate/deassert UART0 clock, mux PB8/PB9 to UART0, set 115200 8N1, clear FIFOs, force non-loopback, drain stale RX. - link-d1.ld: link for DRAM at 0x40000000 (xfel exec target, M-mode). - Makefile: 'lichee' builds the flat binary; 'run-lichee' loads it via xfel (ddr d1 / write / exec). - arch_name() reports the concrete board. UART input (all archs): - arch contract gains arch_uart_getc() and arch_uart_rx_ready(), implemented for riscv, x86 (COM1) and arm (Cadence UART). - console gains getc() and drain_rx(). - kmain() runs an interactive echo loop (seed of the M3 console). Verified: echo works under QEMU on x86/riscv/arm, and on real D1 hardware (banner once, then live keystroke echo over UART0 @115200).
88 lines
2.4 KiB
C
88 lines
2.4 KiB
C
/* arch/arm/arch.c — Cortex-A9 (Zynq-7000 PS) implementation.
|
|
* Uses the Cadence UART (UART0 at 0xE0000000) present on the real Zynq
|
|
* and modeled by QEMU's xilinx-zynq-a9 machine. On QEMU/Zynq the UART is
|
|
* already configured by the bootrom, so we just poll TX-full and write. */
|
|
#include "arch.h"
|
|
#include "console.h"
|
|
|
|
#define UART0_BASE 0xE0000000UL
|
|
#define UART_CR 0x00 /* control register */
|
|
#define UART_MR 0x04 /* mode register */
|
|
#define UART_SR 0x2C /* channel status register */
|
|
#define UART_FIFO 0x30 /* tx/rx FIFO */
|
|
#define UART_SR_TXFULL (1u << 4)
|
|
#define UART_SR_RXEMPTY (1u << 1)
|
|
|
|
/* Control register bits */
|
|
#define CR_RXRES (1u << 0) /* RX logic reset */
|
|
#define CR_TXRES (1u << 1) /* TX logic reset */
|
|
#define CR_RXEN (1u << 2)
|
|
#define CR_TXEN (1u << 4)
|
|
|
|
static volatile unsigned int *reg(unsigned long off)
|
|
{
|
|
return (volatile unsigned int *)(UART0_BASE + off);
|
|
}
|
|
|
|
void arch_early_init(void)
|
|
{
|
|
/* Reset and enable TX/RX. On real Zynq the bootrom sets the baud
|
|
* divisors; QEMU ignores baud, so enabling TX is enough. */
|
|
*reg(UART_CR) = CR_RXRES | CR_TXRES; /* pulse resets */
|
|
*reg(UART_MR) = 0x20; /* 8N1, normal */
|
|
*reg(UART_CR) = CR_RXEN | CR_TXEN; /* enable TX/RX */
|
|
}
|
|
|
|
void arch_uart_putc(char c)
|
|
{
|
|
while (*reg(UART_SR) & UART_SR_TXFULL)
|
|
;
|
|
*reg(UART_FIFO) = (unsigned int)(unsigned char)c;
|
|
}
|
|
|
|
char arch_uart_getc(void)
|
|
{
|
|
while (*reg(UART_SR) & UART_SR_RXEMPTY) /* wait for a received byte */
|
|
;
|
|
return (char)(*reg(UART_FIFO) & 0xff);
|
|
}
|
|
|
|
int arch_uart_rx_ready(void)
|
|
{
|
|
return (*reg(UART_SR) & UART_SR_RXEMPTY) ? 0 : 1;
|
|
}
|
|
|
|
/* M1: point the vector base (VBAR) at our table (defined in vectors.S). */
|
|
extern void vector_table(void);
|
|
void arch_set_trap_vector(void)
|
|
{
|
|
unsigned long v = (unsigned long)vector_table;
|
|
__asm__ volatile("mcr p15, 0, %0, c12, c0, 0" :: "r"(v)); /* VBAR */
|
|
}
|
|
|
|
const char *arch_name(void)
|
|
{
|
|
return "arm (cortex-a9, zynq-7000)";
|
|
}
|
|
|
|
const char *arch_cpu_mode(void)
|
|
{
|
|
/* CPSR bits[4:0] = mode. 0x13=SVC, 0x10=USR, 0x1F=SYS, 0x12=IRQ. */
|
|
unsigned long cpsr;
|
|
__asm__ volatile("mrs %0, cpsr" : "=r"(cpsr));
|
|
switch (cpsr & 0x1f) {
|
|
case 0x10: return "USR";
|
|
case 0x11: return "FIQ";
|
|
case 0x12: return "IRQ";
|
|
case 0x13: return "SVC";
|
|
case 0x1a: return "HYP";
|
|
case 0x1f: return "SYS";
|
|
default: return "?";
|
|
}
|
|
}
|
|
|
|
void arch_halt(void)
|
|
{
|
|
for (;;)
|
|
__asm__ volatile("wfi");
|
|
}
|