| // Copyright 2016 The Fuchsia Authors |
| // Copyright (c) 2009 Corey Tabaka |
| // |
| // Use of this source code is governed by a MIT-style |
| // license that can be found in the LICENSE file or at |
| // https://opensource.org/licenses/MIT |
| |
| #include <stdarg.h> |
| #include <reg.h> |
| #include <stdio.h> |
| #include <kernel/thread.h> |
| #include <kernel/timer.h> |
| #include <vm/pmm.h> |
| #include <lk/init.h> |
| #include <arch/x86.h> |
| #include <arch/x86/apic.h> |
| #include <lib/cbuf.h> |
| #include <dev/interrupt.h> |
| #include <kernel/cmdline.h> |
| #include <platform.h> |
| #include <platform/pc.h> |
| #include <platform/pc/bootloader.h> |
| #include <platform/console.h> |
| #include <platform/debug.h> |
| #include <trace.h> |
| #include <zircon/types.h> |
| |
| #include "platform_p.h" |
| |
| static const int uart_baud_rate = 115200; |
| static int uart_io_port = 0x3f8; |
| static uint64_t uart_mem_addr = 0; |
| static uint32_t uart_irq = ISA_IRQ_SERIAL1; |
| |
| cbuf_t console_input_buf; |
| static bool output_enabled = false; |
| |
| static uint8_t uart_read(uint8_t reg) |
| { |
| if (uart_mem_addr) { |
| return (uint8_t)readl(uart_mem_addr + 4 * reg); |
| } else { |
| return (uint8_t)inp((uint16_t)(uart_io_port + reg)); |
| } |
| } |
| |
| static void uart_write(uint8_t reg, uint8_t val) |
| { |
| if (uart_mem_addr) { |
| writel(val, uart_mem_addr + 4 * reg); |
| } else { |
| outp((uint16_t)(uart_io_port + reg), val); |
| } |
| } |
| |
| static enum handler_return platform_drain_debug_uart_rx(void) |
| { |
| unsigned char c; |
| bool resched = false; |
| |
| while (uart_read(5) & (1<<0)) { |
| c = uart_read(0); |
| cbuf_write_char(&console_input_buf, c, false); |
| resched = true; |
| } |
| |
| return resched ? INT_RESCHEDULE : INT_NO_RESCHEDULE; |
| } |
| |
| static enum handler_return uart_irq_handler(void *arg) |
| { |
| return platform_drain_debug_uart_rx(); |
| } |
| |
| // for devices where the uart rx interrupt doesn't seem to work |
| static enum handler_return uart_rx_poll(struct timer *t, zx_time_t now, void *arg) |
| { |
| timer_set(t, now + ZX_MSEC(10), TIMER_SLACK_CENTER, ZX_MSEC(1), uart_rx_poll, NULL); |
| return platform_drain_debug_uart_rx(); |
| } |
| |
| // also called from the pixel2 quirk file |
| void platform_debug_start_uart_timer(void); |
| |
| void platform_debug_start_uart_timer(void) |
| { |
| static timer_t uart_rx_poll_timer; |
| static bool started = false; |
| |
| if (!started) { |
| started = true; |
| timer_init(&uart_rx_poll_timer); |
| timer_set(&uart_rx_poll_timer, current_time() + ZX_MSEC(10), |
| TIMER_SLACK_CENTER, ZX_MSEC(1), uart_rx_poll, NULL); |
| } |
| } |
| |
| #include <dev/pcie_bus_driver.h> |
| |
| extern "C" void uart_reinit(void) { |
| auto cfg = PcieBusDriver::GetDriver()->GetConfig(0, 0x19, 0); |
| uint64_t bar0 = cfg->Read(PciConfig::kBAR(0)) | ((uint64_t)cfg->Read(PciConfig::kBAR(1)) >> 32); |
| bar0 &= ~7; |
| uart_mem_addr = (uint64_t)paddr_to_kvaddr(bar0); |
| |
| if (uart_irq) { |
| mask_interrupt(uart_irq); |
| uart_irq = 0; |
| platform_debug_start_uart_timer(); |
| } |
| |
| /* configure the uart */ |
| int divisor = 115200 / uart_baud_rate; |
| |
| /* get basic config done so that tx functions */ |
| uart_write(1, 0); // mask all irqs |
| uart_write(3, 0x80); // set up to load divisor latch |
| uart_write(0, static_cast<uint8_t>(divisor)); // lsb |
| uart_write(1, static_cast<uint8_t>(divisor >> 8)); // msb |
| uart_write(3, 3); // 8N1 |
| uart_write(2, 0xc7); // enable FIFO, clear, 14-byte threshold |
| |
| uart_write(1, 0x1); // enable receive data available interrupt |
| |
| // modem control register: Axiliary Output 2 is another IRQ enable bit |
| const uint8_t mcr = uart_read(4); |
| uart_write(4, mcr | 0x8); |
| } |
| |
| void platform_init_debug_early(void) |
| { |
| switch (bootloader.uart.type) { |
| case BOOTDATA_UART_PC_PORT: |
| uart_io_port = static_cast<uint32_t>(bootloader.uart.base); |
| uart_irq = bootloader.uart.irq; |
| break; |
| case BOOTDATA_UART_PC_MMIO: |
| uart_mem_addr = (uint64_t)paddr_to_kvaddr(bootloader.uart.base); |
| uart_irq = bootloader.uart.irq; |
| break; |
| } |
| |
| /* configure the uart */ |
| int divisor = 115200 / uart_baud_rate; |
| |
| /* get basic config done so that tx functions */ |
| uart_write(1, 0); // mask all irqs |
| uart_write(3, 0x80); // set up to load divisor latch |
| uart_write(0, static_cast<uint8_t>(divisor)); // lsb |
| uart_write(1, static_cast<uint8_t>(divisor >> 8)); // msb |
| uart_write(3, 3); // 8N1 |
| uart_write(2, 0xc7); // enable FIFO, clear, 14-byte threshold |
| |
| output_enabled = true; |
| } |
| |
| void platform_init_debug(void) |
| { |
| /* finish uart init to get rx going */ |
| cbuf_initialize(&console_input_buf, 1024); |
| |
| if ((uart_irq == 0) || cmdline_get_bool("kernel.debug_uart_poll", false)) { |
| printf("debug-uart: polling enabled\n"); |
| platform_debug_start_uart_timer(); |
| } else { |
| uart_irq = apic_io_isa_to_global(static_cast<uint8_t>(uart_irq)); |
| register_int_handler(uart_irq, uart_irq_handler, NULL); |
| unmask_interrupt(uart_irq); |
| |
| uart_write(1, 0x1); // enable receive data available interrupt |
| |
| // modem control register: Axiliary Output 2 is another IRQ enable bit |
| const uint8_t mcr = uart_read(4); |
| uart_write(4, mcr | 0x8); |
| } |
| } |
| |
| static void debug_uart_putc(char c) |
| { |
| #if WITH_LEGACY_PC_CONSOLE |
| cputc(c); |
| #endif |
| if (unlikely(!output_enabled)) |
| return; |
| |
| while ((uart_read(5) & (1<<6)) == 0) { |
| arch_spinloop_pause(); |
| } |
| uart_write(0, c); |
| } |
| |
| void platform_dputs(const char* str, size_t len) |
| { |
| while (len-- > 0) { |
| char c = *str++; |
| if (c == '\n') { |
| debug_uart_putc('\r'); |
| } |
| debug_uart_putc(c); |
| } |
| } |
| |
| int platform_dgetc(char *c, bool wait) |
| { |
| return static_cast<int>(cbuf_read_char(&console_input_buf, c, wait)); |
| } |
| |
| // panic time polling IO for the panic shell |
| void platform_pputc(char c) |
| { |
| platform_dputc(c); |
| } |
| |
| int platform_pgetc(char *c, bool wait) |
| { |
| if (uart_read(5) & (1<<0)) { |
| *c = uart_read(0); |
| return 0; |
| } |
| |
| return -1; |
| } |