minos/arch/arm/arch.c
Шурупов Илья Викторович 775a02cf3f Add D1/Lichee RV hardware boot and UART input
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).
2026-09-25 21:13:59 +03:00

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");
}