xref: /optee_os/core/drivers/serial8250_uart.c (revision 9e3c57c88b0cdd41de57107725621c8c0857a838)
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>
11d276907cSYing-Chun Liu (PaulLiu) #include <kernel/dt.h>
12*9e3c57c8SEtienne Carriere #include <kernel/dt_driver.h>
13*9e3c57c8SEtienne Carriere #include <util.h>
1444bd24c5SJames Kung 
1544bd24c5SJames Kung /* uart register defines */
1644bd24c5SJames Kung #define UART_RHR	0x0
1744bd24c5SJames Kung #define UART_THR	0x0
1844bd24c5SJames Kung #define UART_IER	0x4
1944bd24c5SJames Kung #define UART_ISR	0x8
2044bd24c5SJames Kung #define UART_FCR	0x8
2144bd24c5SJames Kung #define UART_LCR	0xc
2244bd24c5SJames Kung #define UART_MCR	0x10
2344bd24c5SJames Kung #define UART_LSR	0x14
2444bd24c5SJames Kung #define UART_MSR	0x18
2544bd24c5SJames Kung #define UART_SPR	0x1c
2644bd24c5SJames Kung 
2744bd24c5SJames Kung /* uart status register bits */
2844bd24c5SJames Kung #define LSR_TEMT	0x40 /* Transmitter empty */
2944bd24c5SJames Kung #define LSR_THRE	0x20 /* Transmit-hold-register empty */
3044bd24c5SJames Kung #define LSR_EMPTY	(LSR_TEMT | LSR_THRE)
3144bd24c5SJames Kung #define LSR_DR		0x01 /* DATA Ready */
3244bd24c5SJames Kung 
chip_to_base(struct serial_chip * chip)33d66fa083SJerome Forissier static vaddr_t chip_to_base(struct serial_chip *chip)
3444bd24c5SJames Kung {
35d66fa083SJerome Forissier 	struct serial8250_uart_data *pd =
36d66fa083SJerome Forissier 		container_of(chip, struct serial8250_uart_data, chip);
37d66fa083SJerome Forissier 
38c2e4eb43SAnton Rybakov 	return io_pa_or_va(&pd->base, SERIAL8250_UART_REG_SIZE);
3944bd24c5SJames Kung }
4044bd24c5SJames Kung 
serial8250_uart_flush(struct serial_chip * chip)41d66fa083SJerome Forissier static void serial8250_uart_flush(struct serial_chip *chip)
4244bd24c5SJames Kung {
43d66fa083SJerome Forissier 	vaddr_t base = chip_to_base(chip);
44d66fa083SJerome Forissier 
4544bd24c5SJames Kung 	while (1) {
46918bb3a5SEtienne Carriere 		uint32_t state = io_read32(base + UART_LSR);
4744bd24c5SJames Kung 
48d66fa083SJerome Forissier 		/* Wait until transmit FIFO is empty */
4944bd24c5SJames Kung 		if ((state & LSR_EMPTY) == LSR_EMPTY)
5044bd24c5SJames Kung 			break;
5144bd24c5SJames Kung 	}
5244bd24c5SJames Kung }
5344bd24c5SJames Kung 
serial8250_uart_have_rx_data(struct serial_chip * chip)54d66fa083SJerome Forissier static bool serial8250_uart_have_rx_data(struct serial_chip *chip)
5544bd24c5SJames Kung {
56d66fa083SJerome Forissier 	vaddr_t base = chip_to_base(chip);
57d66fa083SJerome Forissier 
58918bb3a5SEtienne Carriere 	return (io_read32(base + UART_LSR) & LSR_DR);
5944bd24c5SJames Kung }
6044bd24c5SJames Kung 
serial8250_uart_getchar(struct serial_chip * chip)61d66fa083SJerome Forissier static int serial8250_uart_getchar(struct serial_chip *chip)
6244bd24c5SJames Kung {
63d66fa083SJerome Forissier 	vaddr_t base = chip_to_base(chip);
6444bd24c5SJames Kung 
65d66fa083SJerome Forissier 	while (!serial8250_uart_have_rx_data(chip)) {
66d66fa083SJerome Forissier 		/* Transmit FIFO is empty, waiting again */
6744bd24c5SJames Kung 		;
6844bd24c5SJames Kung 	}
69918bb3a5SEtienne Carriere 	return io_read32(base + UART_RHR) & 0xff;
7044bd24c5SJames Kung }
7144bd24c5SJames Kung 
serial8250_uart_putc(struct serial_chip * chip,int ch)72d66fa083SJerome Forissier static void serial8250_uart_putc(struct serial_chip *chip, int ch)
73d66fa083SJerome Forissier {
74d66fa083SJerome Forissier 	vaddr_t base = chip_to_base(chip);
75d66fa083SJerome Forissier 
76d66fa083SJerome Forissier 	serial8250_uart_flush(chip);
77d66fa083SJerome Forissier 
78d66fa083SJerome Forissier 	/* Write out character to transmit FIFO */
79918bb3a5SEtienne Carriere 	io_write32(base + UART_THR, ch);
80d66fa083SJerome Forissier }
81d66fa083SJerome Forissier 
82d66fa083SJerome Forissier static const struct serial_ops serial8250_uart_ops = {
83d66fa083SJerome Forissier 	.flush = serial8250_uart_flush,
84d66fa083SJerome Forissier 	.getchar = serial8250_uart_getchar,
85d66fa083SJerome Forissier 	.have_rx_data = serial8250_uart_have_rx_data,
86d66fa083SJerome Forissier 	.putc = serial8250_uart_putc,
87d66fa083SJerome Forissier };
883639b55fSJerome Forissier DECLARE_KEEP_PAGER(serial8250_uart_ops);
89d66fa083SJerome Forissier 
serial8250_uart_init(struct serial8250_uart_data * pd,paddr_t base,uint32_t __unused uart_clk,uint32_t __unused baud_rate)90d66fa083SJerome Forissier void serial8250_uart_init(struct serial8250_uart_data *pd, paddr_t base,
91d66fa083SJerome Forissier 			  uint32_t __unused uart_clk,
92d66fa083SJerome Forissier 			  uint32_t __unused baud_rate)
93d66fa083SJerome Forissier 
94d66fa083SJerome Forissier {
95d66fa083SJerome Forissier 	pd->base.pa = base;
96d66fa083SJerome Forissier 	pd->chip.ops = &serial8250_uart_ops;
97d66fa083SJerome Forissier 
98d66fa083SJerome Forissier 	/*
99d66fa083SJerome Forissier 	 * do nothing, debug uart(uart0) share with normal world,
100d66fa083SJerome Forissier 	 * everything for uart0 is ready now.
101d66fa083SJerome Forissier 	 */
102d66fa083SJerome Forissier }
103d276907cSYing-Chun Liu (PaulLiu) 
104d276907cSYing-Chun Liu (PaulLiu) #ifdef CFG_DT
105d276907cSYing-Chun Liu (PaulLiu) 
serial8250_uart_dev_alloc(void)106d276907cSYing-Chun Liu (PaulLiu) static struct serial_chip *serial8250_uart_dev_alloc(void)
107d276907cSYing-Chun Liu (PaulLiu) {
108630d13fbSJens Wiklander 	struct serial8250_uart_data *pd = calloc(1, sizeof(*pd));
109d276907cSYing-Chun Liu (PaulLiu) 
110d276907cSYing-Chun Liu (PaulLiu) 	if (!pd)
111d276907cSYing-Chun Liu (PaulLiu) 		return NULL;
112d276907cSYing-Chun Liu (PaulLiu) 	return &pd->chip;
113d276907cSYing-Chun Liu (PaulLiu) }
114d276907cSYing-Chun Liu (PaulLiu) 
serial8250_uart_dev_init(struct serial_chip * chip,const void * fdt,int offs,const char * parms)115d276907cSYing-Chun Liu (PaulLiu) static int serial8250_uart_dev_init(struct serial_chip *chip,
116d276907cSYing-Chun Liu (PaulLiu) 			       const void *fdt,
117d276907cSYing-Chun Liu (PaulLiu) 			       int offs,
118d276907cSYing-Chun Liu (PaulLiu) 			       const char *parms)
119d276907cSYing-Chun Liu (PaulLiu) {
120d276907cSYing-Chun Liu (PaulLiu) 	struct serial8250_uart_data *pd =
121d276907cSYing-Chun Liu (PaulLiu) 		container_of(chip, struct serial8250_uart_data, chip);
122d276907cSYing-Chun Liu (PaulLiu) 	vaddr_t vbase;
123d276907cSYing-Chun Liu (PaulLiu) 	paddr_t pbase;
124d276907cSYing-Chun Liu (PaulLiu) 	size_t size;
125d276907cSYing-Chun Liu (PaulLiu) 
126d276907cSYing-Chun Liu (PaulLiu) 	if (parms && parms[0])
127d276907cSYing-Chun Liu (PaulLiu) 		IMSG("serial8250_uart: device parameters ignored (%s)", parms);
128d276907cSYing-Chun Liu (PaulLiu) 
129a5d5bbc8SVesa Jääskeläinen 	if (dt_map_dev(fdt, offs, &vbase, &size, DT_MAP_AUTO) < 0)
130d276907cSYing-Chun Liu (PaulLiu) 		return -1;
131d276907cSYing-Chun Liu (PaulLiu) 
132d276907cSYing-Chun Liu (PaulLiu) 	if (size < SERIAL8250_UART_REG_SIZE) {
133d276907cSYing-Chun Liu (PaulLiu) 		EMSG("serial8250_uart: register size too small: %zx", size);
134d276907cSYing-Chun Liu (PaulLiu) 		return -1;
135d276907cSYing-Chun Liu (PaulLiu) 	}
136d276907cSYing-Chun Liu (PaulLiu) 
137d276907cSYing-Chun Liu (PaulLiu) 	pbase = virt_to_phys((void *)vbase);
138d276907cSYing-Chun Liu (PaulLiu) 	serial8250_uart_init(pd, pbase, 0, 0);
139d276907cSYing-Chun Liu (PaulLiu) 
140d276907cSYing-Chun Liu (PaulLiu) 	return 0;
141d276907cSYing-Chun Liu (PaulLiu) }
142d276907cSYing-Chun Liu (PaulLiu) 
serial8250_uart_dev_free(struct serial_chip * chip)143d276907cSYing-Chun Liu (PaulLiu) static void serial8250_uart_dev_free(struct serial_chip *chip)
144d276907cSYing-Chun Liu (PaulLiu) {
145d276907cSYing-Chun Liu (PaulLiu) 	struct serial8250_uart_data *pd =
146d276907cSYing-Chun Liu (PaulLiu) 	  container_of(chip,  struct serial8250_uart_data, chip);
147d276907cSYing-Chun Liu (PaulLiu) 
148d276907cSYing-Chun Liu (PaulLiu) 	free(pd);
149d276907cSYing-Chun Liu (PaulLiu) }
150d276907cSYing-Chun Liu (PaulLiu) 
151d276907cSYing-Chun Liu (PaulLiu) static const struct serial_driver serial8250_driver = {
152d276907cSYing-Chun Liu (PaulLiu) 	.dev_alloc = serial8250_uart_dev_alloc,
153d276907cSYing-Chun Liu (PaulLiu) 	.dev_init = serial8250_uart_dev_init,
154d276907cSYing-Chun Liu (PaulLiu) 	.dev_free = serial8250_uart_dev_free,
155d276907cSYing-Chun Liu (PaulLiu) };
156d276907cSYing-Chun Liu (PaulLiu) 
157d276907cSYing-Chun Liu (PaulLiu) static const struct dt_device_match serial8250_match_table[] = {
158d276907cSYing-Chun Liu (PaulLiu) 	{ .compatible = "snps,dw-apb-uart" },
159d276907cSYing-Chun Liu (PaulLiu) 	{ 0 }
160d276907cSYing-Chun Liu (PaulLiu) };
161d276907cSYing-Chun Liu (PaulLiu) 
16261bdedeaSJerome Forissier DEFINE_DT_DRIVER(serial8250_dt_driver) = {
163d276907cSYing-Chun Liu (PaulLiu) 	.name = "serial8250_uart",
1645e588771SClément Léger 	.type = DT_DRIVER_UART,
165d276907cSYing-Chun Liu (PaulLiu) 	.match_table = serial8250_match_table,
166d276907cSYing-Chun Liu (PaulLiu) 	.driver = &serial8250_driver,
167d276907cSYing-Chun Liu (PaulLiu) };
168d276907cSYing-Chun Liu (PaulLiu) 
169d276907cSYing-Chun Liu (PaulLiu) #endif /* CFG_DT */
170