From 662da6cf7b181ea2787ba001d9cbb6d41916abec Mon Sep 17 00:00:00 2001 From: Andrey Petrov Date: Mon, 16 Mar 2020 22:46:57 -0700 Subject: soc/intel/xeon_sp: Refactor code to allow for additional CPUs types Refactor the code and split it into Xeon common and CPU-specific code. Move most Skylake-SP code into skx/ and keep common code in the current folder. This is a preparation for future work that will enable next generation server CPU. TEST=Tested on OCP Tioga Pass. There does not seem to be degradation of stability as far as I could tell. Signed-off-by: Andrey Petrov Change-Id: I448e6cfd6a85efb83d132ad26565557fe55a265a Reviewed-on: https://review.coreboot.org/c/coreboot/+/39601 Reviewed-by: David Hendricks Tested-by: build bot (Jenkins) --- src/soc/intel/xeon_sp/skx/chip.c | 601 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 601 insertions(+) create mode 100644 src/soc/intel/xeon_sp/skx/chip.c (limited to 'src/soc/intel/xeon_sp/skx/chip.c') diff --git a/src/soc/intel/xeon_sp/skx/chip.c b/src/soc/intel/xeon_sp/skx/chip.c new file mode 100644 index 0000000000..7a737ac2b4 --- /dev/null +++ b/src/soc/intel/xeon_sp/skx/chip.c @@ -0,0 +1,601 @@ +/* + * This file is part of the coreboot project. + * + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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 + +struct pci_resource { + struct device *dev; + struct resource *res; + struct pci_resource *next; +}; + +struct stack_dev_resource { + uint8_t align; + struct pci_resource *children; + struct stack_dev_resource *next; +}; + +static void assign_stack_resources(struct iiostack_resource *stack_list, + struct device *dev, struct resource *bridge); + +static void xeonsp_pci_domain_scan_bus(struct device *dev) +{ + DEV_FUNC_ENTER(dev); + struct bus *link = dev->link_list; + + printk(BIOS_SPEW, "%s:%s scanning buses under device %s\n", + __FILE__, __func__, dev_path(dev)); + while (link != NULL) { + if (link->secondary == 0) { // scan only PSTACK buses + struct device *d; + for (d = link->children; d; d = d->sibling) + pci_probe_dev(d, link, d->path.pci.devfn); + scan_bridges(link); + } else { + pci_scan_bus(link, PCI_DEVFN(0, 0), 0xff); + } + link = link->next; + } + DEV_FUNC_EXIT(dev); +} + +static void xeonsp_pci_dev_iterator(struct bus *bus, + void (*dev_iterator)(struct device *, void *), + void (*res_iterator)(struct device *, struct resource *, void *), + void *data) +{ + struct device *curdev; + struct resource *res; + + /* Walk through all devices and find which resources they need. */ + for (curdev = bus->children; curdev; curdev = curdev->sibling) { + struct bus *link; + + if (!curdev->enabled) + continue; + + if (!curdev->ops || !curdev->ops->read_resources) { + if (curdev->path.type != DEVICE_PATH_APIC) + printk(BIOS_ERR, "%s missing read_resources\n", + dev_path(curdev)); + continue; + } + + if (dev_iterator) + dev_iterator(curdev, data); + + if (res_iterator) { + for (res = curdev->resource_list; res; res = res->next) + res_iterator(curdev, res, data); + } + + /* Read in the resources behind the current device's links. */ + for (link = curdev->link_list; link; link = link->next) + xeonsp_pci_dev_iterator(link, dev_iterator, res_iterator, data); + } +} + +static void xeonsp_pci_dev_read_resources(struct device *dev, void *data) +{ + post_log_path(dev); + dev->ops->read_resources(dev); +} + +static void xeonsp_pci_dev_dummy_func(struct device *dev) +{ +} + +static void xeonsp_reset_pci_op(struct device *dev, void *data) +{ + if (dev->ops) + dev->ops->read_resources = xeonsp_pci_dev_dummy_func; +} + +static STACK_RES *find_stack_for_bus(struct iiostack_resource *info, uint8_t bus) +{ + for (int i = 0; i < info->no_of_stacks; ++i) { + if (bus >= info->res[i].BusBase && bus <= info->res[i].BusLimit) + return &info->res[i]; + } + return NULL; +} + +static void add_res_to_stack(struct stack_dev_resource **root, + struct device *dev, struct resource *res) +{ + struct stack_dev_resource *cur = *root; + while (cur) { + if (cur->align == res->align || cur->next == NULL) /* equal or last record */ + break; + else if (cur->align > res->align) { + if (cur->next->align < res->align) /* need to insert new record here */ + break; + cur = cur->next; + } else { + break; + } + } + + struct stack_dev_resource *nr; + if (!cur || cur->align != res->align) { /* need to add new record */ + nr = malloc(sizeof(struct stack_dev_resource)); + if (nr == 0) + die("assign_resource_to_stack(): out of memory.\n"); + memset(nr, 0, sizeof(struct stack_dev_resource)); + nr->align = res->align; + if (!cur) { + *root = nr; /* head node */ + } else if (cur->align > nr->align) { + if (cur->next == NULL) { + cur->next = nr; + } else { + nr->next = cur->next; + cur->next = nr; + } + } else { /* insert in the beginning */ + nr->next = cur; + *root = nr; + } + } else { + nr = cur; + } + + assert(nr != NULL && nr->align == res->align); + + struct pci_resource *npr = malloc(sizeof(struct pci_resource)); + if (npr == NULL) + die("%s: out of memory.\n", __func__); + npr->res = res; + npr->dev = dev; + npr->next = NULL; + + if (nr->children == NULL) { + nr->children = npr; + } else { + struct pci_resource *pr = nr->children; + while (pr->next != NULL) + pr = pr->next; + pr->next = npr; + } +} + +static void reserve_dev_resources(STACK_RES *stack, unsigned long res_type, + struct stack_dev_resource *res_root, struct resource *bridge) +{ + uint8_t align; + uint64_t orig_base, base; + + if (res_type & IORESOURCE_IO) + orig_base = stack->PciResourceIoBase; + else if ((res_type & IORESOURCE_MEM) && ((res_type & IORESOURCE_PCI64) || + (!res_root && bridge && (bridge->flags & IORESOURCE_PREFETCH)))) + orig_base = stack->PciResourceMem64Base; + else + orig_base = stack->PciResourceMem32Base; + + align = 0; + base = orig_base; + int first = 1; + while (res_root) { /* loop through all devices grouped by alignment requirements */ + struct pci_resource *pr = res_root->children; + while (pr) { + if (first) { + if (bridge) { /* takes highest alignment */ + if (bridge->align < pr->res->align) + bridge->align = pr->res->align; + orig_base = ALIGN_UP(orig_base, 1 << bridge->align); + } else { + orig_base = ALIGN_UP(orig_base, 1 << pr->res->align); + } + base = orig_base; + + if (bridge) + bridge->base = base; + pr->res->base = base; + first = 0; + } else { + pr->res->base = ALIGN_UP(base, 1 << pr->res->align); + } + pr->res->limit = pr->res->base + pr->res->size - 1; + base = pr->res->limit + 1; + pr->res->flags |= (IORESOURCE_ASSIGNED); + pr = pr->next; + } + res_root = res_root->next; + } + + if (bridge) { + /* this bridge doesn't have any resources, will set it to default window */ + if (first) { + orig_base = ALIGN_UP(orig_base, 1 << bridge->align); + bridge->base = orig_base; + base = orig_base + (1ULL << bridge->gran); + } + + bridge->size = ALIGN_UP(base, 1 << bridge->align) - bridge->base; + + bridge->limit = bridge->base + bridge->size - 1; + bridge->flags |= (IORESOURCE_ASSIGNED); + base = bridge->limit + 1; + } + + /* update new limits */ + if (res_type & IORESOURCE_IO) + stack->PciResourceIoBase = base; + else if ((res_type & IORESOURCE_MEM) && ((res_type & IORESOURCE_PCI64) || + (!res_root && bridge && (bridge->flags & IORESOURCE_PREFETCH)))) + stack->PciResourceMem64Base = base; + else + stack->PciResourceMem32Base = base; +} + +static void reclaim_resource_mem(struct stack_dev_resource *res_root) +{ + while (res_root) { /* loop through all devices grouped by alignment requirements */ + /* free pci_resource */ + struct pci_resource *pr = res_root->children; + while (pr) { + struct pci_resource *dpr = pr; + pr = pr->next; + free(dpr); + } + + /* free stack_dev_resource */ + struct stack_dev_resource *ddr = res_root; + res_root = res_root->next; + free(ddr); + } +} + +static void assign_bridge_resources(struct iiostack_resource *stack_list, + struct device *dev, struct resource *bridge) +{ + struct resource *res; + if (!dev->enabled) + return; + + for (res = dev->resource_list; res; res = res->next) { + if (!(res->flags & IORESOURCE_BRIDGE) || + (bridge && ((bridge->flags & (IORESOURCE_IO | IORESOURCE_MEM | + IORESOURCE_PREFETCH | IORESOURCE_PCI64)) != + (res->flags & (IORESOURCE_IO | IORESOURCE_MEM | + IORESOURCE_PREFETCH | IORESOURCE_PCI64))))) + continue; + + assign_stack_resources(stack_list, dev, res); + if (!bridge) + continue; + /* for 1st time update, overlading IORESOURCE_ASSIGNED */ + if (!(bridge->flags & IORESOURCE_ASSIGNED)) { + bridge->base = res->base; + bridge->limit = res->limit; + bridge->flags |= (IORESOURCE_ASSIGNED); + } else { + /* update bridge range from child bridge range */ + if (res->base < bridge->base) + bridge->base = res->base; + if (res->limit > bridge->limit) + bridge->limit = res->limit; + } + bridge->size = (bridge->limit - bridge->base + 1); + } +} + +static void assign_stack_resources(struct iiostack_resource *stack_list, + struct device *dev, struct resource *bridge) +{ + struct bus *bus; + + /* Read in the resources behind the current device's links. */ + for (bus = dev->link_list; bus; bus = bus->next) { + struct device *curdev; + STACK_RES *stack; + + /* get IIO stack for this bus */ + stack = find_stack_for_bus(stack_list, bus->secondary); + assert(stack != NULL); + + /* Assign resources to bridge */ + for (curdev = bus->children; curdev; curdev = curdev->sibling) + assign_bridge_resources(stack_list, curdev, bridge); + + /* Pick non-bridged resources for resource allocation for each resource type */ + unsigned long flags[5] = {IORESOURCE_IO, IORESOURCE_MEM, + (IORESOURCE_PCI64|IORESOURCE_MEM), (IORESOURCE_MEM|IORESOURCE_PREFETCH), + (IORESOURCE_PCI64|IORESOURCE_MEM|IORESOURCE_PREFETCH)}; + uint8_t no_res_types = 5; + if (bridge) { + flags[0] = bridge->flags & + (IORESOURCE_IO | IORESOURCE_MEM | IORESOURCE_PREFETCH); + if ((bridge->flags & IORESOURCE_MEM) && + (bridge->flags & IORESOURCE_PREFETCH)) + flags[0] |= IORESOURCE_PCI64; + no_res_types = 1; + } + + /* Process each resource type */ + for (int rt = 0; rt < no_res_types; ++rt) { + struct stack_dev_resource *res_root = NULL; + + for (curdev = bus->children; curdev; curdev = curdev->sibling) { + struct resource *res; + if (!curdev->enabled) + continue; + + for (res = curdev->resource_list; res; res = res->next) { + if ((res->flags & IORESOURCE_BRIDGE) || (res->flags & + (IORESOURCE_STORED | IORESOURCE_RESERVE | + IORESOURCE_FIXED | IORESOURCE_ASSIGNED) + ) || ((res->flags & (IORESOURCE_IO | + IORESOURCE_MEM | IORESOURCE_PCI64 + | IORESOURCE_PREFETCH)) + != flags[rt]) || res->size == 0) + continue; + else + add_res_to_stack(&res_root, curdev, res); + } + } + + /* Allocate resources and update bridge range */ + if (res_root || (bridge && !(bridge->flags & IORESOURCE_ASSIGNED))) { + reserve_dev_resources(stack, flags[rt], res_root, bridge); + reclaim_resource_mem(res_root); + } + } + } +} + +static void xeonsp_constrain_pci_resources(struct device *dev, struct resource *res, void *data) +{ + STACK_RES *stack = (STACK_RES *) data; + if (!(res->flags & IORESOURCE_FIXED)) + return; + + uint64_t base, limit; + if (res->flags & IORESOURCE_IO) { + base = stack->PciResourceIoBase; + limit = stack->PciResourceIoLimit; + } else if ((res->flags & IORESOURCE_MEM) && (res->flags & IORESOURCE_PCI64)) { + base = stack->PciResourceMem64Base; + limit = stack->PciResourceMem64Limit; + } else { + base = stack->PciResourceMem32Base; + limit = stack->PciResourceMem32Limit; + } + + if (((res->base + res->size - 1) < base) || (res->base > limit)) /* outside window */ + return; + + if (res->limit > limit) /* resource end is out of limit */ + limit = res->base - 1; + else + base = res->base + res->size; + + if (res->flags & IORESOURCE_IO) { + stack->PciResourceIoBase = base; + stack->PciResourceIoLimit = limit; + } else if ((res->flags & IORESOURCE_MEM) && (res->flags & IORESOURCE_PCI64)) { + stack->PciResourceMem64Base = base; + stack->PciResourceMem64Limit = limit; + } else { + stack->PciResourceMem32Base = base; + stack->PciResourceMem32Limit = limit; + } +} + +static void xeonsp_pci_domain_read_resources(struct device *dev) +{ + struct bus *link; + + DEV_FUNC_ENTER(dev); + + pci_domain_read_resources(dev); + + /* + * Walk through all devices in this domain and read resources. + * Since there is no callback when read resource operation is + * complete for all devices, domain read resource function initiates + * read resources for all devices and swaps read resource operation + * with dummy function to avoid warning. + */ + for (link = dev->link_list; link; link = link->next) + xeonsp_pci_dev_iterator(link, xeonsp_pci_dev_read_resources, NULL, NULL); + + for (link = dev->link_list; link; link = link->next) + xeonsp_pci_dev_iterator(link, xeonsp_reset_pci_op, NULL, NULL); + + /* + * 1. group devices, resources for each stack + * 2. order resources in descending order of requested resource allocation sizes + */ + struct iiostack_resource stack_info = {0}; + get_iiostack_info(&stack_info); + + /* constrain stack window */ + for (link = dev->link_list; link; link = link->next) { + STACK_RES *stack = find_stack_for_bus(&stack_info, link->secondary); + assert(stack != 0); + xeonsp_pci_dev_iterator(link, NULL, xeonsp_constrain_pci_resources, stack); + } + + /* assign resources */ + assign_stack_resources(&stack_info, dev, NULL); + + DEV_FUNC_EXIT(dev); +} + +static void reset_resource_to_unassigned(struct device *dev, struct resource *res, void *data) +{ + if ((res->flags & (IORESOURCE_IO | IORESOURCE_MEM)) && + !(res->flags & (IORESOURCE_FIXED | IORESOURCE_RESERVE))) { + res->flags &= ~IORESOURCE_ASSIGNED; + } +} + +static void xeonsp_pci_domain_set_resources(struct device *dev) +{ + DEV_FUNC_ENTER(dev); + + print_resource_tree(dev, BIOS_SPEW, "Before xeonsp pci domain set resource"); + + /* reset bus 0 dev resource assignment - need to change them to FSP IIOStack window */ + xeonsp_pci_dev_iterator(dev->link_list, NULL, reset_resource_to_unassigned, NULL); + + /* update dev resources based on IIOStack IO/Mem32/Mem64 windows */ + xeonsp_pci_domain_read_resources(dev); + + struct bus *link = dev->link_list; + while (link != NULL) { + assign_resources(link); + link = link->next; + } + + print_resource_tree(dev, BIOS_SPEW, "After xeonsp pci domain set resource"); + + DEV_FUNC_EXIT(dev); +} + +static struct device_operations pci_domain_ops = { + .read_resources = &pci_domain_read_resources, + .set_resources = &xeonsp_pci_domain_set_resources, + .scan_bus = &xeonsp_pci_domain_scan_bus, +#if CONFIG(HAVE_ACPI_TABLES) + .write_acpi_tables = &northbridge_write_acpi_tables, +#endif +}; + +static struct device_operations cpu_bus_ops = { + .read_resources = DEVICE_NOOP, + .set_resources = DEVICE_NOOP, + .enable_resources = DEVICE_NOOP, + .init = xeon_sp_init_cpus, + .scan_bus = NULL, +#if CONFIG(HAVE_ACPI_TABLES) + /* defined in src/soc/intel/common/block/acpi/acpi.c */ + .acpi_fill_ssdt_generator = generate_cpu_entries, +#endif +}; + +/* Attach IIO stack bus numbers with dummy device to PCI DOMAIN 0000 device */ +static void attach_iio_stacks(struct device *dev) +{ + struct bus *iiostack_bus; + struct device dummy; + struct iiostack_resource stack_info = {0}; + + DEV_FUNC_ENTER(dev); + + get_iiostack_info(&stack_info); + for (int s = 0; s < stack_info.no_of_stacks; ++s) { + /* only non zero bus no. needs to be enumerated */ + if (stack_info.res[s].BusBase == 0) + continue; + + iiostack_bus = malloc(sizeof(struct bus)); + if (iiostack_bus == NULL) + die("%s: out of memory.\n", __func__); + memset(iiostack_bus, 0, sizeof(*iiostack_bus)); + memcpy(iiostack_bus, dev->bus, sizeof(*iiostack_bus)); + iiostack_bus->secondary = stack_info.res[s].BusBase; + iiostack_bus->subordinate = stack_info.res[s].BusBase; + iiostack_bus->dev = NULL; + iiostack_bus->children = NULL; + iiostack_bus->next = NULL; + iiostack_bus->link_num = 1; + + dummy.bus = iiostack_bus; + dummy.path.type = DEVICE_PATH_PCI; + dummy.path.pci.devfn = 0; + uint32_t id = pci_read_config32(&dummy, PCI_VENDOR_ID); + if (id == 0xffffffff) + printk(BIOS_WARNING, "IIO Stack device %s not visible\n", + dev_path(&dummy)); + + if (dev->link_list == NULL) { + dev->link_list = iiostack_bus; + } else { + struct bus *nlink = dev->link_list; + while (nlink->next != NULL) + nlink = nlink->next; + nlink->next = iiostack_bus; + } + } + + DEV_FUNC_EXIT(dev); +} + +static void soc_enable_dev(struct device *dev) +{ + /* Set the operations if it is a special bus type */ + if (dev->path.type == DEVICE_PATH_DOMAIN) { + dev->ops = &pci_domain_ops; + attach_iio_stacks(dev); + } else if (dev->path.type == DEVICE_PATH_CPU_CLUSTER) { + dev->ops = &cpu_bus_ops; + } +} + +static void soc_init(void *data) +{ + printk(BIOS_DEBUG, "coreboot: calling fsp_silicon_init\n"); + fsp_silicon_init(false); +} + +static void soc_final(void *data) +{ + // Temp Fix - should be done by FSP, in 2S bios completion + // is not carried out on socket 2 + set_bios_init_completion(); +} + +static void soc_silicon_init_params(FSPS_UPD *silupd) +{ +} + +void platform_fsp_silicon_init_params_cb(FSPS_UPD *silupd) +{ + const struct microcode *microcode_file; + size_t microcode_len; + + microcode_file = cbfs_boot_map_with_leak("cpu_microcode_blob.bin", + CBFS_TYPE_MICROCODE, µcode_len); + + if ((microcode_file != NULL) && (microcode_len != 0)) { + /* Update CPU Microcode patch base address/size */ + silupd->FspsConfig.PcdCpuMicrocodePatchBase = + (uint32_t)microcode_file; + silupd->FspsConfig.PcdCpuMicrocodePatchSize = + (uint32_t)microcode_len; + } + + soc_silicon_init_params(silupd); + mainboard_silicon_init_params(silupd); +} + +struct chip_operations soc_intel_xeon_sp_skx_ops = { + CHIP_NAME("Intel Skylake-SP") + .enable_dev = soc_enable_dev, + .init = soc_init, + .final = soc_final +}; + +struct pci_operations soc_pci_ops = { + .set_subsystem = pci_dev_set_subsystem, +}; -- cgit v1.2.3