/* SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #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; }; typedef enum { RES_TYPE_IO = 0, RES_TYPE_NONPREF_MEM, RES_TYPE_PREF_MEM, MAX_RES_TYPES } RES_TYPE; static RES_TYPE get_res_type(uint64_t flags) { if (flags & IORESOURCE_IO) return RES_TYPE_IO; if (flags & IORESOURCE_MEM) { if (flags & IORESOURCE_PREFETCH) { printk(BIOS_DEBUG, "%s:%d flags: 0x%llx\n", __func__, __LINE__, flags); return RES_TYPE_PREF_MEM; } /* both 64-bit and 32-bit use below 4GB address space */ return RES_TYPE_NONPREF_MEM; } die("Invalid resource type 0x%llx\n", flags); } static bool need_assignment(uint64_t flags) { if (flags & (IORESOURCE_STORED | IORESOURCE_RESERVE | IORESOURCE_FIXED | IORESOURCE_ASSIGNED)) return false; else return true; } static uint64_t get_resource_base(STACK_RES *stack, RES_TYPE res_type) { if (res_type == RES_TYPE_IO) { assert(stack->PciResourceIoBase <= stack->PciResourceIoLimit); return stack->PciResourceIoBase; } if (res_type == RES_TYPE_NONPREF_MEM) { assert(stack->PciResourceMem32Base <= stack->PciResourceMem32Limit); return stack->PciResourceMem32Base; } assert(stack->PciResourceMem64Base <= stack->PciResourceMem64Limit); return stack->PciResourceMem64Base; } static void set_resource_base(STACK_RES *stack, RES_TYPE res_type, uint64_t base) { if (res_type == RES_TYPE_IO) { assert(base <= (stack->PciResourceIoLimit + 1)); stack->PciResourceIoBase = base; } else if (res_type == RES_TYPE_NONPREF_MEM) { assert(base <= (stack->PciResourceMem32Limit + 1)); stack->PciResourceMem32Base = base; } else { assert(base <= (stack->PciResourceMem64Limit + 1)); stack->PciResourceMem64Base = base; } } static void assign_stack_resources(struct iiostack_resource *stack_list, struct device *dev, struct resource *bridge); 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) { 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) /* 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) { 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 && nr->align == res->align); struct pci_resource *npr = malloc(sizeof(struct pci_resource)); if (!npr) die("%s: out of memory.\n", __func__); npr->res = res; npr->dev = dev; npr->next = NULL; if (!nr->children) { nr->children = npr; } else { struct pci_resource *pr = nr->children; while (pr->next) pr = pr->next; pr->next = npr; } } static void reserve_dev_resources(STACK_RES *stack, RES_TYPE res_type, struct stack_dev_resource *res_root, struct resource *bridge) { uint64_t orig_base, base; orig_base = get_resource_base(stack, res_type); 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; } set_resource_base(stack, res_type, 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 && (get_res_type(bridge->flags) != get_res_type(res->flags)))) 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); /* 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 */ RES_TYPE res_types[MAX_RES_TYPES] = { RES_TYPE_IO, RES_TYPE_NONPREF_MEM, RES_TYPE_PREF_MEM }; uint8_t no_res_types = MAX_RES_TYPES; /* if it is a bridge, only process matching bridge resource type */ if (bridge) { res_types[0] = get_res_type(bridge->flags); no_res_types = 1; } printk(BIOS_DEBUG, "%s:%d no_res_types: %d\n", __func__, __LINE__, no_res_types); /* Process each resource type */ for (int rt = 0; rt < no_res_types; ++rt) { struct stack_dev_resource *res_root = NULL; printk(BIOS_DEBUG, "%s:%d rt: %d\n", __func__, __LINE__, rt); for (curdev = bus->children; curdev; curdev = curdev->sibling) { struct resource *res; printk(BIOS_DEBUG, "%s:%d dev: %s\n", __func__, __LINE__, dev_path(curdev)); if (!curdev->enabled) continue; for (res = curdev->resource_list; res; res = res->next) { printk(BIOS_DEBUG, "%s:%d dev: %s, flags: 0x%lx\n", __func__, __LINE__, dev_path(curdev), res->flags); if (res->size == 0 || get_res_type(res->flags) != res_types[rt] || (res->flags & IORESOURCE_BRIDGE) || !need_assignment(res->flags)) 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, res_types[rt], res_root, bridge); reclaim_resource_mem(res_root); } } } } static uint8_t is_pci64bit_alloc(void) { /* * For SPR-SP FSP which supports SOC_INTEL_PCIE_64BITS_ALLOC, * Pci64BitResourceAllocation field does not exist in IIO_UDS HOB. */ #if CONFIG(SOC_INTEL_PCIE_64BIT_ALLOC) return 1; #else const IIO_UDS *hob = get_iio_uds(); return hob->PlatformData.Pci64BitResourceAllocation; #endif } 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); struct iiostack_resource stack_info = {0}; get_iiostack_info(&stack_info); if (!is_pci64bit_alloc()) { /* * Split 32 bit address space between prefetchable and * non-prefetchable windows */ for (int s = 0; s < stack_info.no_of_stacks; ++s) { STACK_RES *res = &stack_info.res[s]; uint64_t length = (res->PciResourceMem32Limit - res->PciResourceMem32Base + 1)/2; res->PciResourceMem64Limit = res->PciResourceMem32Limit; res->PciResourceMem32Limit = (res->PciResourceMem32Base + length - 1); res->PciResourceMem64Base = res->PciResourceMem32Limit + 1; } } /* 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; } } 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) { assign_resources(link); link = link->next; } print_resource_tree(dev, BIOS_SPEW, "After xeonsp pci domain set resource"); DEV_FUNC_EXIT(dev); } /* Attach IIO stack bus numbers with dummy device to PCI DOMAIN 0000 device */ void attach_iio_stacks(struct device *dev) { 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) { STACK_RES *sr = &stack_info.res[s]; /* only non zero bus no. needs to be enumerated */ if (sr->BusBase == 0) { /* Update BUS 0 BusLimit */ dev->link_list->max_subordinate = sr->BusLimit; continue; } for (int b = sr->BusBase; b <= sr->BusLimit; ++b) { struct bus tmp_bus; memset(&tmp_bus, 0, sizeof(tmp_bus)); memcpy(&tmp_bus, dev->bus, sizeof(tmp_bus)); tmp_bus.secondary = b; tmp_bus.subordinate = b; tmp_bus.max_subordinate = sr->BusLimit; tmp_bus.dev = NULL; tmp_bus.children = NULL; tmp_bus.next = NULL; tmp_bus.link_num = 1; dummy.bus = &tmp_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_DEBUG, "IIO Stack device %s not visible\n", dev_path(&dummy)); continue; } printk(BIOS_DEBUG, "%s Attaching IIO Bus %s\n", __func__, dev_path(&dummy)); printk(BIOS_DEBUG, " %s attach secondary: 0x%x, subordinate: 0x%x, dev: %s\n", __func__, tmp_bus.secondary, tmp_bus.subordinate, dev_path(&dummy)); struct bus *iiostack_bus = malloc(sizeof(struct bus)); if (iiostack_bus == NULL) die("%s: out of memory.\n", __func__); memcpy(iiostack_bus, &tmp_bus, sizeof(*iiostack_bus)); 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); }