summaryrefslogtreecommitdiff
path: root/drivers/serial/pl011.c
diff options
context:
space:
mode:
authorBradley Morgan <brads@mainlining.org>2026-10-03 18:57:38 +0000
committerBradley Morgan <brads@mainlining.org>2026-10-03 18:57:38 +0000
commit00fc82f17cff9295387d516384ecdc443acb1b22 (patch)
tree798bef0185878286028fe0eaf7594616a66903e4 /drivers/serial/pl011.c
parent7e8514efb64774b6a635b87ae945e519a7c38551 (diff)
tashaboot: arm64 bringup, semihosting payload loader
consoles on the pl011 the dtb points at, walks the memory node and chosen, loads an arm64 kernel Image over semihosting and jumps to it with the documented register state. string functions, printf and libfdt are vendored from lk (lk2nd) and u-boot so the libc surface is the one those projects already proved. built and booted on qemu virt, payload exits clean through semihosting.
Diffstat (limited to 'drivers/serial/pl011.c')
-rw-r--r--drivers/serial/pl011.c56
1 files changed, 56 insertions, 0 deletions
diff --git a/drivers/serial/pl011.c b/drivers/serial/pl011.c
new file mode 100644
index 0000000..d11aa00
--- /dev/null
+++ b/drivers/serial/pl011.c
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * pl011.c - ARM PrimeCell PL011 serial, polled io
+ *
+ * the register layout is shared with the PL010 so we use the combined
+ * struct like u-boot does. base comes from the dtb, never hardcoded.
+ *
+ * Copyright (C) 2026 Bradley Morgan <brads@mainlining.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <reg.h>
+#include <drivers/serial/pl011.h>
+
+void pl011_init(uintptr_t base, uint32 clock, uint32 baud)
+{
+ struct pl01x_regs *regs = (struct pl01x_regs *)base;
+ uint32 divisor;
+ uint32 remainder;
+ uint32 fraction;
+
+ /* baud divisor, same math as the PL011 TRM section 3.3.3 */
+ divisor = clock / (16 * baud);
+ remainder = clock % (16 * baud);
+ fraction = (8 * remainder + baud / 2) / baud;
+
+ writel(0, &regs->pl011_cr);
+ writel(0x7ff, &regs->pl011_icr);
+ writel(divisor, &regs->pl011_ibrd);
+ writel(fraction, &regs->pl011_fbrd);
+ writel((3 << 5) | (1 << 4), &regs->pl011_lcrh);
+ writel((1 << 0) | (1 << 8) | (1 << 9), &regs->pl011_cr);
+}
+
+void pl011_putc(uintptr_t base, char c)
+{
+ struct pl01x_regs *regs = (struct pl01x_regs *)base;
+
+ while (readl(&regs->fr) & UART_PL01x_FR_TXFF)
+ ;
+ writel(c, &regs->dr);
+}
+
+int pl011_getc(uintptr_t base)
+{
+ struct pl01x_regs *regs = (struct pl01x_regs *)base;
+
+ if (readl(&regs->fr) & UART_PL01x_FR_RXFE)
+ return -1;
+ return readl(&regs->dr) & 0xff;
+}