1 // SPDX-License-Identifier: BSD-2-Clause 2 /* 3 * Copyright (c) 2015, Linaro Limited 4 */ 5 6 #include <compiler.h> 7 #include <console.h> 8 #include <drivers/serial8250_uart.h> 9 #include <io.h> 10 #include <keep.h> 11 #include <kernel/dt.h> 12 #include <kernel/dt_driver.h> 13 #include <util.h> 14 15 /* uart register defines */ 16 #define UART_RHR 0x0 17 #define UART_THR 0x0 18 #define UART_IER 0x4 19 #define UART_ISR 0x8 20 #define UART_FCR 0x8 21 #define UART_LCR 0xc 22 #define UART_MCR 0x10 23 #define UART_LSR 0x14 24 #define UART_MSR 0x18 25 #define UART_SPR 0x1c 26 27 /* uart status register bits */ 28 #define LSR_TEMT 0x40 /* Transmitter empty */ 29 #define LSR_THRE 0x20 /* Transmit-hold-register empty */ 30 #define LSR_EMPTY (LSR_TEMT | LSR_THRE) 31 #define LSR_DR 0x01 /* DATA Ready */ 32 33 static vaddr_t chip_to_base(struct serial_chip *chip) 34 { 35 struct serial8250_uart_data *pd = 36 container_of(chip, struct serial8250_uart_data, chip); 37 38 return io_pa_or_va(&pd->base, SERIAL8250_UART_REG_SIZE); 39 } 40 41 static void serial8250_uart_flush(struct serial_chip *chip) 42 { 43 vaddr_t base = chip_to_base(chip); 44 45 while (1) { 46 uint32_t state = io_read32(base + UART_LSR); 47 48 /* Wait until transmit FIFO is empty */ 49 if ((state & LSR_EMPTY) == LSR_EMPTY) 50 break; 51 } 52 } 53 54 static bool serial8250_uart_have_rx_data(struct serial_chip *chip) 55 { 56 vaddr_t base = chip_to_base(chip); 57 58 return (io_read32(base + UART_LSR) & LSR_DR); 59 } 60 61 static int serial8250_uart_getchar(struct serial_chip *chip) 62 { 63 vaddr_t base = chip_to_base(chip); 64 65 while (!serial8250_uart_have_rx_data(chip)) { 66 /* Transmit FIFO is empty, waiting again */ 67 ; 68 } 69 return io_read32(base + UART_RHR) & 0xff; 70 } 71 72 static void serial8250_uart_putc(struct serial_chip *chip, int ch) 73 { 74 vaddr_t base = chip_to_base(chip); 75 76 serial8250_uart_flush(chip); 77 78 /* Write out character to transmit FIFO */ 79 io_write32(base + UART_THR, ch); 80 } 81 82 static const struct serial_ops serial8250_uart_ops = { 83 .flush = serial8250_uart_flush, 84 .getchar = serial8250_uart_getchar, 85 .have_rx_data = serial8250_uart_have_rx_data, 86 .putc = serial8250_uart_putc, 87 }; 88 DECLARE_KEEP_PAGER(serial8250_uart_ops); 89 90 void serial8250_uart_init(struct serial8250_uart_data *pd, paddr_t base, 91 uint32_t __unused uart_clk, 92 uint32_t __unused baud_rate) 93 94 { 95 pd->base.pa = base; 96 pd->chip.ops = &serial8250_uart_ops; 97 98 /* 99 * do nothing, debug uart(uart0) share with normal world, 100 * everything for uart0 is ready now. 101 */ 102 } 103 104 #ifdef CFG_DT 105 106 static struct serial_chip *serial8250_uart_dev_alloc(void) 107 { 108 struct serial8250_uart_data *pd = calloc(1, sizeof(*pd)); 109 110 if (!pd) 111 return NULL; 112 return &pd->chip; 113 } 114 115 static int serial8250_uart_dev_init(struct serial_chip *chip, 116 const void *fdt, 117 int offs, 118 const char *parms) 119 { 120 struct serial8250_uart_data *pd = 121 container_of(chip, struct serial8250_uart_data, chip); 122 vaddr_t vbase; 123 paddr_t pbase; 124 size_t size; 125 126 if (parms && parms[0]) 127 IMSG("serial8250_uart: device parameters ignored (%s)", parms); 128 129 if (dt_map_dev(fdt, offs, &vbase, &size, DT_MAP_AUTO) < 0) 130 return -1; 131 132 if (size < SERIAL8250_UART_REG_SIZE) { 133 EMSG("serial8250_uart: register size too small: %zx", size); 134 return -1; 135 } 136 137 pbase = virt_to_phys((void *)vbase); 138 serial8250_uart_init(pd, pbase, 0, 0); 139 140 return 0; 141 } 142 143 static void serial8250_uart_dev_free(struct serial_chip *chip) 144 { 145 struct serial8250_uart_data *pd = 146 container_of(chip, struct serial8250_uart_data, chip); 147 148 free(pd); 149 } 150 151 static const struct serial_driver serial8250_driver = { 152 .dev_alloc = serial8250_uart_dev_alloc, 153 .dev_init = serial8250_uart_dev_init, 154 .dev_free = serial8250_uart_dev_free, 155 }; 156 157 static const struct dt_device_match serial8250_match_table[] = { 158 { .compatible = "snps,dw-apb-uart" }, 159 { 0 } 160 }; 161 162 DEFINE_DT_DRIVER(serial8250_dt_driver) = { 163 .name = "serial8250_uart", 164 .type = DT_DRIVER_UART, 165 .match_table = serial8250_match_table, 166 .driver = &serial8250_driver, 167 }; 168 169 #endif /* CFG_DT */ 170