1// Copyright 2018 The Fuchsia Authors 2// Use of this source code is governed by a MIT-style 3// license that can be found in the LICENSE file or at 4// https://opensource.org/licenses/MIT 5 6#include <arch/arm64/periphmap.h> 7#include <dev/interrupt.h> 8#include <dev/uart.h> 9#include <kernel/thread.h> 10#include <lib/cbuf.h> 11#include <lib/debuglog.h> 12#include <pdev/driver.h> 13#include <pdev/uart.h> 14#include <platform/debug.h> 15#include <reg.h> 16#include <stdio.h> 17#include <trace.h> 18#include <zircon/boot/driver-config.h> 19 20// clang-format off 21 22// Registers 23 24#define UART_RBR (0x0) // RX Buffer Register (read-only) 25#define UART_THR (0x0) // TX Buffer Register (write-only) 26#define UART_IER (0x4) // Interrupt Enable Register 27#define UART_IIR (0x8) // Interrupt Identification Register (read-only) 28#define UART_FCR (0x8) // FIFO Control Register (write-only) 29#define UART_LCR (0xc) // Line Control Register 30#define UART_MCR (0x10) // Modem Control Register 31#define UART_LSR (0x14) // Line Status Register 32#define UART_MSR (0x18) // Modem Status Register 33#define UART_SCR (0x1c) // Scratch Register 34#define UART_DLL (0x0) // Divisor Latch LS (Only when LCR.DLAB = 1) 35#define UART_DLM (0x4) // Divisor Latch MS (Only when LCR.DLAB = 1) 36#define UART_EFR (0x8) // Enhanced Feature Register (Only when LCR = 0xbf) 37#define UART_XON1 (0x10) // XON1 Char Register (Only when LCR = 0xbf) 38#define UART_XON2 (0x14) // XON2 Char Register (Only when LCR = 0xbf) 39#define UART_XOFF1 (0x18) // XOFF1 Char Register (Only when LCR = 0xbf) 40#define UART_XOFF2 (0x1c) // XOFF2 Char Register (Only when LCR = 0xbf) 41#define UART_AUTOBAUD_EN (0x20) // Auto Baud Detect Enable Register 42#define UART_HIGHSPEED (0x24) // High Speed Mode Register 43#define UART_SAMPLE_COUNT (0x28) // Sample Counter Register 44#define UART_SAMPLE_POINT (0x2c) // Sample Point Register 45#define UART_AUTOBAUD_REG (0x30) // Auto Baud Monitor Register 46#define UART_RATE_FIX_AD (0x34) // Clock Rate Fix Register 47#define UART_AUTOBAUD_SAMPLE (0x38) // Auto Baud Sample Register 48#define UART_GUARD (0x3c) // Guard Time Added Register 49#define UART_ESCAPE_DAT (0x40) // Escape Character Register 50#define UART_ESCAPE_EN (0x44) // Escape Enable Register 51#define UART_SLEEP_EN (0x48) // Sleep Enable Register 52#define UART_VFIFO_EN (0x4c) // DMA Enable Register 53#define UART_RXTRI_AD (0x50) // RX Trigger Address 54 55// IER 56#define UART_IER_ERBFI (1 << 0) 57#define UART_IER_ETBEI (1 << 1) 58#define UART_IER_ELSI (1 << 2) 59#define UART_IER_EDSSI (1 << 3) 60#define UART_IER_XOFFI (1 << 5) 61#define UART_IER_RTSI (1 << 6) 62#define UART_IER_CTSI (1 << 7) 63#define UART_IIR_NO_INT_PENDING (0x01) 64 65// IIR 66#define UART_IIR_RLS (0x06) // Receiver Line Status 67#define UART_IIR_RDA (0x04) // Receive Data Available 68#define UART_IIR_CTI (0x0C) // Character Timeout Indicator 69#define UART_IIR_THRE (0x02) // Transmit Holding Register Empty 70#define UART_IIR_MS (0x00) // Check Modem Status Register 71#define UART_IIR_SW_FLOW_CTRL (0x10) // Receive XOFF characters 72#define UART_IIR_HW_FLOW_CTRL (0x20) // CTS or RTS Rising Edge 73#define UART_IIR_FIFO_EN (0xc0) 74#define UART_IIR_INT_MASK (0x1f) 75 76// LSR 77#define UART_LSR_DR (1 << 0) 78#define UART_LSR_OE (1 << 1) 79#define UART_LSR_PE (1 << 2) 80#define UART_LSR_FE (1 << 3) 81#define UART_LSR_BI (1 << 4) 82#define UART_LSR_THRE (1 << 5) 83#define UART_LSR_TEMT (1 << 6) 84#define UART_LSR_FIFOERR (1 << 7) 85 86// clang-format on 87 88#define RXBUF_SIZE 32 89 90// values read from zbi 91static bool initialized = false; 92static vaddr_t uart_base = 0; 93static uint32_t uart_irq = 0; 94static cbuf_t uart_rx_buf; 95 96static bool uart_tx_irq_enabled = false; 97static event_t uart_dputc_event = EVENT_INITIAL_VALUE(uart_dputc_event, 98 true, 99 EVENT_FLAG_AUTOUNSIGNAL); 100 101static spin_lock_t uart_spinlock = SPIN_LOCK_INITIAL_VALUE; 102 103#define UARTREG(reg) (*(volatile uint32_t*)((uart_base) + (reg))) 104 105static void uart_irq_handler(void* arg) { 106 // read interrupt status and mask 107 while (UARTREG(UART_LSR) & UART_LSR_DR) { 108 if (cbuf_space_avail(&uart_rx_buf) == 0) { 109 break; 110 } 111 char c = UARTREG(UART_RBR) & 0xFF; 112 cbuf_write_char(&uart_rx_buf, c); 113 } 114 115 // Signal if anyone is waiting to TX 116 if (UARTREG(UART_LSR) & UART_LSR_THRE) { 117 spin_lock(&uart_spinlock); 118 event_signal(&uart_dputc_event, true); 119 spin_unlock(&uart_spinlock); 120 } 121} 122 123// panic-time getc/putc 124static int mt8167_uart_pputc(char c) { 125 if (!uart_base) { 126 return -1; 127 } 128 129 // spin while fifo is full 130 while (!(UARTREG(UART_LSR) & UART_LSR_THRE)) 131 ; 132 UARTREG(UART_THR) = c; 133 134 return 1; 135} 136 137static int mt8167_uart_pgetc(void) { 138 if (!uart_base) { 139 return ZX_ERR_NOT_SUPPORTED; 140 } 141 142 // spin while fifo is empty 143 while (!(UARTREG(UART_LSR) & UART_LSR_DR)) 144 ; 145 return UARTREG(UART_RBR); 146} 147 148static int mt8167_uart_getc(bool wait) { 149 if (!uart_base) { 150 return ZX_ERR_NOT_SUPPORTED; 151 } 152 153 if (initialized) { 154 char c; 155 if (cbuf_read_char(&uart_rx_buf, &c, wait) == 1) { 156 return c; 157 } 158 return ZX_ERR_INTERNAL; 159 } else { 160 // Interrupts are not enabled yet. Use panic calls for now 161 return mt8167_uart_pgetc(); 162 } 163} 164 165static void mt8167_dputs(const char* str, size_t len, 166 bool block, bool map_NL) { 167 spin_lock_saved_state_t state; 168 bool copied_CR = false; 169 170 if (!uart_base) { 171 return; 172 } 173 if (!uart_tx_irq_enabled) { 174 block = false; 175 } 176 spin_lock_irqsave(&uart_spinlock, state); 177 178 while (len > 0) { 179 // is FIFO full? 180 while (!(UARTREG(UART_LSR) & UART_LSR_THRE)) { 181 spin_unlock_irqrestore(&uart_spinlock, state); 182 if (block) { 183 event_wait(&uart_dputc_event); 184 } else { 185 arch_spinloop_pause(); 186 } 187 spin_lock_irqsave(&uart_spinlock, state); 188 } 189 if (*str == '\n' && map_NL && !copied_CR) { 190 copied_CR = true; 191 mt8167_uart_pputc('\r'); 192 } else { 193 copied_CR = false; 194 mt8167_uart_pputc(*str++); 195 len--; 196 } 197 } 198 spin_unlock_irqrestore(&uart_spinlock, state); 199} 200 201static void mt8167_start_panic(void) { 202 uart_tx_irq_enabled = false; 203} 204 205static const struct pdev_uart_ops uart_ops = { 206 .getc = mt8167_uart_getc, 207 .pputc = mt8167_uart_pputc, 208 .pgetc = mt8167_uart_pgetc, 209 .start_panic = mt8167_start_panic, 210 .dputs = mt8167_dputs, 211}; 212 213static void mt8167_uart_init(const void* driver_data, uint32_t length) { 214 // create circular buffer to hold received data 215 cbuf_initialize(&uart_rx_buf, RXBUF_SIZE); 216 217 // register uart irq 218 register_int_handler(uart_irq, &uart_irq_handler, NULL); 219 220// TODO: Configure UART interrupt support here 221 222// TODO: Enable interrupt support after we have a way to set the interrupt to active-low 223#if 0 224 if (dlog_bypass() == true) 225 uart_tx_irq_enabled = false; 226 else { 227 // start up tx driven output 228 printf("UART: started IRQ driven TX\n"); 229 uart_tx_irq_enabled = true; 230 } 231 232 initialized = true; 233 234 // TODO we will need to talk to the pinmux controller to set the interrupt to active low. 235 // configure_interrupt() doesn't actually support that. 236 configure_interrupt(uart_irq, IRQ_TRIGGER_MODE_LEVEL, IRQ_POLARITY_ACTIVE_LOW); 237 unmask_interrupt(uart_irq); 238#endif 239} 240 241static void mt8167_uart_init_early(const void* driver_data, uint32_t length) { 242 ASSERT(length >= sizeof(dcfg_simple_t)); 243 auto driver = static_cast<const dcfg_simple_t*>(driver_data); 244 ASSERT(driver->mmio_phys && driver->irq); 245 246 uart_base = periph_paddr_to_vaddr(driver->mmio_phys); 247 ASSERT(uart_base); 248 uart_irq = driver->irq; 249 250 pdev_register_uart(&uart_ops); 251} 252 253LK_PDEV_INIT(mt8167_uart_init_early, KDRV_MT8167_UART, mt8167_uart_init_early, LK_INIT_LEVEL_PLATFORM_EARLY); 254LK_PDEV_INIT(mt8167_uart_init, KDRV_MT8167_UART, mt8167_uart_init, LK_INIT_LEVEL_PLATFORM); 255