diff --git a/src/acpi/device.c b/src/acpi/device.c index 2b085b61a64..fddff7d8b7c 100644 --- a/src/acpi/device.c +++ b/src/acpi/device.c @@ -19,6 +19,27 @@ #define ACPI_DP_UUID "daffd814-6eba-4d8c-8a91-bc9bbf4aa301" #define ACPI_DP_CHILD_UUID "dbb8e3e6-5886-4ba6-8795-1319f52a966b" +/* + * Below properties are defined at + * https://docs.microsoft.com/en-us/windows-hardware/drivers/pci/dsd-for-pcie-root-ports + */ +#define ACPI_DSD_EXTERNAL_FACING_PORT_UUID "EFCC06CC-73AC-4BC3-BFF0-76143807C389" +#define ACPI_DSD_EXTERNAL_FACING_PORT_NAME "ExternalFacingPort" + +#define ACPI_DSD_HOTPLUG_IN_D3_UUID "6211E2C0-58A3-4AF3-90E1-927A4E0C55A4" +#define ACPI_DSD_HOTPLUG_IN_D3_NAME "HotPlugSupportInD3" + +/* ID for the DmaProperty _DSD */ +#define ACPI_DSD_DMA_PROPERTY_UUID "70D24161-6DD5-4C9E-8070-705531292865" +#define ACPI_DSD_DMA_PROPERTY_NAME "DmaProperty" + +/* + * Below properties are defined at + * https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/power-management-for-storage-hardware-devices-intro + */ +#define ACPI_DSD_STORAGE_D3_UUID "5025030F-842F-4AB4-A561-99A5189762D0" +#define ACPI_DSD_STORAGE_D3_NAME "StorageD3Enable" + /* Write empty word value and return pointer to it */ static void *acpi_device_write_zero_len(void) { @@ -1183,3 +1204,65 @@ void acpi_device_write_pci_dev(const struct device *dev) acpigen_pop_len(); /* Device */ acpigen_pop_len(); /* Scope */ } + + +/* + * Helper function to add given integer property with an UUID to _DSD in the current scope. + * + * dsd - Pointer to a _DSD object. + * Append to existing _DSD object if not NULL. + * Create new _DSD object and flush it if NULL. + * uuid - Pointer to the UUID string. + * name - Pointer to the property name string. + * value - Value of the integer property. + */ +static void acpi_device_add_integer_property_with_uuid(struct acpi_dp *dsd, + const char *uuid, + const char *name, + uint64_t value) +{ + struct acpi_dp *prev_dsd = dsd, *pkg; + if (prev_dsd == NULL) + dsd = acpi_dp_new_table("_DSD"); + pkg = acpi_dp_new_table(uuid); + acpi_dp_add_integer(pkg, name, value); + acpi_dp_add_package(dsd, pkg); + if (prev_dsd == NULL) + acpi_dp_write(dsd); +} + +/* _DSD with ExternalFacingPort */ +void acpi_device_add_external_facing_port(struct acpi_dp *dsd) +{ + acpi_device_add_integer_property_with_uuid(dsd, + ACPI_DSD_EXTERNAL_FACING_PORT_UUID, + ACPI_DSD_EXTERNAL_FACING_PORT_NAME, + 1); +} + +/* _DSD with HotPlugSupportInD3 */ +void acpi_device_add_hotplug_support_in_d3(struct acpi_dp *dsd) +{ + acpi_device_add_integer_property_with_uuid(dsd, + ACPI_DSD_HOTPLUG_IN_D3_UUID, + ACPI_DSD_HOTPLUG_IN_D3_NAME, + 1); +} + +/* _DSD with DmaProperty */ +void acpi_device_add_dma_property(struct acpi_dp *dsd) +{ + acpi_device_add_integer_property_with_uuid(dsd, + ACPI_DSD_DMA_PROPERTY_UUID, + ACPI_DSD_DMA_PROPERTY_NAME, + 1); +} + +/* _DSD with StorageD3Enable */ +void acpi_device_add_storage_d3_enable(struct acpi_dp *dsd) +{ + acpi_device_add_integer_property_with_uuid(dsd, + ACPI_DSD_STORAGE_D3_UUID, + ACPI_DSD_STORAGE_D3_NAME, + 1); +} diff --git a/src/drivers/intel/dtbt/Kconfig b/src/drivers/intel/dtbt/Kconfig new file mode 100644 index 00000000000..b01512644ed --- /dev/null +++ b/src/drivers/intel/dtbt/Kconfig @@ -0,0 +1,30 @@ +config DRIVERS_INTEL_DTBT + def_bool n + help + Support for discrete Thunderbolt controllers + + +if !PCIEXP_HOTPLUG + +config PCIEXP_HOTPLUG_BUSES + int + default 42 + +config PCIEXP_HOTPLUG_MEM + hex + default 0xc200000 + +config PCIEXP_HOTPLUG_PREFETCH_MEM + hex + default 0x1c000000 + +config PCIEXP_HOTPLUG_IO + hex + default 0x1000 + +config PCIEXP_HOTPLUG_PREFETCH_MEM_ABOVE_4G + bool + depends on RESOURCE_ALLOCATOR_V4 + default y + +endif diff --git a/src/drivers/intel/dtbt/Makefile.inc b/src/drivers/intel/dtbt/Makefile.inc new file mode 100644 index 00000000000..1b5252dda09 --- /dev/null +++ b/src/drivers/intel/dtbt/Makefile.inc @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only + +ramstage-$(CONFIG_DRIVERS_INTEL_DTBT) += dtbt.c diff --git a/src/drivers/intel/dtbt/chip.h b/src/drivers/intel/dtbt/chip.h new file mode 100644 index 00000000000..2b1dfa70a52 --- /dev/null +++ b/src/drivers/intel/dtbt/chip.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _DRIVERS_INTEL_DTBT_CHIP_H_ +#define _DRIVERS_INTEL_DTBT_CHIP_H_ + +struct drivers_intel_dtbt_config {}; + +#endif /* _DRIVERS_INTEL_DTBT_CHIP_H_ */ diff --git a/src/drivers/intel/dtbt/dtbt.c b/src/drivers/intel/dtbt/dtbt.c new file mode 100644 index 00000000000..629b54c37e3 --- /dev/null +++ b/src/drivers/intel/dtbt/dtbt.c @@ -0,0 +1,262 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include "chip.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#define PCIE2TBT 0x54C +#define PCIE2TBT_VALID BIT(0) +#define PCIE2TBT_GO2SX 2 +#define PCIE2TBT_GO2SX_NO_WAKE 3 +#define PCIE2TBT_SX_EXIT_TBT_CONNECTED 4 +#define PCIE2TBT_SX_EXIT_NO_TBT_CONNECTED 5 +#define PCIE2TBT_SET_SECURITY_LEVEL 8 +#define PCIE2TBT_GET_SECURITY_LEVEL 9 +#define PCIE2TBT_BOOT_ON 24 +#define TBT2PCIE 0x548 +#define TBT2PCIE_DONE BIT(0) +#define TIMEOUT_MS 1000 + +static void dtbt_cmd(struct device *dev, u32 command) +{ + printk(BIOS_DEBUG, "DTBT send command %08x\n", command); + + pci_write_config32(dev, PCIE2TBT, (command << 1) | PCIE2TBT_VALID); + + if (!wait_ms(TIMEOUT_MS, pci_read_config32(dev, TBT2PCIE) & TBT2PCIE_DONE)) { + printk(BIOS_ERR, "DTBT command %08x send timeout\n", command); + } + + pci_write_config32(dev, PCIE2TBT, 0); + if (!wait_ms(TIMEOUT_MS, !(pci_read_config32(dev, TBT2PCIE) & TBT2PCIE_DONE))) { + printk(BIOS_ERR, "DTBT command %08x clear timeout\n", command); + } +} + +static bool is_pcie_port_type(const struct device *dev, u16 type) +{ + unsigned int pciexpos; + u16 flags; + + pciexpos = pci_find_capability(dev, PCI_CAP_ID_PCIE); + if (pciexpos) { + flags = pci_read_config16(dev, pciexpos + PCI_EXP_FLAGS); + if (((flags & PCI_EXP_FLAGS_TYPE) >> 4) == type) + return true; + } + + return false; +} + +#if CONFIG(HAVE_ACPI_TABLES) +static void dtbt_write_dsd(void) +{ + struct acpi_dp *dsd = acpi_dp_new_table("_DSD"); + + acpi_device_add_hotplug_support_in_d3(dsd); + acpi_device_add_external_facing_port(dsd); + acpi_dp_write(dsd); +} + +static void dtbt_write_opregion(const struct bus *bus) +{ + uintptr_t mmconf_base = (uintptr_t)CONFIG_ECAM_MMCONF_BASE_ADDRESS + + (((uintptr_t)(bus->secondary)) << 20); + const struct opregion opregion = OPREGION("PXCS", SYSTEMMEMORY, mmconf_base, 0x1000); + const struct fieldlist fieldlist[] = { + FIELDLIST_OFFSET(TBT2PCIE), + FIELDLIST_NAMESTR("TB2P", 32), + FIELDLIST_OFFSET(PCIE2TBT), + FIELDLIST_NAMESTR("P2TB", 32), + }; + + acpigen_write_opregion(&opregion); + acpigen_write_field("PXCS", fieldlist, ARRAY_SIZE(fieldlist), + FIELD_DWORDACC | FIELD_NOLOCK | FIELD_PRESERVE); +} + +static void dtbt_fill_ssdt(const struct device *dev) +{ + struct bus *bus; + struct device *parent; + const char *parent_scope; + const char *dev_name = acpi_device_name(dev); + u16 dev_id; + u32 address; + + bus = dev->bus; + if (!bus) { + printk(BIOS_ERR, "DTBT bus invalid\n"); + return; + } + + parent = bus->dev; + if (!parent || parent->path.type != DEVICE_PATH_PCI) { + printk(BIOS_ERR, "DTBT parent invalid\n"); + return; + } + + parent_scope = acpi_device_path(parent); + if (!parent_scope) { + printk(BIOS_ERR, "DTBT parent scope not valid\n"); + return; + } + + dev_id = pci_read_config16(dev, PCI_DEVICE_ID); + + /* Only Maple Ridghe support for now */ + if (dev_id != PCI_DID_INTEL_MAPLE_RIDGE_JHL8340 && + dev_id != PCI_DID_INTEL_MAPLE_RIDGE_JHL8540) + return; + + + if (is_pcie_port_type(dev, PCI_EXP_TYPE_DOWNSTREAM)) { + /* For downstream port write Device only to make PEP happy */ + acpigen_write_scope(parent_scope); + acpigen_write_device(dev_name); + + address = PCI_SLOT(dev->path.pci.devfn) & 0xffff; + address <<= 16; + address |= PCI_FUNC(dev->path.pci.devfn) & 0xffff; + acpigen_write_name_dword("_ADR", address); + + acpigen_write_device_end(); + acpigen_write_scope_end(); + return; + } + /* Following code needs to be written only for upstream port */ + + /* Scope */ + acpigen_write_scope(parent_scope); + dtbt_write_dsd(); + + /* Device */ + acpigen_write_device(dev_name); + address = PCI_SLOT(dev->path.pci.devfn) & 0xffff; + address <<= 16; + address |= PCI_FUNC(dev->path.pci.devfn) & 0xffff; + acpigen_write_name_dword("_ADR", address); + dtbt_write_opregion(bus); + + /* Method */ + acpigen_write_method_serialized("PTS", 0); + + acpigen_write_debug_string("DTBT prepare to sleep"); + acpigen_write_store_int_to_namestr(PCIE2TBT_GO2SX_NO_WAKE << 1, "P2TB"); + acpigen_write_delay_until_namestr_int(600, "TB2P", PCIE2TBT_GO2SX_NO_WAKE << 1); + + acpigen_write_debug_namestr("TB2P"); + acpigen_write_store_int_to_namestr(0, "P2TB"); + acpigen_write_delay_until_namestr_int(600, "TB2P", 0); + acpigen_write_debug_namestr("TB2P"); + + acpigen_write_method_end(); + acpigen_write_device_end(); + acpigen_write_scope_end(); + + printk(BIOS_DEBUG, "DTBT fill SSDT\n"); + printk(BIOS_DEBUG, " Dev %s\n", dev_path(dev)); + printk(BIOS_DEBUG, " Bus %s\n", bus_path(bus)); + printk(BIOS_DEBUG, " Parent %s\n", dev_path(parent)); + printk(BIOS_DEBUG, " Scope %s\n", parent_scope); + printk(BIOS_DEBUG, " Device %s\n", dev_name); + + // \.TBTS Method + acpigen_write_scope("\\"); + acpigen_write_method("TBTS", 0); + acpigen_emit_namestring(acpi_device_path_join(dev, "PTS")); + acpigen_write_method_end(); + acpigen_write_scope_end(); +} + +static const char *dtbt_downstream_port_acpi_name(const struct device *dev) +{ + static char acpi_name[5]; + + /* ACPI 6.3, ASL 20.2.2: (Name Objects Encoding). */ + snprintf(acpi_name, sizeof(acpi_name), "TB%02X", + PCI_FUNC(dev->path.pci.devfn)); + return acpi_name; +} + +static const char *dtbt_acpi_name(const struct device *dev) +{ + if (!acpi_device_path(dev)) { + printk(BIOS_INFO, "%s, NULL device path for dev %s\n", __func__, dev_path(dev)); + } else { + printk(BIOS_INFO, "%s: %s at %s\n", __func__, acpi_device_path(dev), + dev_path(dev)); + } + + if (is_pcie_port_type(dev, PCI_EXP_TYPE_UPSTREAM)) + return "DTBT"; + else if (is_pcie_port_type(dev, PCI_EXP_TYPE_DOWNSTREAM)) + return dtbt_downstream_port_acpi_name(dev); + else + return "PXSX"; +} +#endif // HAVE_ACPI_TABLES + +static void dtbt_enable(struct device *dev) +{ + /* override the scan_bus operation for devices hotpluggable to downstream TB4 ports */ + if (is_pcie_port_type(dev, PCI_EXP_TYPE_DOWNSTREAM) && !CONFIG(PCIEXP_HOTPLUG)) + dev->ops->scan_bus = pciexp_hotplug_scan_bridge; + + /* Enable routine needs to be done only for upstream port */ + if (!is_pcie_port_type(dev, PCI_EXP_TYPE_UPSTREAM)) + return; + + printk(BIOS_INFO, "DTBT controller found at %s\n", dev_path(dev)); + + printk(BIOS_DEBUG, "DTBT get security level\n"); + dtbt_cmd(dev, PCIE2TBT_GET_SECURITY_LEVEL); + printk(BIOS_DEBUG, "DTBT set security level SL0\n"); + dtbt_cmd(dev, PCIE2TBT_SET_SECURITY_LEVEL); + printk(BIOS_DEBUG, "DTBT get security level\n"); + dtbt_cmd(dev, PCIE2TBT_GET_SECURITY_LEVEL); + + if (acpi_is_wakeup_s3()) { + printk(BIOS_DEBUG, "DTBT SX exit\n"); + dtbt_cmd(dev, PCIE2TBT_SX_EXIT_TBT_CONNECTED); + } else { + printk(BIOS_DEBUG, "DTBT boot on\n"); + dtbt_cmd(dev, PCIE2TBT_BOOT_ON); + } +} + +static struct pci_operations dtbt_device_ops_pci = { + .set_subsystem = 0, +}; + +static struct device_operations dtbt_device_ops = { + .read_resources = pci_bus_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_bus_enable_resources, +#if CONFIG(HAVE_ACPI_TABLES) + .acpi_name = dtbt_acpi_name, + .acpi_fill_ssdt = dtbt_fill_ssdt, +#endif + .scan_bus = pciexp_scan_bridge, + .reset_bus = pci_bus_reset, + .ops_pci = &dtbt_device_ops_pci, + .enable = dtbt_enable +}; + +static const unsigned short pci_device_ids[] = { + PCI_DID_INTEL_MAPLE_RIDGE_JHL8340, + PCI_DID_INTEL_MAPLE_RIDGE_JHL8540, + 0 +}; + +static const struct pci_driver intel_dtbt_driver __pci_driver = { + .ops = &dtbt_device_ops, + .vendor = PCI_VID_INTEL, + .devices = pci_device_ids, +}; diff --git a/src/include/acpi/acpi_device.h b/src/include/acpi/acpi_device.h index 71809eb6d46..9ce5e0a3b1f 100644 --- a/src/include/acpi/acpi_device.h +++ b/src/include/acpi/acpi_device.h @@ -588,4 +588,16 @@ void acpi_dp_write(struct acpi_dp *table); */ void acpi_device_write_pci_dev(const struct device *dev); +/* Helper function to add ExternalFacingPort to _DSD in the current scope */ +void acpi_device_add_external_facing_port(struct acpi_dp *dsd); + +/* Helper function to add HotPlugSupportInD3 to _DSD in the current scope */ +void acpi_device_add_hotplug_support_in_d3(struct acpi_dp *dsd); + +/* Helper function to add DmaProperty to _DSD in the current scope */ +void acpi_device_add_dma_property(struct acpi_dp *dsd); + +/* Helper function to add StorageD3Enable to _DSD in the current scope */ +void acpi_device_add_storage_d3_enable(struct acpi_dp *dsd); + #endif /* __ACPI_ACPI_DEVICE_H__ */ diff --git a/src/include/device/pci_ids.h b/src/include/device/pci_ids.h index a62a5ba1632..80d44686204 100644 --- a/src/include/device/pci_ids.h +++ b/src/include/device/pci_ids.h @@ -4478,6 +4478,8 @@ #define PCI_DID_INTEL_RPL_TBT_RP2 0xa72f #define PCI_DID_INTEL_RPL_TBT_DMA0 0xa73e #define PCI_DID_INTEL_RPL_TBT_DMA1 0xa76d +#define PCI_DID_INTEL_MAPLE_RIDGE_JHL8340 0x1133 +#define PCI_DID_INTEL_MAPLE_RIDGE_JHL8540 0x1136 /* Intel WIFI Ids */ #define PCI_DID_1000_SERIES_WIFI 0x0084 diff --git a/src/mainboard/msi/ms7d25/Kconfig b/src/mainboard/msi/ms7d25/Kconfig index 2cd1942f49b..3540a31cde2 100644 --- a/src/mainboard/msi/ms7d25/Kconfig +++ b/src/mainboard/msi/ms7d25/Kconfig @@ -10,6 +10,7 @@ config BOARD_MSI_MS7D25 select BOARD_ROMSIZE_KB_32768 select SOC_INTEL_COMMON_BLOCK_HDA_VERB select SUPERIO_NUVOTON_NCT6687D + select DRIVERS_INTEL_DTBT select DRIVERS_UART_8250IO select HAVE_ACPI_RESUME select HAVE_ACPI_TABLES diff --git a/src/mainboard/msi/ms7d25/acpi/mainboard.asl b/src/mainboard/msi/ms7d25/acpi/mainboard.asl index dd64d794bc5..029b3eb0b33 100644 --- a/src/mainboard/msi/ms7d25/acpi/mainboard.asl +++ b/src/mainboard/msi/ms7d25/acpi/mainboard.asl @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +External(\TBTS, MethodObj) + Scope (_GPE) { /* Empty PCI_EXP_STS handler */ Method (_L69, 0, Serialized) { } @@ -15,3 +17,13 @@ Scope (\_SB.PCI0) { } } } + +Scope (\_SB) { + Method (MPTS, 1) { + If (Arg0 == 3 || Arg0 == 4) { + If (CondRefOf (\TBTS)) { + \TBTS() + } + } + } +} \ No newline at end of file