acpi/device: Add GPIO binding property for an array of GPIOs

This change is required for use-cases like GPIO based I2C multiplexer
where more than one GPIOs are used as select lines.

BUG=b:169444894
TEST=Build and boot waddledee to OS. Ensure that the GPIO bindings for
an array of GPIOs are added to the ACPI table as follows:
Device (MUX0)
{
    ...
    Name (_CRS, ResourceTemplate ()  // _CRS: Current Resource Settings
    {
        GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly,
                "\\_SB.PCI0.GPIO", 0x00, ResourceConsumer, ,
                )
        {   // Pin list
            0x0125
        }
        GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly,
                "\\_SB.PCI0.GPIO", 0x00, ResourceConsumer, ,
                )
        {   // Pin list
            0x0126
        }
    })
    Name (_DSD, Package (0x02)  // _DSD: Device-Specific Data
    {
        ToUUID ("daffd814-6eba-4d8c-8a91-bc9bbf4aa301") /* Device Properties for _DSD */,
                Package (0x01)
        {
            Package (0x02)
            {
                "mux-gpios",
                Package (0x08)
                {
                    \_SB.PCI0.I2C3.MUX0,
                    Zero,
                    Zero,
                    Zero,
                    \_SB.PCI0.I2C3.MUX0,
                    One,
                    Zero,
                    Zero
                }
            }
        }
    })
}

Change-Id: I7c6cc36b1bfca2d48c84f169e6b43fd4be8ba330
Signed-off-by: Karthikeyan Ramasubramanian <kramasub@google.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/46056
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Furquan Shaikh <furquan@google.com>
diff --git a/src/acpi/device.c b/src/acpi/device.c
index 450427d..5de31b7 100644
--- a/src/acpi/device.c
+++ b/src/acpi/device.c
@@ -1019,33 +1019,72 @@
 	return dp_array;
 }
 
+struct acpi_dp *acpi_dp_add_gpio_array(struct acpi_dp *dp, const char *name,
+				       const struct acpi_gpio_res_params *params,
+				       size_t param_count)
+{
+	struct acpi_dp *gpio;
+	uint32_t i;
+
+	if (!dp || !param_count)
+		return NULL;
+
+	gpio = acpi_dp_new_table(name);
+	if (!gpio)
+		return NULL;
+
+	/*
+	 * Generate ACPI identifiers as follows:
+	 * Package () {
+	 *     name,           // e.g. cs-gpios
+	 *     Package() {
+	 *           ref, index, pin, active_low, // GPIO-0 (params[0])
+	 *           ref, index, pin, active_low, // GPIO-1 (params[1])
+	 *           ...
+	 *     }
+	 * }
+	 */
+	for (i = 0; i < param_count; i++, params++) {
+		/*
+		 * If refs is NULL, leave a hole in the gpio array. This can be used in
+		 * conditions where some controllers use both GPIOs and native signals.
+		 */
+		if (!params->ref) {
+			acpi_dp_add_integer(gpio, NULL, 0);
+			continue;
+		}
+
+		/* The device that has _CRS containing GpioIO()/GpioInt() */
+		acpi_dp_add_reference(gpio, NULL, params->ref);
+
+		/* Index of the GPIO resource in _CRS starting from zero */
+		acpi_dp_add_integer(gpio, NULL, params->index);
+
+		/* Pin in the GPIO resource, typically zero */
+		acpi_dp_add_integer(gpio, NULL, params->pin);
+
+		/* Set if pin is active low */
+		acpi_dp_add_integer(gpio, NULL, params->active_low);
+	}
+	acpi_dp_add_array(dp, gpio);
+
+	return gpio;
+
+}
+
+
 struct acpi_dp *acpi_dp_add_gpio(struct acpi_dp *dp, const char *name,
 				 const char *ref, int index, int pin,
 				 int active_low)
 {
-	if (!dp)
-		return NULL;
+	struct acpi_gpio_res_params param = {
+		.ref = ref,
+		.index = index,
+		.pin = pin,
+		.active_low = active_low,
+	};
 
-	struct acpi_dp *gpio = acpi_dp_new_table(name);
-
-	if (!gpio)
-		return NULL;
-
-	/* The device that has _CRS containing GpioIO()/GpioInt() */
-	acpi_dp_add_reference(gpio, NULL, ref);
-
-	/* Index of the GPIO resource in _CRS starting from zero */
-	acpi_dp_add_integer(gpio, NULL, index);
-
-	/* Pin in the GPIO resource, typically zero */
-	acpi_dp_add_integer(gpio, NULL, pin);
-
-	/* Set if pin is active low */
-	acpi_dp_add_integer(gpio, NULL, active_low);
-
-	acpi_dp_add_array(dp, gpio);
-
-	return gpio;
+	return acpi_dp_add_gpio_array(dp, name, &param, 1);
 }
 
 /*
diff --git a/src/include/acpi/acpi_device.h b/src/include/acpi/acpi_device.h
index be13bd7..301f9b0 100644
--- a/src/include/acpi/acpi_device.h
+++ b/src/include/acpi/acpi_device.h
@@ -545,6 +545,22 @@
 				 const char *ref, int index, int pin,
 				 int active_low);
 
+struct acpi_gpio_res_params {
+	/* Reference to the parent device. */
+	const char *ref;
+	/* Index to the GpioIo resource within the _CRS. */
+	int index;
+	/* Index to the pin within the GpioIo resource, usually 0. */
+	int pin;
+	/* Flag to indicate if pin is active low. */
+	int active_low;
+};
+
+/* Add a GPIO binding device property for array of GPIOs */
+struct acpi_dp *acpi_dp_add_gpio_array(struct acpi_dp *dp, const char *name,
+				       const struct acpi_gpio_res_params *params,
+				       size_t param_count);
+
 /* Add a child table of Device Properties */
 struct acpi_dp *acpi_dp_add_child(struct acpi_dp *dp, const char *name,
 				  struct acpi_dp *child);