diff --git a/src/device/pci_device.c b/src/device/pci_device.c index c043dd6591..77f0190b85 100644 --- a/src/device/pci_device.c +++ b/src/device/pci_device.c @@ -1218,6 +1218,7 @@ static void pci_bridge_route(struct bus *link, scan_state state) if (state == PCI_ROUTE_SCAN) { link->secondary = parent->subordinate + 1; link->subordinate = link->secondary; + printk(BIOS_DEBUG, "system76: pci_bridge_route: assigning link secondary %d subordinate %d\n", link->secondary, link->subordinate); } if (state == PCI_ROUTE_CLOSE) { diff --git a/src/device/root_device.c b/src/device/root_device.c index beeead2dfb..00d038e71c 100644 --- a/src/device/root_device.c +++ b/src/device/root_device.c @@ -92,6 +92,8 @@ void scan_generic_bus(struct device *bus) link->secondary = ++bus_max; + printk(BIOS_DEBUG, "system76: scan_generic_bus: assigning link secondary %d\n", link->secondary); + for (child = link->children; child; child = child->sibling) { if (child->chip_ops && child->chip_ops->enable_dev) diff --git a/src/drivers/thunderbolt/Kconfig b/src/drivers/thunderbolt/Kconfig new file mode 100644 index 0000000000..f64e6cb0a9 --- /dev/null +++ b/src/drivers/thunderbolt/Kconfig @@ -0,0 +1,4 @@ +config DRIVERS_THUNDERBOLT + bool + help + Thunderbolt support diff --git a/src/drivers/thunderbolt/Makefile.inc b/src/drivers/thunderbolt/Makefile.inc new file mode 100644 index 0000000000..623b8973d0 --- /dev/null +++ b/src/drivers/thunderbolt/Makefile.inc @@ -0,0 +1 @@ +ramstage-$(CONFIG_DRIVERS_THUNDERBOLT) += thunderbolt.c diff --git a/src/drivers/thunderbolt/thunderbolt.c b/src/drivers/thunderbolt/thunderbolt.c new file mode 100644 index 0000000000..f629469401 --- /dev/null +++ b/src/drivers/thunderbolt/thunderbolt.c @@ -0,0 +1,93 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2019 System76. + * Copyright (C) 2017-2018 Intel Corporation. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +static void slot_dev_read_resources(struct device *dev) +{ + struct resource *resource; + + resource = new_resource(dev, 0x10); + resource->size = 1 << 28; + resource->align = 22; + resource->gran = 22; + resource->limit = 0xffffffff; + resource->flags |= IORESOURCE_MEM; + + resource = new_resource(dev, 0x14); + resource->size = 1 << 28; + resource->align = 22; + resource->gran = 22; + resource->limit = 0xffffffff; + resource->flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH; + + resource = new_resource(dev, 0x18); + resource->size = 1 << 13; + resource->align = 12; + resource->gran = 12; + resource->limit = 0xffff; + resource->flags |= IORESOURCE_IO; +} + +static struct device_operations slot_dev_ops = { + .read_resources = slot_dev_read_resources, +}; + +static void tbt_pciexp_scan_bridge(struct device *dev) { + printk(BIOS_DEBUG, "tbt_pciexp_scan_bridge\n"); + + /* Normal PCIe Scan */ + pciexp_scan_bridge(dev); + + /* Add dummy slot to preserve resources */ + struct device *slot; + struct device_path slot_path = { .type = DEVICE_PATH_NONE }; + slot = alloc_dev(dev->link_list, &slot_path); + slot->ops = &slot_dev_ops; +} + +static struct pci_operations pcie_ops = { + .set_subsystem = pci_dev_set_subsystem, +}; + +static struct device_operations device_ops = { + .read_resources = pci_bus_read_resources, + .set_resources = pci_dev_set_resources, + .enable_resources = pci_bus_enable_resources, + .init = 0, + .scan_bus = tbt_pciexp_scan_bridge, + .enable = 0, + .reset_bus = pci_bus_reset, + .ops_pci = &pcie_ops, +}; + +static const unsigned short pcie_device_ids[] = { + // JHL7540 Thunderbolt 3 Bridge + 0x15e7, + 0 +}; + +static const struct pci_driver tbt_pcie __pci_driver = { + .ops = &device_ops, + .vendor = PCI_VENDOR_ID_INTEL, + .devices = pcie_device_ids, +}; diff --git a/src/mainboard/system76/cml-u/Kconfig b/src/mainboard/system76/cml-u/Kconfig index 1a39d11cf6..bec06e78ad 100644 --- a/src/mainboard/system76/cml-u/Kconfig +++ b/src/mainboard/system76/cml-u/Kconfig @@ -4,6 +4,7 @@ config BOARD_SPECIFIC_OPTIONS def_bool y select ADD_FSP_BINARIES select BOARD_ROMSIZE_KB_16384 + select DRIVERS_THUNDERBOLT select EC_ACPI select EXCLUDE_EMMC_INTERFACE select HAVE_ACPI_RESUME diff --git a/src/mainboard/system76/cml-u/devicetree.cb b/src/mainboard/system76/cml-u/devicetree.cb index 04b88ff21f..c8e79613ed 100644 --- a/src/mainboard/system76/cml-u/devicetree.cb +++ b/src/mainboard/system76/cml-u/devicetree.cb @@ -209,7 +209,13 @@ chip soc/intel/cannonlake device pci 19.1 off end # I2C #5 device pci 19.2 off end # UART #2 device pci 1a.0 off end # eMMC - device pci 1c.0 on end # PCI Express Port 1 + device pci 1c.0 on + chip drivers/thunderbolt + device pci 00.0 on end # Thunderbolt 3 NHI + device pci 01.0 on end # Thunderbolt 3 PCI bridge + device pci 02.0 on end # Thunderbolt 3 USB controller + end + end # PCI Express Port 1 device pci 1c.1 off end # PCI Express Port 2 device pci 1c.2 off end # PCI Express Port 3 device pci 1c.3 off end # PCI Express Port 4 diff --git a/src/mainboard/system76/cml-u/ramstage.c b/src/mainboard/system76/cml-u/ramstage.c index 4aebe3b426..6463343418 100644 --- a/src/mainboard/system76/cml-u/ramstage.c +++ b/src/mainboard/system76/cml-u/ramstage.c @@ -13,6 +13,9 @@ * GNU General Public License for more details. */ +#include +#include +#include #include #include #include @@ -75,8 +78,128 @@ static void mainboard_init(struct device *dev) { } } +static bool mainboard_pcie_hotplug(int port_number) { + printk(BIOS_DEBUG, "system76: pcie_hotplug(%d)\n", port_number); + /* RP01 and RP05 */ + return port_number == 0 || port_number == 4; +} + +static void pcie_hotplug_generator(int port_number) +{ + int port; + int have_hotplug = 0; + + for (port = 0; port < port_number; port++) { + if (mainboard_pcie_hotplug(port)) { + have_hotplug = 1; + } + } + + if (!have_hotplug) { + return; + } + + for (port = 0; port < port_number; port++) { + if (mainboard_pcie_hotplug(port)) { + char scope_name[] = "\\_SB.PCI0.RP0x"; + scope_name[sizeof("\\_SB.PCI0.RP0x") - 2] = '1' + port; + acpigen_write_scope(scope_name); + + /* + Device (SLOT) + { + Name (_ADR, 0x00) + Method (_RMV, 0, NotSerialized) + { + Return (0x01) + } + } + */ + + acpigen_write_device("SLOT"); + + acpigen_write_name_byte("_ADR", 0x00); + + acpigen_write_method("_RMV", 0); + /* ReturnOp */ + acpigen_emit_byte (0xa4); + /* One */ + acpigen_emit_byte (0x01); + acpigen_pop_len(); + acpigen_pop_len(); + acpigen_pop_len(); + } + } + + /* Method (_L01, 0, NotSerialized) + { + If (\_SB.PCI0.RP04.HPCS) + { + Sleep (100) + Store (0x01, \_SB.PCI0.RP04.HPCS) + If (\_SB.PCI0.RP04.PDC) + { + Store (0x01, \_SB.PCI0.RP04.PDC) + Notify (\_SB.PCI0.RP04, 0x00) + } + } + } + + */ + acpigen_write_scope("\\_GPE"); + acpigen_write_method("_L01", 0); + for (port = 0; port < port_number; port++) { + if (mainboard_pcie_hotplug(port)) { + char reg_name[] = "\\_SB.PCI0.RP0x.HPCS"; + reg_name[sizeof("\\_SB.PCI0.RP0x") - 2] = '1' + port; + acpigen_emit_byte(0xa0); /* IfOp. */ + acpigen_write_len_f(); + acpigen_emit_namestring(reg_name); + + /* Sleep (100) */ + acpigen_emit_byte(0x5b); /* SleepOp. */ + acpigen_emit_byte(0x22); + acpigen_write_byte(100); + + /* Store (0x01, \_SB.PCI0.RP04.HPCS) */ + acpigen_emit_byte(0x70); + acpigen_emit_byte(0x01); + acpigen_emit_namestring(reg_name); + + memcpy(reg_name + sizeof("\\_SB.PCI0.RP0x.") - 1, "PDC", 4); + + /* If (\_SB.PCI0.RP04.PDC) */ + acpigen_emit_byte(0xa0); /* IfOp. */ + acpigen_write_len_f(); + acpigen_emit_namestring(reg_name); + + /* Store (0x01, \_SB.PCI0.RP04.PDC) */ + acpigen_emit_byte(0x70); + acpigen_emit_byte(0x01); + acpigen_emit_namestring(reg_name); + + reg_name[sizeof("\\_SB.PCI0.RP0x") - 1] = '\0'; + + /* Notify(\_SB.PCI0.RP04, 0x00) */ + acpigen_emit_byte(0x86); + acpigen_emit_namestring(reg_name); + acpigen_emit_byte(0x00); + acpigen_pop_len(); + acpigen_pop_len(); + } + } + acpigen_pop_len(); + acpigen_pop_len(); +} + +static void fill_ssdt(struct device *device) { + printk(BIOS_INFO, "system76: fill_ssdt\n"); + pcie_hotplug_generator(CONFIG_MAX_ROOT_PORTS); +} + static void mainboard_enable(struct device *dev) { dev->ops->init = mainboard_init; + dev->ops->acpi_fill_ssdt_generator = fill_ssdt; // Configure pad for DisplayPort uint32_t config = 0x44000200;