1 /* SPDX-License-Identifier: GPL-2.0-only */
4 * Derived from Cavium's BSD-3 Clause OCTEONTX-SDK-6.2.0.
7 #include <device/mmio.h>
8 #include <console/uart.h>
12 #include <soc/clock.h>
15 #include <soc/addressmap.h>
16 #include <drivers/uart/pl011.h>
18 union cn81xx_uart_ctl
{
29 u64 h_clk_byp_sel
: 1;
36 struct pl011_uart pl011
;
37 union cn81xx_uart_ctl uctl_ctl
;
44 #define UART_IBRD_BAUD_DIVINT_SHIFT 0
45 #define UART_IBRD_BAUD_DIVINT_MASK 0xffff
47 #define UART_FBRD_BAUD_DIVFRAC_SHIFT 0
48 #define UART_FBRD_BAUD_DIVFRAC_MASK 0x3f
50 check_member(cn81xx_uart
, uctl_ctl
, 0x1000);
51 check_member(cn81xx_uart
, uctl_spare1
, 0x10f8);
53 #define UART_SCLK_DIV 3
56 * Returns the current UART HCLK divider
58 * @param reg The H_CLKDIV_SEL value
59 * @return The HCLK divider
61 static size_t uart_sclk_divisor(const size_t reg
)
63 static const u8 div
[] = {1, 2, 4, 6, 8, 16, 24, 32};
65 assert(reg
< ARRAY_SIZE(div
));
71 * Returns the current UART HCLK
73 * @param uart The UART to operate on
74 * @return The HCLK in Hz
76 static size_t uart_hclk(struct cn81xx_uart
*uart
)
78 union cn81xx_uart_ctl ctl
;
79 const uint64_t sclk
= thunderx_get_io_clock();
81 ctl
.u
= read64(&uart
->uctl_ctl
);
82 return sclk
/ uart_sclk_divisor(ctl
.s
.h_clkdiv_sel
);
85 unsigned int uart_platform_refclk(void)
87 struct cn81xx_uart
*uart
=
88 (struct cn81xx_uart
*)CONFIG_CONSOLE_SERIAL_UART_ADDRESS
;
93 return uart_hclk(uart
);
96 uintptr_t uart_platform_base(unsigned int idx
)
98 return CONFIG_CONSOLE_SERIAL_UART_ADDRESS
;
102 * Waits given count if HCLK cycles
104 * @param uart The UART to operate on
105 * @param hclks The number of HCLK cycles to wait
107 static void uart_wait_hclk(struct cn81xx_uart
*uart
, const size_t hclks
)
109 const size_t hclk
= uart_hclk(uart
);
110 const size_t delay
= (hclks
* 1000000ULL) / hclk
;
111 udelay(MAX(delay
, 1));
115 * Returns the UART state.
117 * @param bus The UART to operate on
118 * @return Boolean: True if UART is enabled
120 int uart_is_enabled(const size_t bus
)
122 struct cn81xx_uart
*uart
= (struct cn81xx_uart
*)UAAx_PF_BAR0(bus
);
123 union cn81xx_uart_ctl ctl
;
129 ctl
.u
= read64(&uart
->uctl_ctl
);
130 return !!ctl
.s
.csclk_en
;
134 * Setup UART with desired BAUD rate in 8N1, no parity mode.
136 * @param bus The UART to operate on
137 * @param baudrate baudrate to set up
139 * @return Boolean: True on error
141 int uart_setup(const size_t bus
, int baudrate
)
143 union cn81xx_uart_ctl ctl
;
144 struct cn81xx_uart
*uart
= (struct cn81xx_uart
*)UAAx_PF_BAR0(bus
);
150 /* 1.2.1 Initialization Sequence (Power-On/Hard/Cold Reset) */
151 /* 1. Wait for IOI reset (srst_n) to deassert. */
154 * 2. Assert all resets:
155 * a. UAA reset: UCTL_CTL[UAA_RST] = 1
156 * b. UCTL reset: UCTL_CTL[UCTL_RST] = 1
158 ctl
.u
= read64(&uart
->uctl_ctl
);
161 write64(&uart
->uctl_ctl
, ctl
.u
);
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.
172 ctl
.u
= read64(&uart
->uctl_ctl
);
173 ctl
.s
.h_clkdiv_sel
= UART_SCLK_DIV
;
174 write64(&uart
->uctl_ctl
, ctl
.u
);
176 ctl
.u
= read64(&uart
->uctl_ctl
);
177 ctl
.s
.h_clk_byp_sel
= 0;
178 write64(&uart
->uctl_ctl
, ctl
.u
);
180 ctl
.u
= read64(&uart
->uctl_ctl
);
182 write64(&uart
->uctl_ctl
, ctl
.u
);
184 ctl
.u
= read64(&uart
->uctl_ctl
);
185 ctl
.s
.h_clkdiv_rst
= 0;
186 write64(&uart
->uctl_ctl
, ctl
.u
);
189 * 4. Wait 20 HCLK cycles from step 3 for HCLK to start and async fifo
192 uart_wait_hclk(uart
, 20 + 1);
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.
202 ctl
.u
= read64(&uart
->uctl_ctl
);
204 write64(&uart
->uctl_ctl
, ctl
.u
);
206 uart_wait_hclk(uart
, 10 + 1);
208 ctl
.u
= read64(&uart
->uctl_ctl
);
210 write64(&uart
->uctl_ctl
, ctl
.u
);
212 uart_wait_hclk(uart
, 10 + 1);
215 * 6. Enable conditional SCLK of UCTL by writing
216 * UCTL_CTL[CSCLK_EN] = 1.
218 ctl
.u
= read64(&uart
->uctl_ctl
);
220 write64(&uart
->uctl_ctl
, ctl
.u
);
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
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
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
);
249 * 8. Program the line control register UAA(0..1)_LCR_H and the control
250 * register UAA(0..1)_CR
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
);