xref: /optee_os/core/drivers/mvebu_uart.c (revision b1469ba0bfd0371eb52bd50f5c52eeda7a8f5f1e)
1 /*
2  * Copyright (C) 2017 Marvell International Ltd.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  * this list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  * this list of conditions and the following disclaimer in the documentation
13  * and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
19  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25  * POSSIBILITY OF SUCH DAMAGE.
26  */
27 #include <assert.h>
28 #include <drivers/mvebu_uart.h>
29 #include <io.h>
30 #include <keep.h>
31 #include <kernel/dt.h>
32 #include <stdlib.h>
33 #include <trace.h>
34 #include <types_ext.h>
35 #include <util.h>
36 
37 /* MVEBU UART Registers */
38 #define UART_RX_REG		0x00
39 #define UART_TX_REG		0x04
40 #define UART_CTRL_REG		0x08
41 #define UART_STATUS_REG		0x0c
42 #define UART_BAUD_REG		0x10
43 #define UART_POSSR_REG		0x14
44 
45 /* Line Status Register bits */
46 #define UARTLSR_TXFIFOFULL	(1 << 11)       /* Tx Fifo Full */
47 #define UARTLSR_TXFIFOEMPTY	(1 << 13)
48 #define UARTLSR_TXEMPTY		(1 << 6)
49 #define UART_RX_READY		(1 << 4)
50 
51 /* UART Control Register bits */
52 #define UART_CTRL_RXFIFO_RESET	(1 << 14)
53 #define UART_CTRL_TXFIFO_RESET	(1 << 15)
54 
55 static vaddr_t chip_to_base(struct serial_chip *chip)
56 {
57 	struct mvebu_uart_data *pd =
58 		container_of(chip, struct mvebu_uart_data, chip);
59 
60 	return io_pa_or_va(&pd->base);
61 }
62 
63 static void mvebu_uart_flush(struct serial_chip *chip)
64 {
65 	vaddr_t base = chip_to_base(chip);
66 
67 	/*
68 	 * Wait for the transmit FIFO to be empty.
69 	 * It can happen that Linux initializes the OP-TEE driver with the
70 	 * console UART disabled; avoid an infinite loop by checking the UART
71 	 * enabled flag. Checking it in the loop makes the code safe against
72 	 * asynchronous disable.
73 	 */
74 	while (!(read32(base + UART_STATUS_REG) & UARTLSR_TXFIFOEMPTY))
75 		;
76 }
77 
78 static bool mvebu_uart_have_rx_data(struct serial_chip *chip)
79 {
80 	vaddr_t base = chip_to_base(chip);
81 
82 	return (read32(base + UART_STATUS_REG) & UART_RX_READY);
83 }
84 
85 static int mvebu_uart_getchar(struct serial_chip *chip)
86 {
87 	vaddr_t base = chip_to_base(chip);
88 
89 	while (!mvebu_uart_have_rx_data(chip))
90 		;
91 	return read32(base + UART_RX_REG) & 0xff;
92 }
93 
94 static void mvebu_uart_putc(struct serial_chip *chip, int ch)
95 {
96 	vaddr_t base = chip_to_base(chip);
97 
98 	uint32_t tmp;
99 	/* wait for space in tx FIFO */
100 	do {
101 		tmp = read32(base + UART_STATUS_REG);
102 		tmp &= UARTLSR_TXFIFOFULL;
103 	} while (tmp == UARTLSR_TXFIFOFULL);
104 
105 	write32(ch, base + UART_TX_REG);
106 }
107 
108 static const struct serial_ops mvebu_uart_ops = {
109 	.flush = mvebu_uart_flush,
110 	.getchar = mvebu_uart_getchar,
111 	.have_rx_data = mvebu_uart_have_rx_data,
112 	.putc = mvebu_uart_putc,
113 };
114 KEEP_PAGER(mvebu_uart_ops);
115 
116 void mvebu_uart_init(struct mvebu_uart_data *pd, paddr_t pbase,
117 		uint32_t uart_clk, uint32_t baud_rate)
118 {
119 	vaddr_t base;
120 	uint32_t tmp = 0;
121 	uint32_t dll = 0;
122 
123 	pd->base.pa = pbase;
124 	pd->chip.ops = &mvebu_uart_ops;
125 
126 	base = io_pa_or_va(&pd->base);
127 
128 	dll = (uart_clk / (baud_rate << 4)) & 0x3FF;
129 
130 	/* init UART  */
131 	tmp = read32(base + UART_BAUD_REG);
132 	tmp &= ~0x3FF;
133 	tmp |= dll;
134 	write32(tmp, base + UART_BAUD_REG);   /* set baud rate divisor */
135 
136 	/* set UART to default 16x scheme */
137 	write32(0, base + UART_POSSR_REG);
138 
139 	/* reset FIFO */
140 	write32((UART_CTRL_RXFIFO_RESET | UART_CTRL_TXFIFO_RESET),
141 		base + UART_CTRL_REG);
142 
143 	/* No Parity, 1 stop */
144 	write32(0, base + UART_CTRL_REG);
145 
146 	mvebu_uart_flush(&pd->chip);
147 }
148