soc: Remove copyright notices
[coreboot.git] / src / soc / cavium / cn81xx / uart.c
blob6e88bd1748473f506f2efdcd3da0ac9ae9a03536
1 /*
2 * This file is part of the coreboot project.
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; version 2 of the License.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * Derived from Cavium's BSD-3 Clause OCTEONTX-SDK-6.2.0.
17 #include <device/mmio.h>
18 #include <console/uart.h>
19 #include <delay.h>
20 #include <endian.h>
21 #include <stdint.h>
22 #include <soc/clock.h>
23 #include <soc/uart.h>
24 #include <assert.h>
25 #include <soc/addressmap.h>
26 #include <drivers/uart/pl011.h>
28 union cn81xx_uart_ctl {
29 u64 u;
30 struct {
31 u64 uctl_rst : 1;
32 u64 uaa_rst : 1;
33 u64 : 2;
34 u64 csclk_en : 1;
35 u64 : 19;
36 u64 h_clkdiv_sel : 3;
37 u64 : 1;
38 u64 h_clkdiv_rst : 1;
39 u64 h_clk_byp_sel : 1;
40 u64 h_clk_en : 1;
41 u64 : 33;
42 } s;
45 struct cn81xx_uart {
46 struct pl011_uart pl011;
47 union cn81xx_uart_ctl uctl_ctl;
48 u8 rsvd4[0x8];
49 u64 uctl_spare0;
50 u8 rsvd5[0xe0];
51 u64 uctl_spare1;
54 #define UART_IBRD_BAUD_DIVINT_SHIFT 0
55 #define UART_IBRD_BAUD_DIVINT_MASK 0xffff
57 #define UART_FBRD_BAUD_DIVFRAC_SHIFT 0
58 #define UART_FBRD_BAUD_DIVFRAC_MASK 0x3f
61 check_member(cn81xx_uart, uctl_ctl, 0x1000);
62 check_member(cn81xx_uart, uctl_spare1, 0x10f8);
64 #define UART_SCLK_DIV 3
66 /**
67 * Returns the current UART HCLK divider
69 * @param reg The H_CLKDIV_SEL value
70 * @return The HCLK divider
72 static size_t uart_sclk_divisor(const size_t reg)
74 static const u8 div[] = {1, 2, 4, 6, 8, 16, 24, 32};
76 assert(reg < ARRAY_SIZE(div));
78 return div[reg];
81 /**
82 * Returns the current UART HCLK
84 * @param uart The UART to operate on
85 * @return The HCLK in Hz
87 static size_t uart_hclk(struct cn81xx_uart *uart)
89 union cn81xx_uart_ctl ctl;
90 const uint64_t sclk = thunderx_get_io_clock();
92 ctl.u = read64(&uart->uctl_ctl);
93 return sclk / uart_sclk_divisor(ctl.s.h_clkdiv_sel);
96 unsigned int uart_platform_refclk(void)
98 struct cn81xx_uart *uart =
99 (struct cn81xx_uart *)CONFIG_CONSOLE_SERIAL_UART_ADDRESS;
101 if (!uart)
102 return 0;
104 return uart_hclk(uart);
107 uintptr_t uart_platform_base(int idx)
109 return CONFIG_CONSOLE_SERIAL_UART_ADDRESS;
113 * Waits given count if HCLK cycles
115 * @param uart The UART to operate on
116 * @param hclks The number of HCLK cycles to wait
118 static void uart_wait_hclk(struct cn81xx_uart *uart, const size_t hclks)
120 const size_t hclk = uart_hclk(uart);
121 const size_t delay = (hclks * 1000000ULL) / hclk;
122 udelay(MAX(delay, 1));
126 * Returns the UART state.
128 * @param bus The UART to operate on
129 * @return Boolean: True if UART is enabled
131 int uart_is_enabled(const size_t bus)
133 struct cn81xx_uart *uart = (struct cn81xx_uart *)UAAx_PF_BAR0(bus);
134 union cn81xx_uart_ctl ctl;
136 assert(uart);
137 if (!uart)
138 return 0;
140 ctl.u = read64(&uart->uctl_ctl);
141 return !!ctl.s.csclk_en;
145 * Setup UART with desired BAUD rate in 8N1, no parity mode.
147 * @param bus The UART to operate on
148 * @param baudrate baudrate to set up
150 * @return Boolean: True on error
152 int uart_setup(const size_t bus, int baudrate)
154 union cn81xx_uart_ctl ctl;
155 struct cn81xx_uart *uart = (struct cn81xx_uart *)UAAx_PF_BAR0(bus);
157 assert(uart);
158 if (!uart)
159 return 1;
161 /* 1.2.1 Initialization Sequence (Power-On/Hard/Cold Reset) */
162 /* 1. Wait for IOI reset (srst_n) to deassert. */
165 * 2. Assert all resets:
166 * a. UAA reset: UCTL_CTL[UAA_RST] = 1
167 * b. UCTL reset: UCTL_CTL[UCTL_RST] = 1
169 ctl.u = read64(&uart->uctl_ctl);
170 ctl.s.uctl_rst = 1;
171 ctl.s.uaa_rst = 1;
172 write64(&uart->uctl_ctl, ctl.u);
175 * 3. Configure the HCLK:
176 * a. Reset the clock dividers: UCTL_CTL[H_CLKDIV_RST] = 1.
177 * b. Select the HCLK frequency
178 * i. UCTL_CTL[H_CLKDIV] = desired value,
179 * ii. UCTL_CTL[H_CLKDIV_EN] = 1 to enable the HCLK.
180 * iii. Readback UCTL_CTL to ensure the values take effect.
181 * c. Deassert the HCLK clock divider reset: UCTL_CTL[H_CLKDIV_RST] = 0.
183 ctl.u = read64(&uart->uctl_ctl);
184 ctl.s.h_clkdiv_sel = UART_SCLK_DIV;
185 write64(&uart->uctl_ctl, ctl.u);
187 ctl.u = read64(&uart->uctl_ctl);
188 ctl.s.h_clk_byp_sel = 0;
189 write64(&uart->uctl_ctl, ctl.u);
191 ctl.u = read64(&uart->uctl_ctl);
192 ctl.s.h_clk_en = 1;
193 write64(&uart->uctl_ctl, ctl.u);
195 ctl.u = read64(&uart->uctl_ctl);
196 ctl.s.h_clkdiv_rst = 0;
197 write64(&uart->uctl_ctl, ctl.u);
200 * 4. Wait 20 HCLK cycles from step 3 for HCLK to start and async fifo
201 * to properly reset.
203 uart_wait_hclk(uart, 20 + 1);
206 * 5. Deassert UCTL and UAHC resets:
207 * a. UCTL_CTL[UCTL_RST] = 0
208 * b. Wait 10 HCLK cycles.
209 * c. UCTL_CTL[UAHC_RST] = 0
210 * d. You will have to wait 10 HCLK cycles before accessing any
211 * HCLK-only registers.
213 ctl.u = read64(&uart->uctl_ctl);
214 ctl.s.uctl_rst = 0;
215 write64(&uart->uctl_ctl, ctl.u);
217 uart_wait_hclk(uart, 10 + 1);
219 ctl.u = read64(&uart->uctl_ctl);
220 ctl.s.uaa_rst = 0;
221 write64(&uart->uctl_ctl, ctl.u);
223 uart_wait_hclk(uart, 10 + 1);
226 * 6. Enable conditional SCLK of UCTL by writing
227 * UCTL_CTL[CSCLK_EN] = 1.
229 ctl.u = read64(&uart->uctl_ctl);
230 ctl.s.csclk_en = 1;
231 write64(&uart->uctl_ctl, ctl.u);
234 * Exit here if the UART is not going to be used in coreboot.
235 * The previous initialization steps are sufficient to make the Linux
236 * kernel not panic.
238 if (!baudrate)
239 return 0;
242 * 7. Initialize the integer and fractional baud rate divider registers
243 * UARTIBRD and UARTFBRD as follows:
244 * a. Baud Rate Divisor = UARTCLK/(16xBaud Rate) = BRDI + BRDF
245 * b. The fractional register BRDF, m is calculated as
246 * integer(BRDF x 64 + 0.5)
247 * Example calculation:
248 * If the required baud rate is 230400 and hclk = 4MHz then:
249 * Baud Rate Divisor = (4x10^6)/(16x230400) = 1.085
250 * This means BRDI = 1 and BRDF = 0.085.
251 * Therefore, fractional part, BRDF = integer((0.085x64)+0.5) = 5
252 * Generated baud rate divider = 1+5/64 = 1.078
254 u64 divisor = thunderx_get_io_clock() /
255 (baudrate * 16 * uart_sclk_divisor(UART_SCLK_DIV) / 64);
256 write32(&uart->pl011.ibrd, divisor >> 6);
257 write32(&uart->pl011.fbrd, divisor & UART_FBRD_BAUD_DIVFRAC_MASK);
260 * 8. Program the line control register UAA(0..1)_LCR_H and the control
261 * register UAA(0..1)_CR
263 /* 8-bits, FIFO enable */
264 write32(&uart->pl011.lcr_h, PL011_UARTLCR_H_WLEN_8 |
265 PL011_UARTLCR_H_FEN);
266 /* RX/TX enable, UART enable */
267 write32(&uart->pl011.cr, PL011_UARTCR_RXE | PL011_UARTCR_TXE |
268 PL011_UARTCR_UARTEN);
270 return 0;