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