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 <reg.h> 7#include <stdio.h> 8#include <trace.h> 9#include <arch/arm64/periphmap.h> 10#include <lib/cbuf.h> 11#include <lib/debuglog.h> 12#include <kernel/thread.h> 13#include <dev/interrupt.h> 14#include <dev/uart.h> 15#include <platform/debug.h> 16#include <pdev/driver.h> 17#include <pdev/uart.h> 18#include <zircon/boot/driver-config.h> 19 20/* Registers */ 21#define MX8_URXD (0x00) 22#define MX8_UTXD (0x40) 23#define MX8_UCR1 (0x80) 24#define MX8_UCR2 (0x84) 25#define MX8_UCR3 (0x88) 26#define MX8_UCR4 (0x8C) 27#define MX8_UFCR (0x90) 28#define MX8_USR1 (0x94) 29#define MX8_USR2 (0x98) 30#define MX8_UTS (0xB4) 31 32/* UCR1 Bit Definition */ 33#define UCR1_TRDYEN (1 << 13) 34#define UCR1_RRDYEN (1 << 9) 35#define UCR1_UARTEN (1 << 0) 36 37/* UCR2 Bit Definition */ 38#define UCR2_TXEN (1 << 2) 39#define UCR2_RXEN (1 << 1) 40#define UCR2_SRST (1 << 0) 41 42/* UFCR Bit Definition */ 43#define UFCR_TXTL(x) (x << 10) 44#define UFCR_RXTL(x) (x << 0) 45#define UFCR_MASK (0x3f) 46 47/* USR1 Bit Definition */ 48#define USR1_TRDY (1 << 13) 49#define USR1_RRDY (1 << 9) 50 51/* USR2 Bit Definition */ 52#define USR2_TXFE (1 << 14) 53 54/* UTS Bit Definition */ 55#define UTS_TXEMPTY (1 << 6) 56#define UTS_RXEMPTY (1 << 5) 57#define UTS_TXFULL (1 << 4) 58#define UTS_RXFULL (1 << 3) 59 60#define RXBUF_SIZE 32 61 62 63// values read from zbi 64static bool initialized = false; 65static vaddr_t uart_base = 0; 66static uint32_t uart_irq = 0; 67static cbuf_t uart_rx_buf; 68// static cbuf_t uart_tx_buf; 69 70static bool uart_tx_irq_enabled = false; 71static event_t uart_dputc_event = EVENT_INITIAL_VALUE(uart_dputc_event, 72 true, 73 EVENT_FLAG_AUTOUNSIGNAL); 74 75static spin_lock_t uart_spinlock = SPIN_LOCK_INITIAL_VALUE; 76 77#define UARTREG(reg) (*(volatile uint32_t*)((uart_base) + (reg))) 78 79static void uart_irq_handler(void *arg) 80{ 81 /* read interrupt status and mask */ 82 while ((UARTREG(MX8_USR1) & USR1_RRDY)) { 83 if (cbuf_space_avail(&uart_rx_buf) == 0) { 84 break; 85 } 86 char c = UARTREG(MX8_URXD) & 0xFF; 87 cbuf_write_char(&uart_rx_buf, c); 88 } 89 90 /* Signal if anyone is waiting to TX */ 91 if (UARTREG(MX8_UCR1) & UCR1_TRDYEN) { 92 spin_lock(&uart_spinlock); 93 if (!(UARTREG(MX8_USR2) & UTS_TXFULL)) { 94 // signal 95 event_signal(&uart_dputc_event, true); 96 } 97 spin_unlock(&uart_spinlock); 98 } 99} 100 101 102 103/* panic-time getc/putc */ 104static int imx_uart_pputc(char c) 105{ 106 if (!uart_base) { 107 return -1; 108 } 109 110 /* spin while fifo is full */ 111 while (UARTREG(MX8_UTS) & UTS_TXFULL) 112 ; 113 UARTREG(MX8_UTXD) = c; 114 115 return 1; 116} 117 118static int imx_uart_pgetc(void) 119{ 120 if (!uart_base) { 121 return ZX_ERR_NOT_SUPPORTED; 122 } 123 124 if ((UARTREG(MX8_UTS) & UTS_RXEMPTY)) { 125 return ZX_ERR_INTERNAL; 126 } 127 128 return UARTREG(MX8_URXD); 129} 130 131static int imx_uart_getc(bool wait) 132{ 133 if (!uart_base) { 134 return ZX_ERR_NOT_SUPPORTED; 135 } 136 137 if (initialized) { 138 char c; 139 if (cbuf_read_char(&uart_rx_buf, &c, wait) == 1) { 140 return c; 141 } 142 return ZX_ERR_INTERNAL; 143 } else { 144 // Interrupts are not enabled yet. Use panic calls for now 145 return imx_uart_pgetc(); 146 } 147 148} 149 150static void imx_dputs(const char* str, size_t len, 151 bool block, bool map_NL) 152{ 153 spin_lock_saved_state_t state; 154 bool copied_CR = false; 155 156 if (!uart_base) { 157 return; 158 } 159 if (!uart_tx_irq_enabled) { 160 block = false; 161 } 162 spin_lock_irqsave(&uart_spinlock, state); 163 164 while (len > 0) { 165 // is FIFO full? 166 while ((UARTREG(MX8_UTS) & UTS_TXFULL)) { 167 spin_unlock_irqrestore(&uart_spinlock, state); 168 if (block) { 169 event_wait(&uart_dputc_event); 170 } else { 171 arch_spinloop_pause(); 172 } 173 spin_lock_irqsave(&uart_spinlock, state); 174 } 175 if (*str == '\n' && map_NL && !copied_CR) { 176 copied_CR = true; 177 imx_uart_pputc('\r'); 178 } else { 179 copied_CR = false; 180 imx_uart_pputc(*str++); 181 len--; 182 } 183 } 184 spin_unlock_irqrestore(&uart_spinlock, state); 185} 186 187static void imx_start_panic(void) 188{ 189 uart_tx_irq_enabled = false; 190} 191 192static const struct pdev_uart_ops uart_ops = { 193 .getc = imx_uart_getc, 194 .pputc = imx_uart_pputc, 195 .pgetc = imx_uart_pgetc, 196 .start_panic = imx_start_panic, 197 .dputs = imx_dputs, 198}; 199 200static void imx_uart_init(const void* driver_data, uint32_t length) 201{ 202 uint32_t regVal; 203 204 // create circular buffer to hold received data 205 cbuf_initialize(&uart_rx_buf, RXBUF_SIZE); 206 207 // register uart irq 208 register_int_handler(uart_irq, &uart_irq_handler, NULL); 209 210 // set rx fifo threshold to 1 character 211 regVal = UARTREG(MX8_UFCR); 212 regVal &= ~UFCR_RXTL(UFCR_MASK); 213 regVal &= ~UFCR_TXTL(UFCR_MASK); 214 regVal |= UFCR_RXTL(1); 215 regVal |= UFCR_TXTL(0x2); 216 UARTREG(MX8_UFCR) = regVal; 217 218 // enable rx interrupt 219 regVal = UARTREG(MX8_UCR1); 220 regVal |= UCR1_RRDYEN; 221 if (dlog_bypass() == false) { 222 // enable tx interrupt 223 regVal |= UCR1_TRDYEN; 224 } 225 UARTREG(MX8_UCR1) = regVal; 226 227 // enable rx and tx transmisster 228 regVal = UARTREG(MX8_UCR2); 229 regVal |= UCR2_RXEN | UCR2_TXEN; 230 UARTREG(MX8_UCR2) = regVal; 231 232 if (dlog_bypass() == true) 233 uart_tx_irq_enabled = false; 234 else { 235 /* start up tx driven output */ 236 printf("UART: started IRQ driven TX\n"); 237 uart_tx_irq_enabled = true; 238 } 239 240 initialized = true; 241 242 // enable interrupts 243 unmask_interrupt(uart_irq); 244} 245 246static void imx_uart_init_early(const void* driver_data, uint32_t length) { 247 ASSERT(length >= sizeof(dcfg_simple_t)); 248 const dcfg_simple_t* driver = driver_data; 249 ASSERT(driver->mmio_phys && driver->irq); 250 251 uart_base = periph_paddr_to_vaddr(driver->mmio_phys); 252 ASSERT(uart_base); 253 uart_irq = driver->irq; 254 255 pdev_register_uart(&uart_ops); 256} 257 258LK_PDEV_INIT(imx_uart_init_early, KDRV_NXP_IMX_UART, imx_uart_init_early, LK_INIT_LEVEL_PLATFORM_EARLY); 259LK_PDEV_INIT(imx_uart_init, KDRV_NXP_IMX_UART, imx_uart_init, LK_INIT_LEVEL_PLATFORM); 260