Patrick Rudolph has uploaded this change for review. ( https://review.coreboot.org/23779
Change subject: soc/cavium: Move UART ......................................................................
soc/cavium: Move UART
Move UART code to common folder. Use addressmap.h instead of hardcoding registers. Implement a function to wait # HCLKs and get rid of long delays. Fix coding style. Add comments.
Change-Id: I33a043b753651b0aa8cf8b31bd0ecde836859c64 Signed-off-by: Patrick Rudolph patrick.rudolph@9elements.com --- M src/mainboard/cavium/cn8100_sff_evb/bootblock.c M src/soc/cavium/cn81xx/Makefile.inc D src/soc/cavium/cn81xx/include/soc/uart.h D src/soc/cavium/cn81xx/uart.c M src/soc/cavium/common/Makefile.inc A src/soc/cavium/common/include/soc/uart.h A src/soc/cavium/common/uart.c 7 files changed, 296 insertions(+), 177 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/79/23779/1
diff --git a/src/mainboard/cavium/cn8100_sff_evb/bootblock.c b/src/mainboard/cavium/cn8100_sff_evb/bootblock.c index 977a295..8cf2307 100644 --- a/src/mainboard/cavium/cn8100_sff_evb/bootblock.c +++ b/src/mainboard/cavium/cn8100_sff_evb/bootblock.c @@ -26,18 +26,12 @@
void bootblock_mainboard_early_init(void) { - struct cn81xx_uart *uart0 = (struct cn81xx_uart *)UAA0_PF_BAR0; - struct cn81xx_uart *uart1 = (struct cn81xx_uart *)UAA1_PF_BAR0; - -#if IS_ENABLED(CONFIG_DRIVERS_UART) - _Static_assert(CONFIG_CONSOLE_SERIAL_UART_ADDRESS == UAA0_PF_BAR0, - "CONSOLE_SERIAL_UART should be UART0"); -#endif - - if (!(uart0->uctl_ctl & UART_UCTL_CTL_H_CLK_EN)) - cavium_uart_setup(uart0, CONFIG_TTYS0_BAUD); - if (!(uart1->uctl_ctl & UART_UCTL_CTL_H_CLK_EN)) - cavium_uart_setup(uart1, CONFIG_TTYS0_BAUD); + if (IS_ENABLED(CONFIG_BOOTBLOCK_CONSOLE)) { + if (!uart_is_enabled(0)) + uart_setup(0, CONFIG_TTYS0_BAUD); + if (!uart_is_enabled(1)) + uart_setup(0, CONFIG_TTYS0_BAUD); + } }
static void configure_spi_flash(void) diff --git a/src/soc/cavium/cn81xx/Makefile.inc b/src/soc/cavium/cn81xx/Makefile.inc index 1ac9f69..f835b09 100644 --- a/src/soc/cavium/cn81xx/Makefile.inc +++ b/src/soc/cavium/cn81xx/Makefile.inc @@ -19,9 +19,6 @@ bootblock-$(CONFIG_BOOTBLOCK_CUSTOM) += bootblock_custom.S
#bootblock-y += ../common/i2c.c -ifeq ($(CONFIG_BOOTBLOCK_CONSOLE),y) -bootblock-$(CONFIG_DRIVERS_UART) += uart.c -endif #bootblock-y += ../common/gpio.c #bootblock-y += ../common/pwm.c bootblock-y += bootblock.c @@ -69,13 +66,11 @@ #verstage-y += gpio.c #verstage-y += sdram.c #verstage-y += ../common/i2c.c -#verstage-$(CONFIG_DRIVERS_UART) += uart.c
################################################################################
romstage-y += ../common/cbmem.c -romstage-$(CONFIG_DRIVERS_UART) += uart.c romstage-y += mmu_operations.c #romstage-y += ../common/pwm.c #romstage-y += gpio.c @@ -85,7 +80,6 @@
ramstage-y += ../common/cbmem.c ramstage-y += sdram.c -ramstage-$(CONFIG_DRIVERS_UART) += uart.c #ramstage-y += ../common/gpio.c #ramstage-y += gpio.c #ramstage-y += ../common/i2c.c diff --git a/src/soc/cavium/cn81xx/include/soc/uart.h b/src/soc/cavium/cn81xx/include/soc/uart.h deleted file mode 100644 index f45dbe1..0000000 --- a/src/soc/cavium/cn81xx/include/soc/uart.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * This file is part of the coreboot project. - * - * Copyright 2017-present Facebook, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef __SOC_CAVIUM_COMMON_INCLUDE_SOC_UART_H -#define __SOC_CAVIUM_COMMON_INCLUDE_SOC_UART_H - -#include <drivers/uart/pl011.h> -#include <inttypes.h> -#include <types.h> - -struct cn81xx_uart { - struct pl011_uart pl011; - u64 uctl_ctl; - u8 rsvd4[0x8]; - u64 uctl_spare0; - u8 rsvd5[0xe0]; - u64 uctl_spare1; -}; -check_member(cn81xx_uart, uctl_ctl, 0x1000); -check_member(cn81xx_uart, uctl_spare1, 0x10f8); - -#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 - -#define UART_UCTL_CTL_UCTL_RST (1 << 0) -#define UART_UCTL_CTL_UAA_RST (1 << 1) -#define UART_UCTL_CTL_CSCLK_EN (1 << 4) -#define UART_UCTL_CTL_H_CLKDIV_SEL_SHIFT 24 -#define UART_UCTL_CTL_H_CLKDIV_MASK 0x7 -#define UART_UCTL_CTL_H_CLKDIV_RST (1 << 28) -#define UART_UCTL_CTL_H_CLK_BYP_SEL (1 << 29) -#define UART_UCTL_CTL_H_CLK_EN (1 << 30) - -int cavium_uart_setup(struct cn81xx_uart *uart, int baudrate); - -#endif /* __SOC_CAVIUM_COMMON_INCLUDE_SOC_UART_H */ diff --git a/src/soc/cavium/cn81xx/uart.c b/src/soc/cavium/cn81xx/uart.c deleted file mode 100644 index aacca82..0000000 --- a/src/soc/cavium/cn81xx/uart.c +++ /dev/null @@ -1,105 +0,0 @@ -/* - * This file is part of the coreboot project. - * - * Copyright (c) 2003-2017 Cavium Inc. (support@cavium.com). All rights - * reserved. - * Copyright 2017-present Facebook, Inc. - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#include <arch/io.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> - -#define UART_SCLK_DIV 3 - -static size_t uart_sclk_divisor(size_t reg) -{ - static const u8 div[] = {1, 2, 4, 6, 8, 16, 24, 32}; - - assert(reg < ARRAY_SIZE(div)); - - return div[reg]; -} - -unsigned int uart_platform_refclk(void) -{ - return thunderx_get_io_clock() / uart_sclk_divisor(UART_SCLK_DIV); -} - -uintptr_t uart_platform_base(int idx) -{ - return CONFIG_CONSOLE_SERIAL_UART_ADDRESS; -} - -int cavium_uart_setup(struct cn81xx_uart *uart, int baudrate) -{ - /* 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 */ - setbits_le64(&uart->uctl_ctl, UART_UCTL_CTL_UCTL_RST | UART_UCTL_CTL_UAA_RST); - - /* 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. */ - clrsetbits_le64(&uart->uctl_ctl, - UART_UCTL_CTL_H_CLKDIV_MASK << UART_UCTL_CTL_H_CLKDIV_SEL_SHIFT, - UART_SCLK_DIV << UART_UCTL_CTL_H_CLKDIV_SEL_SHIFT); - clrbits_le64(&uart->uctl_ctl, UART_UCTL_CTL_H_CLK_BYP_SEL); - setbits_le64(&uart->uctl_ctl, UART_UCTL_CTL_H_CLK_EN); - clrbits_le64(&uart->uctl_ctl, UART_UCTL_CTL_H_CLKDIV_RST); - - /* 4. Wait 20 HCLK cycles from step 3 for HCLK to start and async fifo - to properly reset. */ - mdelay(200); /* Overkill */ - - /* 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. */ - clrbits_le64(&uart->uctl_ctl, UART_UCTL_CTL_UCTL_RST); - mdelay(100); /* Overkill */ - clrbits_le64(&uart->uctl_ctl, UART_UCTL_CTL_UAA_RST); - mdelay(100); /* Overkill */ - - /* 6. Enable conditional SCLK of UCTL by writing UCTL_CTL[CSCLK_EN] = 1. */ - setbits_le32(&uart->uctl_ctl, UART_UCTL_CTL_CSCLK_EN); - - /* 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; -} diff --git a/src/soc/cavium/common/Makefile.inc b/src/soc/cavium/common/Makefile.inc index fce377c..feeeb6e 100644 --- a/src/soc/cavium/common/Makefile.inc +++ b/src/soc/cavium/common/Makefile.inc @@ -21,7 +21,10 @@ bootblock-y += gpio.c bootblock-y += timer.c bootblock-y += spi.c - +bootblock-y += uart.c +ifeq ($(CONFIG_BOOTBLOCK_CONSOLE),y) +bootblock-$(CONFIG_DRIVERS_UART) += uart.c +endif
romstage-y += twsi.c @@ -29,14 +32,16 @@ romstage-y += gpio.c romstage-y += timer.c romstage-y += spi.c - - +romstage-y += uart.c +romstage-$(CONFIG_DRIVERS_UART) += uart.c
ramstage-y += twsi.c ramstage-y += clock.c ramstage-y += gpio.c ramstage-y += timer.c ramstage-y += spi.c +ramstage-y += uart.c +ramstage-$(CONFIG_DRIVERS_UART) += uart.c
CPPFLAGS_common += -Isrc/soc/cavium/common/include
diff --git a/src/soc/cavium/common/include/soc/uart.h b/src/soc/cavium/common/include/soc/uart.h new file mode 100644 index 0000000..e402206 --- /dev/null +++ b/src/soc/cavium/common/include/soc/uart.h @@ -0,0 +1,25 @@ +/* + * This file is part of the coreboot project. + * + * Copyright 2017-present Facebook, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __SOC_CAVIUM_COMMON_INCLUDE_SOC_UART_H +#define __SOC_CAVIUM_COMMON_INCLUDE_SOC_UART_H + +#include <inttypes.h> +#include <types.h> + +int uart_is_enabled(const size_t bus); +int uart_setup(const size_t bus, int baudrate); + +#endif /* __SOC_CAVIUM_COMMON_INCLUDE_SOC_UART_H */ diff --git a/src/soc/cavium/common/uart.c b/src/soc/cavium/common/uart.c new file mode 100644 index 0000000..b3cce8e --- /dev/null +++ b/src/soc/cavium/common/uart.c @@ -0,0 +1,257 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (c) 2003-2017 Cavium Inc. (support@cavium.com). All rights + * reserved. + * Copyright 2017-present Facebook, Inc. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <arch/io.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); + + /** + * 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; +}