/* * console.c - the console dprintf writes to. the dtb picks the uart, * chosen/stdout-path first, any arm,pl011 node as fallback. * * Copyright (c) 2008 Travis Geiselbrecht * Copyright (c) 2026 Bradley Morgan * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files * (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, * and to permit persons to whom the Software is furnished to do so, * subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include static uintptr_t tb_uart_base; static uint32 tb_uart_clock; static bool tb_uart_ready; /* * read one reg pair out of a node. cells are u32s in the blob, address * and size widths come from the parent #address-cells/#size-cells, * 2/1 is what every board we care about uses, qemu virt included. */ static int tb_read_reg(const void *blob, int node, int index, uintptr_t *base, uint32 *size) { const fdt32_t *prop; int len; int offset; prop = fdt_getprop(blob, node, "reg", &len); if (!prop) return -1; /* * a reg pair here is , 3 cells, 2 for the * address and 1 for the size. index 0 is the pair we want. */ offset = index * 3; if (offset + 3 > len / 4) return -1; *base = ((uintptr_t)fdt32_to_cpu(prop[offset]) << 32) | fdt32_to_cpu(prop[offset + 1]); *size = fdt32_to_cpu(prop[offset + 2]); return 0; } static int tb_console_from_stdout_path(const void *blob) { const char *path; const char *p; char buf[32]; int node; int chosen; int len; chosen = fdt_path_offset(blob, "/chosen"); if (chosen < 0) return -1; path = fdt_getprop(blob, chosen, "stdout-path", &len); if (!path || len <= 0) return -1; /* the path can carry :baud after the node, cut it */ p = strchr(path, ':'); len = p ? (int)(p - path) : strlen(path); if (len >= (int)sizeof(buf)) len = sizeof(buf) - 1; memcpy(buf, path, len); buf[len] = 0; /* a leading / is a full path, otherwise it's an alias */ if (buf[0] == '/') node = fdt_path_offset(blob, buf); else node = fdt_path_offset(blob, fdt_get_alias(blob, buf)); if (node < 0) return -1; return node; } static int tb_console_scan(const void *blob) { const char *compat; int node; int len; fdt_for_each_subnode(node, blob, 0) { compat = fdt_getprop(blob, node, "compatible", &len); if (!compat) continue; if (fdt_stringlist_contains(compat, len, "arm,pl011")) return node; } return -1; } int tb_console_init(const void *dtb) { const fdt32_t *clk; int node; uint32 size; uint32 baud; int len; node = tb_console_from_stdout_path(dtb); if (node < 0) node = tb_console_scan(dtb); if (node < 0) return -1; if (tb_read_reg(dtb, node, 0, &tb_uart_base, &size)) return -1; clk = fdt_getprop(dtb, node, "clock-frequency", &len); tb_uart_clock = (clk && len == 4) ? fdt32_to_cpu(*clk) : 24000000; { const fdt32_t *cs; int cslen; cs = fdt_getprop(dtb, node, "current-speed", &cslen); baud = (cs && cslen == 4) ? fdt32_to_cpu(*cs) : 115200; } pl011_init(tb_uart_base, tb_uart_clock, baud); tb_uart_ready = true; return 0; } static int tb_console_out(char c, void *state) { if (c == '\n') pl011_putc(tb_uart_base, '\r'); pl011_putc(tb_uart_base, c); return 1; } int _dprintf(const char *fmt, ...) { va_list ap; int ret; if (!tb_uart_ready) return 0; va_start(ap, fmt); ret = _dvprintf(fmt, ap); va_end(ap); return ret; } int _dvprintf(const char *fmt, va_list ap) { if (!tb_uart_ready) return 0; return _printf_engine(tb_console_out, NULL, fmt, ap); } void platform_halt(void) { for (;;) __asm__ volatile("wfe"); }