blob: 2f8537cbcd5258256f3b648dcef81fca2fb55dce [file] [log] [blame]
Angel Pons5f249e62020-04-04 18:51:01 +02001/* SPDX-License-Identifier: GPL-2.0-only */
Angel Pons5f249e62020-04-04 18:51:01 +02002
David Hendricks8cbd5692017-12-01 20:49:48 -08003/*
David Hendricks8cbd5692017-12-01 20:49:48 -08004 * Derived from Cavium's BSD-3 Clause OCTEONTX-SDK-6.2.0.
5 */
6
Kyösti Mälkki13f66502019-03-03 08:01:05 +02007#include <device/mmio.h>
David Hendricks8cbd5692017-12-01 20:49:48 -08008#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
18union 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;
Elyes HAOUAScb103462019-01-03 09:38:52 +010025 u64 : 19;
David Hendricks8cbd5692017-12-01 20:49:48 -080026 u64 h_clkdiv_sel : 3;
Elyes HAOUAScb103462019-01-03 09:38:52 +010027 u64 : 1;
David Hendricks8cbd5692017-12-01 20:49:48 -080028 u64 h_clkdiv_rst : 1;
29 u64 h_clk_byp_sel : 1;
30 u64 h_clk_en : 1;
Elyes HAOUAScb103462019-01-03 09:38:52 +010031 u64 : 33;
David Hendricks8cbd5692017-12-01 20:49:48 -080032 } s;
33};
34
35struct 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
David Hendricks8cbd5692017-12-01 20:49:48 -080050check_member(cn81xx_uart, uctl_ctl, 0x1000);
51check_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 */
61static 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 */
76static 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
85unsigned 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
Felix Helde3a12472020-09-11 15:47:09 +020096uintptr_t uart_platform_base(unsigned int idx)
David Hendricks8cbd5692017-12-01 20:49:48 -080097{
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 */
107static 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 */
120int 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 */
141int 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 /**
Patrick Rudolphd0dcf872018-03-28 12:28:02 +0200223 * 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 /**
David Hendricks8cbd5692017-12-01 20:49:48 -0800231 * 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}