device/allocator: Allow for multiple domain resources of a type

Don't assume only one IO and one MEM domain resource.

Currently the code is awkward for bridge devices where loops over
resources are done twice. This would be avoided on top of other patches
that improve the allocator (topic:allocator) by adding a top-down mode.
However those patches break the tree and having the option to have
multiple resources per type would make it easier to get those patches in
without breaking the tree.

Change-Id: I3d3a60c9a4438accdb06444e2b50cc9b0b2eb009
Signed-off-by: Arthur Heymans <arthur@aheymans.xyz>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/67018
Reviewed-by: Nico Huber <nico.h@gmx.de>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Felix Held <felix-coreboot@felixheld.de>
Reviewed-by: Varshit Pandya <pandyavarshit@gmail.com>
diff --git a/src/device/resource_allocator_v4.c b/src/device/resource_allocator_v4.c
index 511c450..2f4ea12 100644
--- a/src/device/resource_allocator_v4.c
+++ b/src/device/resource_allocator_v4.c
@@ -215,14 +215,14 @@
 	}
 }
 
-static unsigned char get_alignment_by_resource_type(const struct resource *res)
+static unsigned char get_alignment_by_resource_type(const unsigned long type)
 {
-	if (res->flags & IORESOURCE_MEM)
+	if (type & IORESOURCE_MEM)
 		return 12;  /* Page-aligned --> log2(4KiB) */
-	else if (res->flags & IORESOURCE_IO)
+	else if (type & IORESOURCE_IO)
 		return 0;   /* No special alignment required --> log2(1) */
 
-	die("Unexpected resource type: flags(%lu)!\n", res->flags);
+	die("Unexpected resource type: flags(%lu)!\n", type);
 }
 
 /*
@@ -303,20 +303,31 @@
  * satisfy resource requests from downstream devices for allocations
  * above 4G.
  */
-static void initialize_domain_memranges(struct memranges *ranges, const struct resource *res,
+static void initialize_domain_memranges(const struct device *dev, struct memranges *ranges,
 					unsigned long memrange_type)
 {
-	unsigned char align = get_alignment_by_resource_type(res);
+	unsigned char align = get_alignment_by_resource_type(memrange_type);
 
 	memranges_init_empty_with_alignment(ranges, NULL, 0, align);
 
-	if (is_resource_invalid(res))
-		return;
+	struct resource *res;
+	for (res = dev->resource_list; res != NULL; res = res->next) {
+		if (is_resource_invalid(res))
+			continue;
+		if (res->flags & IORESOURCE_FIXED)
+			continue;
+		if ((res->flags & IORESOURCE_TYPE_MASK) != memrange_type)
+			continue;
 
-	if (res->flags & IORESOURCE_IO)
-		initialize_domain_io_resource_memranges(ranges, res, memrange_type);
-	else
-		initialize_domain_mem_resource_memranges(ranges, res, memrange_type);
+		printk(BIOS_DEBUG, "%s %s: base: %llx size: %llx align: %d gran: %d limit: %llx\n",
+		       dev_path(dev), resource2str(res), res->base, res->size, res->align,
+		       res->gran, res->limit);
+
+		if (res->flags & IORESOURCE_IO)
+			initialize_domain_io_resource_memranges(ranges, res, memrange_type);
+		else
+			initialize_domain_mem_resource_memranges(ranges, res, memrange_type);
+	}
 }
 
 /*
@@ -335,15 +346,22 @@
  * downstream resources of the same type under the bridge get allocated
  * above 4G.
  */
-static void initialize_bridge_memranges(struct memranges *ranges, const struct resource *res,
+static void initialize_bridge_memranges(const struct device *dev, struct memranges *ranges,
 					unsigned long memrange_type)
 {
-	unsigned char align = get_alignment_by_resource_type(res);
+	unsigned char align = get_alignment_by_resource_type(memrange_type);
 
 	memranges_init_empty_with_alignment(ranges, NULL, 0, align);
 
-	if (is_resource_invalid(res))
-		return;
+	struct resource *res;
+	for (res = dev->resource_list; res != NULL; res = res->next) {
+		if (is_resource_invalid(res))
+			continue;
+		if (res->flags & IORESOURCE_FIXED)
+			continue;
+		if ((res->flags & (IORESOURCE_TYPE_MASK | IORESOURCE_PREFETCH)) == memrange_type)
+			break;
+	}
 
 	memranges_insert(ranges, res->base, res->limit - res->base + 1, memrange_type);
 }
@@ -485,32 +503,41 @@
  * windows which cannot be used for resource allocation as fixed
  * resources.
  */
-static void setup_resource_ranges(const struct device *dev, const struct resource *res,
-				  unsigned long type, struct memranges *ranges)
+static void setup_resource_ranges(const struct device *dev, unsigned long type,
+				  struct memranges *ranges)
 {
-	printk(BIOS_DEBUG, "%s %s: base: %llx size: %llx align: %d gran: %d limit: %llx\n",
-	       dev_path(dev), resource2str(res), res->base, res->size, res->align,
-	       res->gran, res->limit);
-
 	if (dev->path.type == DEVICE_PATH_DOMAIN) {
-		initialize_domain_memranges(ranges, res, type);
+		initialize_domain_memranges(dev, ranges, type);
 		constrain_domain_resources(dev, ranges, type);
 	} else {
-		initialize_bridge_memranges(ranges, res, type);
+		initialize_bridge_memranges(dev, ranges, type);
 	}
 
 	print_resource_ranges(dev, ranges);
 }
 
-static void cleanup_resource_ranges(const struct device *dev, struct memranges *ranges,
-				    const struct resource *res)
+static void print_resource_done(const struct device *dev, const struct resource *res)
 {
-	memranges_teardown(ranges);
 	printk(BIOS_DEBUG, "%s %s: base: %llx size: %llx align: %d gran: %d limit: %llx done\n",
 	       dev_path(dev), resource2str(res), res->base, res->size, res->align,
 	       res->gran, res->limit);
 }
 
+static void cleanup_domain_resource_ranges(const struct device *dev, struct memranges *ranges,
+					   unsigned long type)
+{
+	memranges_teardown(ranges);
+	for (struct resource *res = dev->resource_list; res != NULL; res = res->next) {
+		if (is_resource_invalid(res))
+			continue;
+		if (res->flags & IORESOURCE_FIXED)
+			continue;
+		if ((res->flags & IORESOURCE_TYPE_MASK) != type)
+			continue;
+		print_resource_done(dev, res);
+	}
+}
+
 /*
  * Pass 2 of the resource allocator at the bridge level loops through
  * all the resources for the bridge and generates a list of memory
@@ -547,9 +574,10 @@
 
 		type_match = res->flags & type_mask;
 
-		setup_resource_ranges(bridge, res, type_match, &ranges);
+		setup_resource_ranges(bridge, type_match, &ranges);
 		allocate_child_resources(bus, &ranges, type_mask, type_match);
-		cleanup_resource_ranges(bridge, &ranges, res);
+		print_resource_done(bridge, res);
+		memranges_teardown(&ranges);
 	}
 
 	for (child = bus->children; child; child = child->sibling) {
@@ -560,22 +588,6 @@
 	}
 }
 
-static const struct resource *find_domain_resource(const struct device *domain,
-						   unsigned long type)
-{
-	const struct resource *res;
-
-	for (res = domain->resource_list; res; res = res->next) {
-		if (res->flags & IORESOURCE_FIXED)
-			continue;
-
-		if ((res->flags & IORESOURCE_TYPE_MASK) == type)
-			return res;
-	}
-
-	return NULL;
-}
-
 /*
  * Pass 2 of resource allocator begins at the domain level. Every domain
  * has two types of resources - io and mem. For each of these resources,
@@ -593,16 +605,12 @@
 {
 	struct memranges ranges;
 	struct device *child;
-	const struct resource *res;
 
 	/* Resource type I/O */
-	res = find_domain_resource(domain, IORESOURCE_IO);
-	if (res) {
-		setup_resource_ranges(domain, res, IORESOURCE_IO, &ranges);
-		allocate_child_resources(domain->link_list, &ranges, IORESOURCE_TYPE_MASK,
-					 IORESOURCE_IO);
-		cleanup_resource_ranges(domain, &ranges, res);
-	}
+	setup_resource_ranges(domain, IORESOURCE_IO, &ranges);
+	allocate_child_resources(domain->link_list, &ranges, IORESOURCE_TYPE_MASK,
+				 IORESOURCE_IO);
+	cleanup_domain_resource_ranges(domain, &ranges, IORESOURCE_IO);
 
 	/*
 	 * Resource type Mem:
@@ -621,17 +629,14 @@
 	 * the 4G boundary are handled separately by setting the type_mask and
 	 * type_match to allocate_child_resources() accordingly.
 	 */
-	res = find_domain_resource(domain, IORESOURCE_MEM);
-	if (res) {
-		setup_resource_ranges(domain, res, IORESOURCE_MEM, &ranges);
-		allocate_child_resources(domain->link_list, &ranges,
-					 IORESOURCE_TYPE_MASK | IORESOURCE_ABOVE_4G,
-					 IORESOURCE_MEM);
-		allocate_child_resources(domain->link_list, &ranges,
-					 IORESOURCE_TYPE_MASK | IORESOURCE_ABOVE_4G,
-					 IORESOURCE_MEM | IORESOURCE_ABOVE_4G);
-		cleanup_resource_ranges(domain, &ranges, res);
-	}
+	setup_resource_ranges(domain, IORESOURCE_MEM, &ranges);
+	allocate_child_resources(domain->link_list, &ranges,
+				 IORESOURCE_TYPE_MASK | IORESOURCE_ABOVE_4G,
+				 IORESOURCE_MEM);
+	allocate_child_resources(domain->link_list, &ranges,
+				 IORESOURCE_TYPE_MASK | IORESOURCE_ABOVE_4G,
+				 IORESOURCE_MEM | IORESOURCE_ABOVE_4G);
+	cleanup_domain_resource_ranges(domain, &ranges, IORESOURCE_MEM);
 
 	for (child = domain->link_list->children; child; child = child->sibling) {
 		if (!dev_has_children(child))