xref: /aosp_15_r20/external/coreboot/src/soc/cavium/cn81xx/uart.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 /*
4  * Derived from Cavium's BSD-3 Clause OCTEONTX-SDK-6.2.0.
5  */
6 
7 #include <device/mmio.h>
8 #include <console/uart.h>
9 #include <delay.h>
10 #include <endian.h>
11 #include <stdint.h>
12 #include <soc/clock.h>
13 #include <soc/uart.h>
14 #include <assert.h>
15 #include <soc/addressmap.h>
16 #include <drivers/uart/pl011.h>
17 
18 union cn81xx_uart_ctl {
19 	u64 u;
20 	struct {
21 		u64 uctl_rst		: 1;
22 		u64 uaa_rst		: 1;
23 		u64			: 2;
24 		u64 csclk_en		: 1;
25 		u64			: 19;
26 		u64 h_clkdiv_sel	: 3;
27 		u64			: 1;
28 		u64 h_clkdiv_rst	: 1;
29 		u64 h_clk_byp_sel	: 1;
30 		u64 h_clk_en		: 1;
31 		u64			: 33;
32 	} s;
33 };
34 
35 struct cn81xx_uart {
36 	struct pl011_uart pl011;
37 	union cn81xx_uart_ctl uctl_ctl;
38 	u8 rsvd4[0x8];
39 	u64 uctl_spare0;
40 	u8 rsvd5[0xe0];
41 	u64 uctl_spare1;
42 };
43 
44 #define UART_IBRD_BAUD_DIVINT_SHIFT		0
45 #define UART_IBRD_BAUD_DIVINT_MASK		0xffff
46 
47 #define UART_FBRD_BAUD_DIVFRAC_SHIFT		0
48 #define UART_FBRD_BAUD_DIVFRAC_MASK		0x3f
49 
50 check_member(cn81xx_uart, uctl_ctl, 0x1000);
51 check_member(cn81xx_uart, uctl_spare1, 0x10f8);
52 
53 #define UART_SCLK_DIV 3
54 
55 /**
56  * Returns the current UART HCLK divider
57  *
58  * @param reg      The H_CLKDIV_SEL value
59  * @return         The HCLK divider
60  */
uart_sclk_divisor(const size_t reg)61 static size_t uart_sclk_divisor(const size_t reg)
62 {
63 	static const u8 div[] = {1, 2, 4, 6, 8, 16, 24, 32};
64 
65 	assert(reg < ARRAY_SIZE(div));
66 
67 	return div[reg];
68 }
69 
70 /**
71  * Returns the current UART HCLK
72  *
73  * @param uart     The UART to operate on
74  * @return         The HCLK in Hz
75  */
uart_hclk(struct cn81xx_uart * uart)76 static size_t uart_hclk(struct cn81xx_uart *uart)
77 {
78 	union cn81xx_uart_ctl ctl;
79 	const uint64_t sclk = thunderx_get_io_clock();
80 
81 	ctl.u = read64(&uart->uctl_ctl);
82 	return sclk / uart_sclk_divisor(ctl.s.h_clkdiv_sel);
83 }
84 
uart_platform_refclk(void)85 unsigned int uart_platform_refclk(void)
86 {
87 	struct cn81xx_uart *uart =
88 	    (struct cn81xx_uart *)CONFIG_CONSOLE_SERIAL_UART_ADDRESS;
89 
90 	if (!uart)
91 		return 0;
92 
93 	return uart_hclk(uart);
94 }
95 
uart_platform_base(unsigned int idx)96 uintptr_t uart_platform_base(unsigned int idx)
97 {
98 	return CONFIG_CONSOLE_SERIAL_UART_ADDRESS;
99 }
100 
101 /**
102  * Waits given count if HCLK cycles
103  *
104  * @param uart     The UART to operate on
105  * @param hclks    The number of HCLK cycles to wait
106  */
uart_wait_hclk(struct cn81xx_uart * uart,const size_t hclks)107 static void uart_wait_hclk(struct cn81xx_uart *uart, const size_t hclks)
108 {
109 	const size_t hclk = uart_hclk(uart);
110 	const size_t delay = (hclks * 1000000ULL) / hclk;
111 	udelay(MAX(delay, 1));
112 }
113 
114 /**
115  * Returns the UART state.
116  *
117  * @param bus     The UART to operate on
118  * @return        Boolean: True if UART is enabled
119  */
uart_is_enabled(const size_t bus)120 int uart_is_enabled(const size_t bus)
121 {
122 	struct cn81xx_uart *uart = (struct cn81xx_uart *)UAAx_PF_BAR0(bus);
123 	union cn81xx_uart_ctl ctl;
124 
125 	assert(uart);
126 	if (!uart)
127 		return 0;
128 
129 	ctl.u = read64(&uart->uctl_ctl);
130 	return !!ctl.s.csclk_en;
131 }
132 
133 /**
134  * Setup UART with desired BAUD rate in 8N1, no parity mode.
135  *
136  * @param bus          The UART to operate on
137  * @param baudrate     baudrate to set up
138  *
139  * @return             Boolean: True on error
140  */
uart_setup(const size_t bus,int baudrate)141 int uart_setup(const size_t bus, int baudrate)
142 {
143 	union cn81xx_uart_ctl ctl;
144 	struct cn81xx_uart *uart = (struct cn81xx_uart *)UAAx_PF_BAR0(bus);
145 
146 	assert(uart);
147 	if (!uart)
148 		return 1;
149 
150 	/* 1.2.1 Initialization Sequence (Power-On/Hard/Cold Reset) */
151 	/* 1. Wait for IOI reset (srst_n) to deassert. */
152 
153 	/**
154 	 * 2. Assert all resets:
155 	 * a. UAA reset: UCTL_CTL[UAA_RST] = 1
156 	 * b. UCTL reset: UCTL_CTL[UCTL_RST] = 1
157 	 */
158 	ctl.u = read64(&uart->uctl_ctl);
159 	ctl.s.uctl_rst = 1;
160 	ctl.s.uaa_rst = 1;
161 	write64(&uart->uctl_ctl, ctl.u);
162 
163 	/**
164 	 * 3. Configure the HCLK:
165 	 * a. Reset the clock dividers: UCTL_CTL[H_CLKDIV_RST] = 1.
166 	 * b. Select the HCLK frequency
167 	 * i. UCTL_CTL[H_CLKDIV] = desired value,
168 	 * ii. UCTL_CTL[H_CLKDIV_EN] = 1 to enable the HCLK.
169 	 * iii. Readback UCTL_CTL to ensure the values take effect.
170 	 * c. Deassert the HCLK clock divider reset: UCTL_CTL[H_CLKDIV_RST] = 0.
171 	 */
172 	ctl.u = read64(&uart->uctl_ctl);
173 	ctl.s.h_clkdiv_sel = UART_SCLK_DIV;
174 	write64(&uart->uctl_ctl, ctl.u);
175 
176 	ctl.u = read64(&uart->uctl_ctl);
177 	ctl.s.h_clk_byp_sel = 0;
178 	write64(&uart->uctl_ctl, ctl.u);
179 
180 	ctl.u = read64(&uart->uctl_ctl);
181 	ctl.s.h_clk_en = 1;
182 	write64(&uart->uctl_ctl, ctl.u);
183 
184 	ctl.u = read64(&uart->uctl_ctl);
185 	ctl.s.h_clkdiv_rst = 0;
186 	write64(&uart->uctl_ctl, ctl.u);
187 
188 	/**
189 	 * 4. Wait 20 HCLK cycles from step 3 for HCLK to start and async fifo
190 	 * to properly reset.
191 	 */
192 	uart_wait_hclk(uart, 20 + 1);
193 
194 	/**
195 	 * 5. Deassert UCTL and UAHC resets:
196 	 *  a. UCTL_CTL[UCTL_RST] = 0
197 	 * b. Wait 10 HCLK cycles.
198 	 * c. UCTL_CTL[UAHC_RST] = 0
199 	 * d. You will have to wait 10 HCLK cycles before accessing any
200 	 * HCLK-only registers.
201 	 */
202 	ctl.u = read64(&uart->uctl_ctl);
203 	ctl.s.uctl_rst = 0;
204 	write64(&uart->uctl_ctl, ctl.u);
205 
206 	uart_wait_hclk(uart, 10 + 1);
207 
208 	ctl.u = read64(&uart->uctl_ctl);
209 	ctl.s.uaa_rst = 0;
210 	write64(&uart->uctl_ctl, ctl.u);
211 
212 	uart_wait_hclk(uart, 10 + 1);
213 
214 	/**
215 	 * 6. Enable conditional SCLK of UCTL by writing
216 	 * UCTL_CTL[CSCLK_EN] = 1.
217 	 */
218 	ctl.u = read64(&uart->uctl_ctl);
219 	ctl.s.csclk_en = 1;
220 	write64(&uart->uctl_ctl, ctl.u);
221 
222 	/**
223 	 * Exit here if the UART is not going to be used in coreboot.
224 	 * The previous initialization steps are sufficient to make the Linux
225 	 * kernel not panic.
226 	 */
227 	if (!baudrate)
228 		return 0;
229 
230 	/**
231 	 * 7. Initialize the integer and fractional baud rate divider registers
232 	 * UARTIBRD and UARTFBRD as follows:
233 	 * a. Baud Rate Divisor = UARTCLK/(16xBaud Rate) = BRDI + BRDF
234 	 * b. The fractional register BRDF, m is calculated as
235 	 * integer(BRDF x 64 + 0.5)
236 	 * Example calculation:
237 	 * If the required baud rate is 230400 and hclk = 4MHz then:
238 	 * Baud Rate Divisor = (4x10^6)/(16x230400) = 1.085
239 	 * This means BRDI = 1 and BRDF = 0.085.
240 	 * Therefore, fractional part, BRDF = integer((0.085x64)+0.5) = 5
241 	 * Generated baud rate divider = 1+5/64 = 1.078
242 	 */
243 	u64 divisor = thunderx_get_io_clock() /
244 		(baudrate * 16 * uart_sclk_divisor(UART_SCLK_DIV) / 64);
245 	write32(&uart->pl011.ibrd, divisor >> 6);
246 	write32(&uart->pl011.fbrd, divisor & UART_FBRD_BAUD_DIVFRAC_MASK);
247 
248 	/**
249 	 * 8. Program the line control register UAA(0..1)_LCR_H and the control
250 	 * register UAA(0..1)_CR
251 	 */
252 	/* 8-bits, FIFO enable */
253 	write32(&uart->pl011.lcr_h, PL011_UARTLCR_H_WLEN_8 |
254 				    PL011_UARTLCR_H_FEN);
255 	/* RX/TX enable, UART enable */
256 	write32(&uart->pl011.cr, PL011_UARTCR_RXE | PL011_UARTCR_TXE |
257 				 PL011_UARTCR_UARTEN);
258 
259 	return 0;
260 }
261