diff --git a/configs/config.google_panther.pch_serialio_uart b/configs/config.google_panther.pch_serialio_uart new file mode 100644 index 0000000000..5f7f4c7745 --- /dev/null +++ b/configs/config.google_panther.pch_serialio_uart @@ -0,0 +1,5 @@ +# Configuration used to build-test Lynx Point SerialIO UART console code. +CONFIG_VENDOR_GOOGLE=y +CONFIG_BOARD_GOOGLE_PANTHER=y +CONFIG_SERIALIO_UART_CONSOLE=y +# CONFIG_DRIVERS_UART_8250IO is not set diff --git a/src/southbridge/intel/lynxpoint/Kconfig b/src/southbridge/intel/lynxpoint/Kconfig index a9a2f5bb9e..fe859f62ed 100644 --- a/src/southbridge/intel/lynxpoint/Kconfig +++ b/src/southbridge/intel/lynxpoint/Kconfig @@ -68,4 +68,15 @@ config PCIEXP_AER bool default y +config SERIALIO_UART_CONSOLE + bool "Use SerialIO UART for console" + depends on INTEL_LYNXPOINT_LP + select DRIVERS_UART_8250MEM_32 + help + Selected by mainboards where SerialIO UARTs can be used to retrieve + coreboot logs. Boards also need to set UART_FOR_CONSOLE accordingly. + +config CONSOLE_UART_BASE_ADDRESS + default 0xd6000000 if SERIALIO_UART_CONSOLE + endif diff --git a/src/southbridge/intel/lynxpoint/Makefile.inc b/src/southbridge/intel/lynxpoint/Makefile.inc index 2923035e1e..02022d348d 100644 --- a/src/southbridge/intel/lynxpoint/Makefile.inc +++ b/src/southbridge/intel/lynxpoint/Makefile.inc @@ -42,6 +42,10 @@ romstage-y += lp_gpio.c ramstage-y += lp_gpio.c smm-y += lp_gpio.c verstage-y += lp_gpio.c +bootblock-$(CONFIG_SERIALIO_UART_CONSOLE) += uart_init.c +bootblock-$(CONFIG_SERIALIO_UART_CONSOLE) += iobp.c +all-$(CONFIG_SERIALIO_UART_CONSOLE) += uart.c +smm-$(CONFIG_SERIALIO_UART_CONSOLE) += uart.c endif verstage-y += pmutil.c diff --git a/src/southbridge/intel/lynxpoint/bootblock.c b/src/southbridge/intel/lynxpoint/bootblock.c index c063bfb10a..495871c6d2 100644 --- a/src/southbridge/intel/lynxpoint/bootblock.c +++ b/src/southbridge/intel/lynxpoint/bootblock.c @@ -51,4 +51,7 @@ void bootblock_early_southbridge_init(void) pch_enable_lpc(); mainboard_config_superio(); + + if (CONFIG(SERIALIO_UART_CONSOLE)) + uart_bootblock_init(); } diff --git a/src/southbridge/intel/lynxpoint/pch.h b/src/southbridge/intel/lynxpoint/pch.h index 7b09d54f18..598c2dc7b2 100644 --- a/src/southbridge/intel/lynxpoint/pch.h +++ b/src/southbridge/intel/lynxpoint/pch.h @@ -157,6 +157,7 @@ void acpi_create_serialio_ssdt(acpi_header_t *ssdt); void enable_usb_bar(void); void early_pch_init(void); void pch_enable_lpc(void); +void uart_bootblock_init(void); void mainboard_config_superio(void); void mainboard_config_rcba(void); @@ -372,6 +373,9 @@ void mainboard_config_rcba(void); #define SIO_REG_PPR_CLOCK 0x800 #define SIO_REG_PPR_CLOCK_EN (1 << 0) +#define SIO_REG_PPR_CLOCK_UPDATE (1 << 31) +#define SIO_REG_PPR_CLOCK_M_DIV 0x25a +#define SIO_REG_PPR_CLOCK_N_DIV 0x7fff #define SIO_REG_PPR_RST 0x804 #define SIO_REG_PPR_RST_ASSERT 0x3 #define SIO_REG_PPR_GEN 0x808 diff --git a/src/southbridge/intel/lynxpoint/serialio.c b/src/southbridge/intel/lynxpoint/serialio.c index 05263fec39..34f8c78c6d 100644 --- a/src/southbridge/intel/lynxpoint/serialio.c +++ b/src/southbridge/intel/lynxpoint/serialio.c @@ -8,6 +8,7 @@ #include #include #include +#include #include "chip.h" #include "iobp.h" #include "pch.h" @@ -20,6 +21,19 @@ static void serialio_enable_clock(struct resource *bar0) write32(res2mmio(bar0, SIO_REG_PPR_CLOCK, 0), reg32); } +static bool serialio_uart_is_debug(struct device *dev) +{ + if (CONFIG(SERIALIO_UART_CONSOLE)) { + switch (dev->path.pci.devfn) { + case PCH_DEVFN_UART0: + return CONFIG_UART_FOR_CONSOLE == 0; + case PCH_DEVFN_UART1: + return CONFIG_UART_FOR_CONSOLE == 1; + } + } + return 0; +} + /* Put Serial IO D21:F0-F6 device into desired mode. */ static void serialio_d21_mode(int sio_index, int int_pin, int acpi_mode) { @@ -197,13 +211,15 @@ static void serialio_init(struct device *dev) break; case PCH_DEVFN_UART0: /* UART0 */ sio_index = SIO_ID_UART0; - serialio_d21_ltr(bar0); + if (!serialio_uart_is_debug(dev)) + serialio_d21_ltr(bar0); serialio_d21_mode(sio_index, SIO_PIN_INTD, config->sio_acpi_mode); break; case PCH_DEVFN_UART1: /* UART1 */ sio_index = SIO_ID_UART1; - serialio_d21_ltr(bar0); + if (!serialio_uart_is_debug(dev)) + serialio_d21_ltr(bar0); serialio_d21_mode(sio_index, SIO_PIN_INTD, config->sio_acpi_mode); break; @@ -221,8 +237,21 @@ static void serialio_init(struct device *dev) update_bars(sio_index, (u32)bar0->base, (u32)bar1->base); } +static void serialio_read_resources(struct device *dev) +{ + pci_dev_read_resources(dev); + + /* Set the configured UART base address for the debug port */ + if (CONFIG(SERIALIO_UART_CONSOLE) && serialio_uart_is_debug(dev)) { + struct resource *res = find_resource(dev, PCI_BASE_ADDRESS_0); + res->base = CONFIG_CONSOLE_UART_BASE_ADDRESS; + res->size = 0x1000; + res->flags = IORESOURCE_MEM | IORESOURCE_ASSIGNED | IORESOURCE_FIXED; + } +} + static struct device_operations device_ops = { - .read_resources = pci_dev_read_resources, + .read_resources = serialio_read_resources, .set_resources = pci_dev_set_resources, .enable_resources = pci_dev_enable_resources, .init = serialio_init, diff --git a/src/southbridge/intel/lynxpoint/uart.c b/src/southbridge/intel/lynxpoint/uart.c new file mode 100644 index 0000000000..95b286ebfc --- /dev/null +++ b/src/southbridge/intel/lynxpoint/uart.c @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include + +uintptr_t uart_platform_base(unsigned int idx) +{ + if (idx == CONFIG_UART_FOR_CONSOLE) + return CONFIG_CONSOLE_UART_BASE_ADDRESS; + + return 0; +} diff --git a/src/southbridge/intel/lynxpoint/uart_init.c b/src/southbridge/intel/lynxpoint/uart_init.c new file mode 100644 index 0000000000..66b9e01981 --- /dev/null +++ b/src/southbridge/intel/lynxpoint/uart_init.c @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static pci_devfn_t get_uart_pci_device(void) +{ + switch (CONFIG_UART_FOR_CONSOLE) { + case 0: return PCI_DEV(0, 0x15, 5); + case 1: return PCI_DEV(0, 0x15, 6); + default: return dead_code_t(pci_devfn_t); + } +} + +/* TODO: Figure out if all steps are actually necessary */ +void uart_bootblock_init(void) +{ + const pci_devfn_t dev = get_uart_pci_device(); + + /* Program IOBP GPIODF */ + pch_iobp_update(SIO_IOBP_GPIODF, ~0x131f, 0x131f); + + /* Program IOBP CB000180h[5:0] = 111111b (undefined register) */ + pch_iobp_update(0xcb000180, ~0x0000003f, 0x0000003f); + + /* Set and enable MMIO BAR */ + pci_write_config32(dev, PCI_BASE_ADDRESS_0, CONFIG_CONSOLE_UART_BASE_ADDRESS); + pci_or_config16(dev, PCI_COMMAND, PCI_COMMAND_MEMORY); + + void *const bar = (void *)(uintptr_t)CONFIG_CONSOLE_UART_BASE_ADDRESS; + + /* Initialize LTR */ + clrbits32(bar + SIO_REG_PPR_GEN, SIO_REG_PPR_GEN_LTR_MODE_MASK); + clrbits32(bar + SIO_REG_PPR_RST, SIO_REG_PPR_RST_ASSERT); + + /* Take UART out of reset */ + setbits32(bar + SIO_REG_PPR_RST, SIO_REG_PPR_RST_ASSERT); + + /* Set M and N divisor inputs and enable clock */ + uint32_t ppr_clock = 0; + ppr_clock |= SIO_REG_PPR_CLOCK_EN; + ppr_clock |= SIO_REG_PPR_CLOCK_UPDATE; + ppr_clock |= SIO_REG_PPR_CLOCK_N_DIV << 16; + ppr_clock |= SIO_REG_PPR_CLOCK_M_DIV << 1; + write32(bar + SIO_REG_PPR_CLOCK, ppr_clock); +}