xref: /optee_os/core/drivers/serial8250_uart.c (revision a5d5bbc82de95f531512fafa76a42c879e81b4c4)
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>
12d276907cSYing-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 
37c2e4eb43SAnton Rybakov 	return io_pa_or_va(&pd->base, SERIAL8250_UART_REG_SIZE);
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) {
45918bb3a5SEtienne Carriere 		uint32_t state = io_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 
57918bb3a5SEtienne Carriere 	return (io_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 	}
68918bb3a5SEtienne Carriere 	return io_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 */
78918bb3a5SEtienne Carriere 	io_write32(base + UART_THR, ch);
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 };
873639b55fSJerome Forissier DECLARE_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 }
102d276907cSYing-Chun Liu (PaulLiu) 
103d276907cSYing-Chun Liu (PaulLiu) #ifdef CFG_DT
104d276907cSYing-Chun Liu (PaulLiu) 
105d276907cSYing-Chun Liu (PaulLiu) static struct serial_chip *serial8250_uart_dev_alloc(void)
106d276907cSYing-Chun Liu (PaulLiu) {
107630d13fbSJens Wiklander 	struct serial8250_uart_data *pd = calloc(1, sizeof(*pd));
108d276907cSYing-Chun Liu (PaulLiu) 
109d276907cSYing-Chun Liu (PaulLiu) 	if (!pd)
110d276907cSYing-Chun Liu (PaulLiu) 		return NULL;
111d276907cSYing-Chun Liu (PaulLiu) 	return &pd->chip;
112d276907cSYing-Chun Liu (PaulLiu) }
113d276907cSYing-Chun Liu (PaulLiu) 
114d276907cSYing-Chun Liu (PaulLiu) static int serial8250_uart_dev_init(struct serial_chip *chip,
115d276907cSYing-Chun Liu (PaulLiu) 			       const void *fdt,
116d276907cSYing-Chun Liu (PaulLiu) 			       int offs,
117d276907cSYing-Chun Liu (PaulLiu) 			       const char *parms)
118d276907cSYing-Chun Liu (PaulLiu) {
119d276907cSYing-Chun Liu (PaulLiu) 	struct serial8250_uart_data *pd =
120d276907cSYing-Chun Liu (PaulLiu) 		container_of(chip, struct serial8250_uart_data, chip);
121d276907cSYing-Chun Liu (PaulLiu) 	vaddr_t vbase;
122d276907cSYing-Chun Liu (PaulLiu) 	paddr_t pbase;
123d276907cSYing-Chun Liu (PaulLiu) 	size_t size;
124d276907cSYing-Chun Liu (PaulLiu) 
125d276907cSYing-Chun Liu (PaulLiu) 	if (parms && parms[0])
126d276907cSYing-Chun Liu (PaulLiu) 		IMSG("serial8250_uart: device parameters ignored (%s)", parms);
127d276907cSYing-Chun Liu (PaulLiu) 
128*a5d5bbc8SVesa Jääskeläinen 	if (dt_map_dev(fdt, offs, &vbase, &size, DT_MAP_AUTO) < 0)
129d276907cSYing-Chun Liu (PaulLiu) 		return -1;
130d276907cSYing-Chun Liu (PaulLiu) 
131d276907cSYing-Chun Liu (PaulLiu) 	if (size < SERIAL8250_UART_REG_SIZE) {
132d276907cSYing-Chun Liu (PaulLiu) 		EMSG("serial8250_uart: register size too small: %zx", size);
133d276907cSYing-Chun Liu (PaulLiu) 		return -1;
134d276907cSYing-Chun Liu (PaulLiu) 	}
135d276907cSYing-Chun Liu (PaulLiu) 
136d276907cSYing-Chun Liu (PaulLiu) 	pbase = virt_to_phys((void *)vbase);
137d276907cSYing-Chun Liu (PaulLiu) 	serial8250_uart_init(pd, pbase, 0, 0);
138d276907cSYing-Chun Liu (PaulLiu) 
139d276907cSYing-Chun Liu (PaulLiu) 	return 0;
140d276907cSYing-Chun Liu (PaulLiu) }
141d276907cSYing-Chun Liu (PaulLiu) 
142d276907cSYing-Chun Liu (PaulLiu) static void serial8250_uart_dev_free(struct serial_chip *chip)
143d276907cSYing-Chun Liu (PaulLiu) {
144d276907cSYing-Chun Liu (PaulLiu) 	struct serial8250_uart_data *pd =
145d276907cSYing-Chun Liu (PaulLiu) 	  container_of(chip,  struct serial8250_uart_data, chip);
146d276907cSYing-Chun Liu (PaulLiu) 
147d276907cSYing-Chun Liu (PaulLiu) 	free(pd);
148d276907cSYing-Chun Liu (PaulLiu) }
149d276907cSYing-Chun Liu (PaulLiu) 
150d276907cSYing-Chun Liu (PaulLiu) static const struct serial_driver serial8250_driver = {
151d276907cSYing-Chun Liu (PaulLiu) 	.dev_alloc = serial8250_uart_dev_alloc,
152d276907cSYing-Chun Liu (PaulLiu) 	.dev_init = serial8250_uart_dev_init,
153d276907cSYing-Chun Liu (PaulLiu) 	.dev_free = serial8250_uart_dev_free,
154d276907cSYing-Chun Liu (PaulLiu) };
155d276907cSYing-Chun Liu (PaulLiu) 
156d276907cSYing-Chun Liu (PaulLiu) static const struct dt_device_match serial8250_match_table[] = {
157d276907cSYing-Chun Liu (PaulLiu) 	{ .compatible = "snps,dw-apb-uart" },
158d276907cSYing-Chun Liu (PaulLiu) 	{ 0 }
159d276907cSYing-Chun Liu (PaulLiu) };
160d276907cSYing-Chun Liu (PaulLiu) 
16161bdedeaSJerome Forissier DEFINE_DT_DRIVER(serial8250_dt_driver) = {
162d276907cSYing-Chun Liu (PaulLiu) 	.name = "serial8250_uart",
1635e588771SClément Léger 	.type = DT_DRIVER_UART,
164d276907cSYing-Chun Liu (PaulLiu) 	.match_table = serial8250_match_table,
165d276907cSYing-Chun Liu (PaulLiu) 	.driver = &serial8250_driver,
166d276907cSYing-Chun Liu (PaulLiu) };
167d276907cSYing-Chun Liu (PaulLiu) 
168d276907cSYing-Chun Liu (PaulLiu) #endif /* CFG_DT */
169