xref: /optee_os/core/drivers/serial8250_uart.c (revision 12fc37711783247b0d05fdc271ef007f4930767b)
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