xref: /optee_os/core/drivers/serial8250_uart.c (revision d276907c8c4f85c3227a28b81f34b74c324806c7)
11bb92983SJerome Forissier // SPDX-License-Identifier: BSD-2-Clause
244bd24c5SJames Kung /*
344bd24c5SJames Kung  * Copyright (c) 2015, Linaro Limited
444bd24c5SJames Kung  */
544bd24c5SJames Kung 
6d66fa083SJerome Forissier #include <compiler.h>
744bd24c5SJames Kung #include <console.h>
88d94060aSEtienne Carriere #include <drivers/serial8250_uart.h>
944bd24c5SJames Kung #include <io.h>
108d94060aSEtienne Carriere #include <keep.h>
11d66fa083SJerome Forissier #include <util.h>
12*d276907cSYing-Chun Liu (PaulLiu) #include <kernel/dt.h>
1344bd24c5SJames Kung 
1444bd24c5SJames Kung /* uart register defines */
1544bd24c5SJames Kung #define UART_RHR	0x0
1644bd24c5SJames Kung #define UART_THR	0x0
1744bd24c5SJames Kung #define UART_IER	0x4
1844bd24c5SJames Kung #define UART_ISR	0x8
1944bd24c5SJames Kung #define UART_FCR	0x8
2044bd24c5SJames Kung #define UART_LCR	0xc
2144bd24c5SJames Kung #define UART_MCR	0x10
2244bd24c5SJames Kung #define UART_LSR	0x14
2344bd24c5SJames Kung #define UART_MSR	0x18
2444bd24c5SJames Kung #define UART_SPR	0x1c
2544bd24c5SJames Kung 
2644bd24c5SJames Kung /* uart status register bits */
2744bd24c5SJames Kung #define LSR_TEMT	0x40 /* Transmitter empty */
2844bd24c5SJames Kung #define LSR_THRE	0x20 /* Transmit-hold-register empty */
2944bd24c5SJames Kung #define LSR_EMPTY	(LSR_TEMT | LSR_THRE)
3044bd24c5SJames Kung #define LSR_DR		0x01 /* DATA Ready */
3144bd24c5SJames Kung 
32d66fa083SJerome Forissier static vaddr_t chip_to_base(struct serial_chip *chip)
3344bd24c5SJames Kung {
34d66fa083SJerome Forissier 	struct serial8250_uart_data *pd =
35d66fa083SJerome Forissier 		container_of(chip, struct serial8250_uart_data, chip);
36d66fa083SJerome Forissier 
37d66fa083SJerome Forissier 	return io_pa_or_va(&pd->base);
3844bd24c5SJames Kung }
3944bd24c5SJames Kung 
40d66fa083SJerome Forissier static void serial8250_uart_flush(struct serial_chip *chip)
4144bd24c5SJames Kung {
42d66fa083SJerome Forissier 	vaddr_t base = chip_to_base(chip);
43d66fa083SJerome Forissier 
4444bd24c5SJames Kung 	while (1) {
4565d34b1fSJoseph Chen 		uint32_t state = read32(base + UART_LSR);
4644bd24c5SJames Kung 
47d66fa083SJerome Forissier 		/* Wait until transmit FIFO is empty */
4844bd24c5SJames Kung 		if ((state & LSR_EMPTY) == LSR_EMPTY)
4944bd24c5SJames Kung 			break;
5044bd24c5SJames Kung 	}
5144bd24c5SJames Kung }
5244bd24c5SJames Kung 
53d66fa083SJerome Forissier static bool serial8250_uart_have_rx_data(struct serial_chip *chip)
5444bd24c5SJames Kung {
55d66fa083SJerome Forissier 	vaddr_t base = chip_to_base(chip);
56d66fa083SJerome Forissier 
5744bd24c5SJames Kung 	return (read32(base + UART_LSR) & LSR_DR);
5844bd24c5SJames Kung }
5944bd24c5SJames Kung 
60d66fa083SJerome Forissier static int serial8250_uart_getchar(struct serial_chip *chip)
6144bd24c5SJames Kung {
62d66fa083SJerome Forissier 	vaddr_t base = chip_to_base(chip);
6344bd24c5SJames Kung 
64d66fa083SJerome Forissier 	while (!serial8250_uart_have_rx_data(chip)) {
65d66fa083SJerome Forissier 		/* Transmit FIFO is empty, waiting again */
6644bd24c5SJames Kung 		;
6744bd24c5SJames Kung 	}
6865d34b1fSJoseph Chen 	return read32(base + UART_RHR) & 0xff;
6944bd24c5SJames Kung }
7044bd24c5SJames Kung 
71d66fa083SJerome Forissier static void serial8250_uart_putc(struct serial_chip *chip, int ch)
72d66fa083SJerome Forissier {
73d66fa083SJerome Forissier 	vaddr_t base = chip_to_base(chip);
74d66fa083SJerome Forissier 
75d66fa083SJerome Forissier 	serial8250_uart_flush(chip);
76d66fa083SJerome Forissier 
77d66fa083SJerome Forissier 	/* Write out character to transmit FIFO */
7865d34b1fSJoseph Chen 	write32(ch, base + UART_THR);
79d66fa083SJerome Forissier }
80d66fa083SJerome Forissier 
81d66fa083SJerome Forissier static const struct serial_ops serial8250_uart_ops = {
82d66fa083SJerome Forissier 	.flush = serial8250_uart_flush,
83d66fa083SJerome Forissier 	.getchar = serial8250_uart_getchar,
84d66fa083SJerome Forissier 	.have_rx_data = serial8250_uart_have_rx_data,
85d66fa083SJerome Forissier 	.putc = serial8250_uart_putc,
86d66fa083SJerome Forissier };
878d94060aSEtienne Carriere KEEP_PAGER(serial8250_uart_ops);
88d66fa083SJerome Forissier 
89d66fa083SJerome Forissier void serial8250_uart_init(struct serial8250_uart_data *pd, paddr_t base,
90d66fa083SJerome Forissier 			  uint32_t __unused uart_clk,
91d66fa083SJerome Forissier 			  uint32_t __unused baud_rate)
92d66fa083SJerome Forissier 
93d66fa083SJerome Forissier {
94d66fa083SJerome Forissier 	pd->base.pa = base;
95d66fa083SJerome Forissier 	pd->chip.ops = &serial8250_uart_ops;
96d66fa083SJerome Forissier 
97d66fa083SJerome Forissier 	/*
98d66fa083SJerome Forissier 	 * do nothing, debug uart(uart0) share with normal world,
99d66fa083SJerome Forissier 	 * everything for uart0 is ready now.
100d66fa083SJerome Forissier 	 */
101d66fa083SJerome Forissier }
102*d276907cSYing-Chun Liu (PaulLiu) 
103*d276907cSYing-Chun Liu (PaulLiu) #ifdef CFG_DT
104*d276907cSYing-Chun Liu (PaulLiu) 
105*d276907cSYing-Chun Liu (PaulLiu) static struct serial_chip *serial8250_uart_dev_alloc(void)
106*d276907cSYing-Chun Liu (PaulLiu) {
107*d276907cSYing-Chun Liu (PaulLiu) 	struct serial8250_uart_data *pd = malloc(sizeof(*pd));
108*d276907cSYing-Chun Liu (PaulLiu) 
109*d276907cSYing-Chun Liu (PaulLiu) 	if (!pd)
110*d276907cSYing-Chun Liu (PaulLiu) 		return NULL;
111*d276907cSYing-Chun Liu (PaulLiu) 	return &pd->chip;
112*d276907cSYing-Chun Liu (PaulLiu) }
113*d276907cSYing-Chun Liu (PaulLiu) 
114*d276907cSYing-Chun Liu (PaulLiu) static int serial8250_uart_dev_init(struct serial_chip *chip,
115*d276907cSYing-Chun Liu (PaulLiu) 			       const void *fdt,
116*d276907cSYing-Chun Liu (PaulLiu) 			       int offs,
117*d276907cSYing-Chun Liu (PaulLiu) 			       const char *parms)
118*d276907cSYing-Chun Liu (PaulLiu) {
119*d276907cSYing-Chun Liu (PaulLiu) 	struct serial8250_uart_data *pd =
120*d276907cSYing-Chun Liu (PaulLiu) 		container_of(chip, struct serial8250_uart_data, chip);
121*d276907cSYing-Chun Liu (PaulLiu) 	vaddr_t vbase;
122*d276907cSYing-Chun Liu (PaulLiu) 	paddr_t pbase;
123*d276907cSYing-Chun Liu (PaulLiu) 	size_t size;
124*d276907cSYing-Chun Liu (PaulLiu) 
125*d276907cSYing-Chun Liu (PaulLiu) 	if (parms && parms[0])
126*d276907cSYing-Chun Liu (PaulLiu) 		IMSG("serial8250_uart: device parameters ignored (%s)", parms);
127*d276907cSYing-Chun Liu (PaulLiu) 
128*d276907cSYing-Chun Liu (PaulLiu) 	if (dt_map_dev(fdt, offs, &vbase, &size) < 0)
129*d276907cSYing-Chun Liu (PaulLiu) 		return -1;
130*d276907cSYing-Chun Liu (PaulLiu) 
131*d276907cSYing-Chun Liu (PaulLiu) 	if (size < SERIAL8250_UART_REG_SIZE) {
132*d276907cSYing-Chun Liu (PaulLiu) 		EMSG("serial8250_uart: register size too small: %zx", size);
133*d276907cSYing-Chun Liu (PaulLiu) 		return -1;
134*d276907cSYing-Chun Liu (PaulLiu) 	}
135*d276907cSYing-Chun Liu (PaulLiu) 
136*d276907cSYing-Chun Liu (PaulLiu) 	pbase = virt_to_phys((void *)vbase);
137*d276907cSYing-Chun Liu (PaulLiu) 	serial8250_uart_init(pd, pbase, 0, 0);
138*d276907cSYing-Chun Liu (PaulLiu) 
139*d276907cSYing-Chun Liu (PaulLiu) 	return 0;
140*d276907cSYing-Chun Liu (PaulLiu) }
141*d276907cSYing-Chun Liu (PaulLiu) 
142*d276907cSYing-Chun Liu (PaulLiu) static void serial8250_uart_dev_free(struct serial_chip *chip)
143*d276907cSYing-Chun Liu (PaulLiu) {
144*d276907cSYing-Chun Liu (PaulLiu) 	struct serial8250_uart_data *pd =
145*d276907cSYing-Chun Liu (PaulLiu) 	  container_of(chip,  struct serial8250_uart_data, chip);
146*d276907cSYing-Chun Liu (PaulLiu) 
147*d276907cSYing-Chun Liu (PaulLiu) 	free(pd);
148*d276907cSYing-Chun Liu (PaulLiu) }
149*d276907cSYing-Chun Liu (PaulLiu) 
150*d276907cSYing-Chun Liu (PaulLiu) static const struct serial_driver serial8250_driver = {
151*d276907cSYing-Chun Liu (PaulLiu) 	.dev_alloc = serial8250_uart_dev_alloc,
152*d276907cSYing-Chun Liu (PaulLiu) 	.dev_init = serial8250_uart_dev_init,
153*d276907cSYing-Chun Liu (PaulLiu) 	.dev_free = serial8250_uart_dev_free,
154*d276907cSYing-Chun Liu (PaulLiu) };
155*d276907cSYing-Chun Liu (PaulLiu) 
156*d276907cSYing-Chun Liu (PaulLiu) static const struct dt_device_match serial8250_match_table[] = {
157*d276907cSYing-Chun Liu (PaulLiu) 	{ .compatible = "snps,dw-apb-uart" },
158*d276907cSYing-Chun Liu (PaulLiu) 	{ 0 }
159*d276907cSYing-Chun Liu (PaulLiu) };
160*d276907cSYing-Chun Liu (PaulLiu) 
161*d276907cSYing-Chun Liu (PaulLiu) const struct dt_driver serial8250_dt_driver __dt_driver = {
162*d276907cSYing-Chun Liu (PaulLiu) 	.name = "serial8250_uart",
163*d276907cSYing-Chun Liu (PaulLiu) 	.match_table = serial8250_match_table,
164*d276907cSYing-Chun Liu (PaulLiu) 	.driver = &serial8250_driver,
165*d276907cSYing-Chun Liu (PaulLiu) };
166*d276907cSYing-Chun Liu (PaulLiu) 
167*d276907cSYing-Chun Liu (PaulLiu) #endif /* CFG_DT */
168