/* SPDX-License-Identifier: GPL-2.0-only */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "chip.h" #define UART_PCI_ENABLE (PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER) extern const unsigned int uart_devices[]; extern const int uart_devices_size; static void uart_lpss_init(pci_devfn_t dev, uintptr_t baseaddr) { /* Ensure controller is in D0 state */ lpss_set_power_state(dev, STATE_D0); /* Take UART out of reset */ lpss_reset_release(baseaddr); /* Set M and N divisor inputs and enable clock */ lpss_clk_update(baseaddr, CONFIG_SOC_INTEL_COMMON_LPSS_UART_CLK_M_VAL, CONFIG_SOC_INTEL_COMMON_LPSS_UART_CLK_N_VAL); } #if CONFIG(INTEL_LPSS_UART_FOR_CONSOLE) uintptr_t uart_platform_base(unsigned int idx) { if (idx == CONFIG_UART_FOR_CONSOLE) return CONFIG_CONSOLE_UART_BASE_ADDRESS; return 0; } #endif static pci_devfn_t uart_console_get_pci_bdf(void) { int devfn; /* * This function will get called even if INTEL_LPSS_UART_FOR_CONSOLE * config option is not selected. * By default return NULL in this case to avoid compilation errors. */ if (!CONFIG(INTEL_LPSS_UART_FOR_CONSOLE)) return PCI_DEV_INVALID; if (CONFIG_UART_FOR_CONSOLE > uart_devices_size) return PCI_DEV_INVALID; devfn = uart_devices[CONFIG_UART_FOR_CONSOLE]; if (devfn == PCI_DEVFN_INVALID) return PCI_DEV_INVALID; return PCI_DEV(0, PCI_SLOT(devfn), PCI_FUNC(devfn)); } const struct device *uart_get_device(void) { pci_devfn_t dev = uart_console_get_pci_bdf(); if (dev == PCI_DEV_INVALID) return NULL; return pcidev_path_on_root(PCI_DEV2DEVFN(dev)); } bool uart_is_controller_initialized(void) { uintptr_t base; pci_devfn_t dev = uart_console_get_pci_bdf(); if (dev == PCI_DEV_INVALID) return false; base = pci_s_read_config32(dev, PCI_BASE_ADDRESS_0) & ~0xFFF; if (!base) return false; if ((pci_s_read_config16(dev, PCI_COMMAND) & UART_PCI_ENABLE) != UART_PCI_ENABLE) return false; return !lpss_is_controller_in_reset(base); } void uart_bootblock_init(void) { const uint32_t baseaddr = CONFIG_CONSOLE_UART_BASE_ADDRESS; pci_devfn_t dev = uart_console_get_pci_bdf(); if (dev == PCI_DEV_INVALID) return; /* Set UART base address */ pci_s_write_config32(dev, PCI_BASE_ADDRESS_0, baseaddr); /* Enable memory access and bus master */ pci_s_write_config16(dev, PCI_COMMAND, UART_PCI_ENABLE); uart_lpss_init(dev, baseaddr); } #if ENV_RAMSTAGE static void uart_read_resources(struct device *dev) { pci_dev_read_resources(dev); /* Set the configured UART base address for the debug port */ if (CONFIG(INTEL_LPSS_UART_FOR_CONSOLE) && uart_is_debug_controller(dev)) { struct resource *res = find_resource(dev, PCI_BASE_ADDRESS_0); /* Need to set the base and size for the resource allocator. */ res->base = CONFIG_CONSOLE_UART_BASE_ADDRESS; res->size = 0x1000; res->flags = IORESOURCE_MEM | IORESOURCE_ASSIGNED | IORESOURCE_FIXED; } /* In ACPI mode mark the decoded region as reserved */ if (dev->hidden) { struct resource *res = find_resource(dev, PCI_BASE_ADDRESS_0); res->flags |= IORESOURCE_RESERVE; } } /* * Check if UART debug port controller needs to be initialized on resume. * * Returns: * true = when SoC wants debug port initialization on resume * false = otherwise */ static bool pch_uart_init_debug_controller_on_resume(void) { struct global_nvs *gnvs = acpi_get_gnvs(); if (gnvs) return !!gnvs->uior; return false; } bool uart_is_debug_controller(struct device *dev) { return dev == uart_get_device(); } /* * This is a workaround to enable UART controller for the debug port if: * 1. CONSOLE_SERIAL is not enabled in coreboot, and * 2. This boot is S3 resume, and * 3. SoC wants to initialize debug UART controller. * * This workaround is required because Linux kernel hangs on resume if console * is not enabled in coreboot, but it is enabled in kernel and not suspended. */ static bool uart_controller_needs_init(struct device *dev) { /* * If coreboot has CONSOLE_SERIAL enabled, the skip re-initializing * controller here. */ if (CONFIG(CONSOLE_SERIAL)) return false; /* If this device does not correspond to debug port, then skip. */ if (!uart_is_debug_controller(dev)) return false; /* Initialize UART controller only on S3 resume. */ if (!acpi_is_wakeup_s3()) return false; /* * check if SOC wants to initialize UART on resume */ return pch_uart_init_debug_controller_on_resume(); } static void uart_common_enable_resources(struct device *dev) { pci_dev_enable_resources(dev); if (uart_controller_needs_init(dev)) { uintptr_t base; base = pci_read_config32(dev, PCI_BASE_ADDRESS_0) & ~0xFFF; if (base) uart_lpss_init(PCI_BDF(dev), base); } } static void uart_acpi_write_irq(const struct device *dev) { if (CONFIG(SOC_INTEL_COMMON_BLOCK_IRQ)) { const int irq = get_pci_devfn_irq(dev->path.pci.devfn); if (irq != INVALID_IRQ) { struct acpi_irq airq = (struct acpi_irq)ACPI_IRQ_LEVEL_LOW(irq); acpi_device_write_interrupt(&airq); } } } /* * Generate an ACPI entry if the device is enabled in devicetree for the ACPI * LPSS driver. In this mode the device and vendor ID reads as 0xffff, but the * PCI device is still there. */ static void uart_fill_ssdt(const struct device *dev) { const char *scope = acpi_device_scope(dev); const char *hid = acpi_device_hid(dev); struct resource *res; /* In ACPI mode the device is "invisible" */ if (!dev->hidden) return; if (!scope || !hid) return; res = probe_resource(dev, PCI_BASE_ADDRESS_0); if (!res) return; /* Scope */ acpigen_write_scope(scope); /* Device */ acpigen_write_device(acpi_device_name(dev)); acpigen_write_name_string("_HID", hid); /* * Advertise compatibility to Sunrise Point, as the Linux kernel doesn't support * CannonPoint yet... */ if (strcmp(hid, "INT34B8") == 0) acpigen_write_name_string("_CID", "INT3448"); else if (strcmp(hid, "INT34B9") == 0) acpigen_write_name_string("_CID", "INT3449"); else if (strcmp(hid, "INT34BA") == 0) acpigen_write_name_string("_CID", "INT344A"); acpi_device_write_uid(dev); acpigen_write_name_string("_DDN", "LPSS ACPI UART"); /* Do not hide the UART device from the OS */ acpigen_write_STA(ACPI_STATUS_DEVICE_ALL_ON); /* Resources */ acpigen_write_name("_CRS"); acpigen_write_resourcetemplate_header(); uart_acpi_write_irq(dev); acpigen_write_mem32fixed(1, res->base, res->size); acpigen_write_resourcetemplate_footer(); acpigen_pop_len(); /* Device */ acpigen_pop_len(); /* Scope */ } static const char *uart_acpi_hid(const struct device *dev) { switch (dev->device) { case PCI_DID_INTEL_APL_UART0: return "80865abc"; case PCI_DID_INTEL_APL_UART1: return "80865abe"; case PCI_DID_INTEL_APL_UART2: return "80865ac0"; case PCI_DID_INTEL_GLK_UART0: return "808631bc"; case PCI_DID_INTEL_GLK_UART1: return "808631be"; case PCI_DID_INTEL_GLK_UART2: return "808631c0"; case PCI_DID_INTEL_GLK_UART3: return "808631ee"; case PCI_DID_INTEL_SPT_UART0: case PCI_DID_INTEL_SPT_H_UART0: return "INT3448"; case PCI_DID_INTEL_SPT_UART1: case PCI_DID_INTEL_SPT_H_UART1: return "INT3449"; case PCI_DID_INTEL_SPT_UART2: case PCI_DID_INTEL_SPT_H_UART2: return "INT344A"; case PCI_DID_INTEL_CNP_H_UART0: return "INT34B8"; case PCI_DID_INTEL_CNP_H_UART1: return "INT34B9"; case PCI_DID_INTEL_CNP_H_UART2: return "INT34BA"; default: return NULL; } } static const char *uart_acpi_name(const struct device *dev) { switch (dev->device) { case PCI_DID_INTEL_ADP_P_UART0: case PCI_DID_INTEL_APL_UART0: case PCI_DID_INTEL_GLK_UART0: case PCI_DID_INTEL_SPT_UART0: case PCI_DID_INTEL_SPT_H_UART0: case PCI_DID_INTEL_CNP_H_UART0: return "UAR0"; case PCI_DID_INTEL_ADP_P_UART1: case PCI_DID_INTEL_APL_UART1: case PCI_DID_INTEL_GLK_UART1: case PCI_DID_INTEL_SPT_UART1: case PCI_DID_INTEL_SPT_H_UART1: case PCI_DID_INTEL_CNP_H_UART1: return "UAR1"; case PCI_DID_INTEL_ADP_P_UART2: case PCI_DID_INTEL_APL_UART2: case PCI_DID_INTEL_GLK_UART2: case PCI_DID_INTEL_SPT_UART2: case PCI_DID_INTEL_SPT_H_UART2: case PCI_DID_INTEL_CNP_H_UART2: return "UAR2"; case PCI_DID_INTEL_ADP_P_UART3: case PCI_DID_INTEL_GLK_UART3: return "UAR3"; default: return NULL; } } struct device_operations uart_ops = { .read_resources = uart_read_resources, .set_resources = pci_dev_set_resources, .enable_resources = uart_common_enable_resources, .ops_pci = &pci_dev_ops_pci, .acpi_fill_ssdt = uart_fill_ssdt, .acpi_hid = uart_acpi_hid, .acpi_name = uart_acpi_name, }; static const unsigned short pci_device_ids[] = { PCI_DID_INTEL_MTL_UART0, PCI_DID_INTEL_MTL_UART1, PCI_DID_INTEL_MTL_UART2, PCI_DID_INTEL_APL_UART0, PCI_DID_INTEL_APL_UART1, PCI_DID_INTEL_APL_UART2, PCI_DID_INTEL_APL_UART3, PCI_DID_INTEL_CNL_UART0, PCI_DID_INTEL_CNL_UART1, PCI_DID_INTEL_CNL_UART2, PCI_DID_INTEL_GLK_UART0, PCI_DID_INTEL_GLK_UART1, PCI_DID_INTEL_GLK_UART2, PCI_DID_INTEL_GLK_UART3, PCI_DID_INTEL_CNP_H_UART0, PCI_DID_INTEL_CNP_H_UART1, PCI_DID_INTEL_CNP_H_UART2, PCI_DID_INTEL_ICP_UART0, PCI_DID_INTEL_ICP_UART1, PCI_DID_INTEL_ICP_UART2, PCI_DID_INTEL_CMP_UART0, PCI_DID_INTEL_CMP_UART1, PCI_DID_INTEL_CMP_UART2, PCI_DID_INTEL_CMP_H_UART0, PCI_DID_INTEL_CMP_H_UART1, PCI_DID_INTEL_CMP_H_UART2, PCI_DID_INTEL_TGP_UART0, PCI_DID_INTEL_TGP_UART1, PCI_DID_INTEL_TGP_UART2, PCI_DID_INTEL_TGP_H_UART0, PCI_DID_INTEL_TGP_H_UART1, PCI_DID_INTEL_TGP_H_UART2, PCI_DID_INTEL_TGP_H_UART3, PCI_DID_INTEL_MCC_UART0, PCI_DID_INTEL_MCC_UART1, PCI_DID_INTEL_MCC_UART2, PCI_DID_INTEL_JSP_UART0, PCI_DID_INTEL_JSP_UART1, PCI_DID_INTEL_JSP_UART2, PCI_DID_INTEL_ADP_S_UART0, PCI_DID_INTEL_ADP_S_UART1, PCI_DID_INTEL_ADP_S_UART2, PCI_DID_INTEL_ADP_S_UART3, PCI_DID_INTEL_ADP_S_UART4, PCI_DID_INTEL_ADP_S_UART5, PCI_DID_INTEL_ADP_S_UART6, PCI_DID_INTEL_ADP_P_UART0, PCI_DID_INTEL_ADP_P_UART1, PCI_DID_INTEL_ADP_P_UART2, PCI_DID_INTEL_ADP_P_UART3, PCI_DID_INTEL_ADP_P_UART4, PCI_DID_INTEL_ADP_P_UART5, PCI_DID_INTEL_ADP_P_UART6, PCI_DID_INTEL_ADP_M_N_UART0, PCI_DID_INTEL_ADP_M_N_UART1, PCI_DID_INTEL_ADP_M_N_UART2, PCI_DID_INTEL_ADP_M_N_UART3, 0, }; static const struct pci_driver pch_uart __pci_driver = { .ops = &uart_ops, .vendor = PCI_VID_INTEL, .devices = pci_device_ids, }; static void uart_enable(struct device *dev) { struct soc_intel_common_block_uart_config *conf = dev->chip_info; dev->ops = &uart_ops; dev->device = conf ? conf->devid : 0; } struct chip_operations soc_intel_common_block_uart_ops = { CHIP_NAME("LPSS UART in ACPI mode") .enable_dev = uart_enable }; #endif /* ENV_RAMSTAGE */