| /* SPDX-License-Identifier: GPL-2.0-only */ |
| |
| /* |
| * Derived from Cavium's BSD-3 Clause OCTEONTX-SDK-6.2.0. |
| */ |
| |
| #include <device/mmio.h> |
| #include <console/uart.h> |
| #include <delay.h> |
| #include <endian.h> |
| #include <stdint.h> |
| #include <soc/clock.h> |
| #include <soc/uart.h> |
| #include <assert.h> |
| #include <soc/addressmap.h> |
| #include <drivers/uart/pl011.h> |
| |
| union cn81xx_uart_ctl { |
| u64 u; |
| struct { |
| u64 uctl_rst : 1; |
| u64 uaa_rst : 1; |
| u64 : 2; |
| u64 csclk_en : 1; |
| u64 : 19; |
| u64 h_clkdiv_sel : 3; |
| u64 : 1; |
| u64 h_clkdiv_rst : 1; |
| u64 h_clk_byp_sel : 1; |
| u64 h_clk_en : 1; |
| u64 : 33; |
| } s; |
| }; |
| |
| struct cn81xx_uart { |
| struct pl011_uart pl011; |
| union cn81xx_uart_ctl uctl_ctl; |
| u8 rsvd4[0x8]; |
| u64 uctl_spare0; |
| u8 rsvd5[0xe0]; |
| u64 uctl_spare1; |
| }; |
| |
| #define UART_IBRD_BAUD_DIVINT_SHIFT 0 |
| #define UART_IBRD_BAUD_DIVINT_MASK 0xffff |
| |
| #define UART_FBRD_BAUD_DIVFRAC_SHIFT 0 |
| #define UART_FBRD_BAUD_DIVFRAC_MASK 0x3f |
| |
| |
| check_member(cn81xx_uart, uctl_ctl, 0x1000); |
| check_member(cn81xx_uart, uctl_spare1, 0x10f8); |
| |
| #define UART_SCLK_DIV 3 |
| |
| /** |
| * Returns the current UART HCLK divider |
| * |
| * @param reg The H_CLKDIV_SEL value |
| * @return The HCLK divider |
| */ |
| static size_t uart_sclk_divisor(const size_t reg) |
| { |
| static const u8 div[] = {1, 2, 4, 6, 8, 16, 24, 32}; |
| |
| assert(reg < ARRAY_SIZE(div)); |
| |
| return div[reg]; |
| } |
| |
| /** |
| * Returns the current UART HCLK |
| * |
| * @param uart The UART to operate on |
| * @return The HCLK in Hz |
| */ |
| static size_t uart_hclk(struct cn81xx_uart *uart) |
| { |
| union cn81xx_uart_ctl ctl; |
| const uint64_t sclk = thunderx_get_io_clock(); |
| |
| ctl.u = read64(&uart->uctl_ctl); |
| return sclk / uart_sclk_divisor(ctl.s.h_clkdiv_sel); |
| } |
| |
| unsigned int uart_platform_refclk(void) |
| { |
| struct cn81xx_uart *uart = |
| (struct cn81xx_uart *)CONFIG_CONSOLE_SERIAL_UART_ADDRESS; |
| |
| if (!uart) |
| return 0; |
| |
| return uart_hclk(uart); |
| } |
| |
| uintptr_t uart_platform_base(int idx) |
| { |
| return CONFIG_CONSOLE_SERIAL_UART_ADDRESS; |
| } |
| |
| /** |
| * Waits given count if HCLK cycles |
| * |
| * @param uart The UART to operate on |
| * @param hclks The number of HCLK cycles to wait |
| */ |
| static void uart_wait_hclk(struct cn81xx_uart *uart, const size_t hclks) |
| { |
| const size_t hclk = uart_hclk(uart); |
| const size_t delay = (hclks * 1000000ULL) / hclk; |
| udelay(MAX(delay, 1)); |
| } |
| |
| /** |
| * Returns the UART state. |
| * |
| * @param bus The UART to operate on |
| * @return Boolean: True if UART is enabled |
| */ |
| int uart_is_enabled(const size_t bus) |
| { |
| struct cn81xx_uart *uart = (struct cn81xx_uart *)UAAx_PF_BAR0(bus); |
| union cn81xx_uart_ctl ctl; |
| |
| assert(uart); |
| if (!uart) |
| return 0; |
| |
| ctl.u = read64(&uart->uctl_ctl); |
| return !!ctl.s.csclk_en; |
| } |
| |
| /** |
| * Setup UART with desired BAUD rate in 8N1, no parity mode. |
| * |
| * @param bus The UART to operate on |
| * @param baudrate baudrate to set up |
| * |
| * @return Boolean: True on error |
| */ |
| int uart_setup(const size_t bus, int baudrate) |
| { |
| union cn81xx_uart_ctl ctl; |
| struct cn81xx_uart *uart = (struct cn81xx_uart *)UAAx_PF_BAR0(bus); |
| |
| assert(uart); |
| if (!uart) |
| return 1; |
| |
| /* 1.2.1 Initialization Sequence (Power-On/Hard/Cold Reset) */ |
| /* 1. Wait for IOI reset (srst_n) to deassert. */ |
| |
| /** |
| * 2. Assert all resets: |
| * a. UAA reset: UCTL_CTL[UAA_RST] = 1 |
| * b. UCTL reset: UCTL_CTL[UCTL_RST] = 1 |
| */ |
| ctl.u = read64(&uart->uctl_ctl); |
| ctl.s.uctl_rst = 1; |
| ctl.s.uaa_rst = 1; |
| write64(&uart->uctl_ctl, ctl.u); |
| |
| /** |
| * 3. Configure the HCLK: |
| * a. Reset the clock dividers: UCTL_CTL[H_CLKDIV_RST] = 1. |
| * b. Select the HCLK frequency |
| * i. UCTL_CTL[H_CLKDIV] = desired value, |
| * ii. UCTL_CTL[H_CLKDIV_EN] = 1 to enable the HCLK. |
| * iii. Readback UCTL_CTL to ensure the values take effect. |
| * c. Deassert the HCLK clock divider reset: UCTL_CTL[H_CLKDIV_RST] = 0. |
| */ |
| ctl.u = read64(&uart->uctl_ctl); |
| ctl.s.h_clkdiv_sel = UART_SCLK_DIV; |
| write64(&uart->uctl_ctl, ctl.u); |
| |
| ctl.u = read64(&uart->uctl_ctl); |
| ctl.s.h_clk_byp_sel = 0; |
| write64(&uart->uctl_ctl, ctl.u); |
| |
| ctl.u = read64(&uart->uctl_ctl); |
| ctl.s.h_clk_en = 1; |
| write64(&uart->uctl_ctl, ctl.u); |
| |
| ctl.u = read64(&uart->uctl_ctl); |
| ctl.s.h_clkdiv_rst = 0; |
| write64(&uart->uctl_ctl, ctl.u); |
| |
| /** |
| * 4. Wait 20 HCLK cycles from step 3 for HCLK to start and async fifo |
| * to properly reset. |
| */ |
| uart_wait_hclk(uart, 20 + 1); |
| |
| /** |
| * 5. Deassert UCTL and UAHC resets: |
| * a. UCTL_CTL[UCTL_RST] = 0 |
| * b. Wait 10 HCLK cycles. |
| * c. UCTL_CTL[UAHC_RST] = 0 |
| * d. You will have to wait 10 HCLK cycles before accessing any |
| * HCLK-only registers. |
| */ |
| ctl.u = read64(&uart->uctl_ctl); |
| ctl.s.uctl_rst = 0; |
| write64(&uart->uctl_ctl, ctl.u); |
| |
| uart_wait_hclk(uart, 10 + 1); |
| |
| ctl.u = read64(&uart->uctl_ctl); |
| ctl.s.uaa_rst = 0; |
| write64(&uart->uctl_ctl, ctl.u); |
| |
| uart_wait_hclk(uart, 10 + 1); |
| |
| /** |
| * 6. Enable conditional SCLK of UCTL by writing |
| * UCTL_CTL[CSCLK_EN] = 1. |
| */ |
| ctl.u = read64(&uart->uctl_ctl); |
| ctl.s.csclk_en = 1; |
| write64(&uart->uctl_ctl, ctl.u); |
| |
| /** |
| * Exit here if the UART is not going to be used in coreboot. |
| * The previous initialization steps are sufficient to make the Linux |
| * kernel not panic. |
| */ |
| if (!baudrate) |
| return 0; |
| |
| /** |
| * 7. Initialize the integer and fractional baud rate divider registers |
| * UARTIBRD and UARTFBRD as follows: |
| * a. Baud Rate Divisor = UARTCLK/(16xBaud Rate) = BRDI + BRDF |
| * b. The fractional register BRDF, m is calculated as |
| * integer(BRDF x 64 + 0.5) |
| * Example calculation: |
| * If the required baud rate is 230400 and hclk = 4MHz then: |
| * Baud Rate Divisor = (4x10^6)/(16x230400) = 1.085 |
| * This means BRDI = 1 and BRDF = 0.085. |
| * Therefore, fractional part, BRDF = integer((0.085x64)+0.5) = 5 |
| * Generated baud rate divider = 1+5/64 = 1.078 |
| */ |
| u64 divisor = thunderx_get_io_clock() / |
| (baudrate * 16 * uart_sclk_divisor(UART_SCLK_DIV) / 64); |
| write32(&uart->pl011.ibrd, divisor >> 6); |
| write32(&uart->pl011.fbrd, divisor & UART_FBRD_BAUD_DIVFRAC_MASK); |
| |
| /** |
| * 8. Program the line control register UAA(0..1)_LCR_H and the control |
| * register UAA(0..1)_CR |
| */ |
| /* 8-bits, FIFO enable */ |
| write32(&uart->pl011.lcr_h, PL011_UARTLCR_H_WLEN_8 | |
| PL011_UARTLCR_H_FEN); |
| /* RX/TX enable, UART enable */ |
| write32(&uart->pl011.cr, PL011_UARTCR_RXE | PL011_UARTCR_TXE | |
| PL011_UARTCR_UARTEN); |
| |
| return 0; |
| } |