diff mbox series

[v2,07/35] gpio: Add a method to convert a GPIO to ACPI

Message ID 20200510143320.v2.7.I29adaf758a9ea81f51ffbecf89b53983968a0d2a@changeid
State Accepted
Commit 2912686c08c33aff5269512de962dffb35fbee7c
Headers show
Series dm: Add programmatic generation of ACPI tables (part B) | expand

Commit Message

Simon Glass May 10, 2020, 8:33 p.m. UTC
When generating ACPI tables we need to convert GPIOs in U-Boot to the ACPI
structures required by ACPI. This is a SoC-specific conversion and cannot
be handled by generic code, so add a new GPIO method to do the conversion.

Signed-off-by: Simon Glass <sjg at chromium.org>
Reviewed-by: Wolfgang Wallner <wolfgang.wallner at br-automation.com>
---

Changes in v2: None
Changes in v1:
- Update sandbox driver slightly for testing

 drivers/gpio/gpio-uclass.c | 21 +++++++++
 drivers/gpio/sandbox.c     | 86 ++++++++++++++++++++++++++++++++++++
 include/acpi/acpi_device.h | 90 ++++++++++++++++++++++++++++++++++++++
 include/asm-generic/gpio.h | 27 ++++++++++++
 test/dm/gpio.c             | 62 ++++++++++++++++++++++++++
 test/dm/irq.c              |  1 +
 6 files changed, 287 insertions(+)
diff mbox series

Patch

diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c
index aaaeb5940b..d50e677947 100644
--- a/drivers/gpio/gpio-uclass.c
+++ b/drivers/gpio/gpio-uclass.c
@@ -13,6 +13,7 @@ 
 #include <errno.h>
 #include <fdtdec.h>
 #include <malloc.h>
+#include <acpi/acpi_device.h>
 #include <asm/gpio.h>
 #include <dm/device_compat.h>
 #include <linux/bug.h>
@@ -794,6 +795,26 @@  int gpio_get_status(struct udevice *dev, int offset, char *buf, int buffsize)
 	return 0;
 }
 
+#if CONFIG_IS_ENABLED(ACPIGEN)
+int gpio_get_acpi(const struct gpio_desc *desc, struct acpi_gpio *gpio)
+{
+	struct dm_gpio_ops *ops;
+
+	if (!dm_gpio_is_valid(desc)) {
+		/* Indicate that the GPIO is not valid */
+		gpio->pin_count = 0;
+		gpio->pins[0] = 0;
+		return -EINVAL;
+	}
+
+	ops = gpio_get_ops(desc->dev);
+	if (!ops->get_acpi)
+		return -ENOSYS;
+
+	return ops->get_acpi(desc, gpio);
+}
+#endif
+
 int gpio_claim_vector(const int *gpio_num_array, const char *fmt)
 {
 	int i, ret;
diff --git a/drivers/gpio/sandbox.c b/drivers/gpio/sandbox.c
index 98b7fa4bb3..6cd2a1455a 100644
--- a/drivers/gpio/sandbox.c
+++ b/drivers/gpio/sandbox.c
@@ -8,7 +8,9 @@ 
 #include <fdtdec.h>
 #include <log.h>
 #include <malloc.h>
+#include <acpi/acpi_device.h>
 #include <asm/gpio.h>
+#include <dm/acpi.h>
 #include <dm/device_compat.h>
 #include <dm/lists.h>
 #include <dm/of.h>
@@ -197,6 +199,72 @@  static int sb_gpio_get_dir_flags(struct udevice *dev, unsigned int offset,
 	return 0;
 }
 
+#if CONFIG_IS_ENABLED(ACPIGEN)
+static int sb_gpio_get_acpi(const struct gpio_desc *desc,
+			    struct acpi_gpio *gpio)
+{
+	int ret;
+
+	/* All of these values are just used for testing */
+	if (desc->flags & GPIOD_ACTIVE_LOW) {
+		gpio->pin_count = 1;
+		gpio->pins[0] = desc->offset;
+		gpio->pin0_addr = 0x80012 + desc->offset;
+		gpio->type = ACPI_GPIO_TYPE_INTERRUPT;
+		gpio->pull = ACPI_GPIO_PULL_DOWN;
+		ret = acpi_device_scope(desc->dev, gpio->resource,
+					sizeof(gpio->resource));
+		if (ret)
+			return log_ret(ret);
+		gpio->interrupt_debounce_timeout = 4321;
+		memset(&gpio->irq, '\0', sizeof(gpio->irq));
+
+		/* We use the GpioInt part */
+		gpio->irq.pin = desc->offset;
+		gpio->irq.polarity = ACPI_IRQ_ACTIVE_BOTH;
+		gpio->irq.shared = ACPI_IRQ_SHARED;
+		gpio->irq.wake = ACPI_IRQ_WAKE;
+
+		/* The GpioIo part is not used */
+		gpio->output_drive_strength = 0;
+		gpio->io_shared = false;
+		gpio->io_restrict = 0;
+		gpio->polarity = ACPI_GPIO_ACTIVE_LOW;
+	} else {
+		gpio->pin_count = 1;
+		gpio->pins[0] = desc->offset;
+		gpio->pin0_addr = 0xc00dc + desc->offset;
+		gpio->type = ACPI_GPIO_TYPE_IO;
+		gpio->pull = ACPI_GPIO_PULL_UP;
+		ret = acpi_device_scope(desc->dev, gpio->resource,
+					sizeof(gpio->resource));
+		if (ret)
+			return log_ret(ret);
+		gpio->interrupt_debounce_timeout = 0;
+
+		/* The GpioInt part is not used */
+		memset(&gpio->irq, '\0', sizeof(gpio->irq));
+
+		/* We use the GpioIo part */
+		gpio->output_drive_strength = 1234;
+		gpio->io_shared = true;
+		gpio->io_restrict = ACPI_GPIO_IO_RESTRICT_INPUT;
+		gpio->polarity = 0;
+	}
+
+	return 0;
+}
+
+static int sb_gpio_get_name(const struct udevice *dev, char *out_name)
+{
+	return acpi_copy_name(out_name, "GPIO");
+}
+
+struct acpi_ops gpio_sandbox_acpi_ops = {
+	.get_name	= sb_gpio_get_name,
+};
+#endif /* ACPIGEN */
+
 static const struct dm_gpio_ops gpio_sandbox_ops = {
 	.direction_input	= sb_gpio_direction_input,
 	.direction_output	= sb_gpio_direction_output,
@@ -206,6 +274,9 @@  static const struct dm_gpio_ops gpio_sandbox_ops = {
 	.xlate			= sb_gpio_xlate,
 	.set_dir_flags		= sb_gpio_set_dir_flags,
 	.get_dir_flags		= sb_gpio_get_dir_flags,
+#if CONFIG_IS_ENABLED(ACPIGEN)
+	.get_acpi		= sb_gpio_get_acpi,
+#endif
 };
 
 static int sandbox_gpio_ofdata_to_platdata(struct udevice *dev)
@@ -252,6 +323,7 @@  U_BOOT_DRIVER(gpio_sandbox) = {
 	.probe	= gpio_sandbox_probe,
 	.remove	= gpio_sandbox_remove,
 	.ops	= &gpio_sandbox_ops,
+	ACPI_OPS_PTR(&gpio_sandbox_acpi_ops)
 };
 
 /* pincontrol: used only to check GPIO pin configuration (pinmux command) */
@@ -419,6 +491,13 @@  static int sb_pinctrl_get_pin_muxing(struct udevice *dev,
 	return 0;
 }
 
+#if CONFIG_IS_ENABLED(ACPIGEN)
+static int sb_pinctrl_get_name(const struct udevice *dev, char *out_name)
+{
+	return acpi_copy_name(out_name, "PINC");
+}
+#endif
+
 static int sandbox_pinctrl_probe(struct udevice *dev)
 {
 	struct sb_pinctrl_priv *priv = dev_get_priv(dev);
@@ -434,6 +513,12 @@  static struct pinctrl_ops sandbox_pinctrl_gpio_ops = {
 	.get_pin_muxing		= sb_pinctrl_get_pin_muxing,
 };
 
+#if CONFIG_IS_ENABLED(ACPIGEN)
+struct acpi_ops pinctrl_sandbox_acpi_ops = {
+	.get_name	= sb_pinctrl_get_name,
+};
+#endif
+
 static const struct udevice_id sandbox_pinctrl_gpio_match[] = {
 	{ .compatible = "sandbox,pinctrl-gpio" },
 	{ /* sentinel */ }
@@ -447,4 +532,5 @@  U_BOOT_DRIVER(sandbox_pinctrl_gpio) = {
 	.bind = dm_scan_fdt_dev,
 	.probe = sandbox_pinctrl_probe,
 	.priv_auto_alloc_size	= sizeof(struct sb_pinctrl_priv),
+	ACPI_OPS_PTR(&pinctrl_sandbox_acpi_ops)
 };
diff --git a/include/acpi/acpi_device.h b/include/acpi/acpi_device.h
index 4f87cd003a..cb9166aeae 100644
--- a/include/acpi/acpi_device.h
+++ b/include/acpi/acpi_device.h
@@ -92,6 +92,96 @@  struct acpi_irq {
 	enum acpi_irq_wake wake;
 };
 
+/**
+ * enum acpi_gpio_type - type of the descriptor
+ *
+ * @ACPI_GPIO_TYPE_INTERRUPT: GpioInterrupt
+ * @ACPI_GPIO_TYPE_IO: GpioIo
+ */
+enum acpi_gpio_type {
+	ACPI_GPIO_TYPE_INTERRUPT,
+	ACPI_GPIO_TYPE_IO,
+};
+
+/**
+ * enum acpi_gpio_pull - pull direction
+ *
+ * @ACPI_GPIO_PULL_DEFAULT: Use default value for pin
+ * @ACPI_GPIO_PULL_UP: Pull up
+ * @ACPI_GPIO_PULL_DOWN: Pull down
+ * @ACPI_GPIO_PULL_NONE: No pullup/pulldown
+ */
+enum acpi_gpio_pull {
+	ACPI_GPIO_PULL_DEFAULT,
+	ACPI_GPIO_PULL_UP,
+	ACPI_GPIO_PULL_DOWN,
+	ACPI_GPIO_PULL_NONE,
+};
+
+/**
+ * enum acpi_gpio_io_restrict - controls input/output of pin
+ *
+ * @ACPI_GPIO_IO_RESTRICT_NONE: no restrictions
+ * @ACPI_GPIO_IO_RESTRICT_INPUT: input only (no output)
+ * @ACPI_GPIO_IO_RESTRICT_OUTPUT: output only (no input)
+ * @ACPI_GPIO_IO_RESTRICT_PRESERVE: preserve settings when driver not active
+ */
+enum acpi_gpio_io_restrict {
+	ACPI_GPIO_IO_RESTRICT_NONE,
+	ACPI_GPIO_IO_RESTRICT_INPUT,
+	ACPI_GPIO_IO_RESTRICT_OUTPUT,
+	ACPI_GPIO_IO_RESTRICT_PRESERVE,
+};
+
+/** enum acpi_gpio_polarity - controls the GPIO polarity */
+enum acpi_gpio_polarity {
+	ACPI_GPIO_ACTIVE_HIGH = 0,
+	ACPI_GPIO_ACTIVE_LOW = 1,
+};
+
+#define ACPI_GPIO_REVISION_ID		1
+#define ACPI_GPIO_MAX_PINS		2
+
+/**
+ * struct acpi_gpio - representation of an ACPI GPIO
+ *
+ * @pin_count: Number of pins represented
+ * @pins: List of pins
+ * @pin0_addr: Address in memory of the control registers for pin 0. This is
+ *   used when generating ACPI tables
+ * @type: GPIO type
+ * @pull: Pullup/pulldown setting
+ * @resource: Resource name for this GPIO controller
+ * For GpioInt:
+ * @interrupt_debounce_timeout: Debounce timeout in units of 10us
+ * @irq: Interrupt
+ *
+ * For GpioIo:
+ * @output_drive_strength: Drive strength in units of 10uA
+ * @io_shared; true if GPIO is shared
+ * @io_restrict: I/O restriction setting
+ * @polarity: GPIO polarity
+ */
+struct acpi_gpio {
+	int pin_count;
+	u16 pins[ACPI_GPIO_MAX_PINS];
+	ulong pin0_addr;
+
+	enum acpi_gpio_type type;
+	enum acpi_gpio_pull pull;
+	char resource[ACPI_PATH_MAX];
+
+	/* GpioInt */
+	u16 interrupt_debounce_timeout;
+	struct acpi_irq irq;
+
+	/* GpioIo */
+	u16 output_drive_strength;
+	bool io_shared;
+	enum acpi_gpio_io_restrict io_restrict;
+	enum acpi_gpio_polarity polarity;
+};
+
 /**
  * acpi_device_path() - Get the full path to an ACPI device
  *
diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h
index e16c2f31d9..a57dd2665c 100644
--- a/include/asm-generic/gpio.h
+++ b/include/asm-generic/gpio.h
@@ -10,6 +10,7 @@ 
 #include <dm/ofnode.h>
 #include <linux/bitops.h>
 
+struct acpi_gpio;
 struct ofnode_phandle_args;
 
 /*
@@ -329,6 +330,20 @@  struct dm_gpio_ops {
 	 */
 	int (*get_dir_flags)(struct udevice *dev, unsigned int offset,
 			     ulong *flags);
+
+#if CONFIG_IS_ENABLED(ACPIGEN)
+	/**
+	 * get_acpi() - Get the ACPI info for a GPIO
+	 *
+	 * This converts a GPIO to an ACPI structure for adding to the ACPI
+	 * tables.
+	 *
+	 * @desc:	GPIO description to convert
+	 * @gpio:	Output ACPI GPIO information
+	 * @return ACPI pin number or -ve on error
+	 */
+	int (*get_acpi)(const struct gpio_desc *desc, struct acpi_gpio *gpio);
+#endif
 };
 
 /**
@@ -674,4 +689,16 @@  int dm_gpio_get_dir_flags(struct gpio_desc *desc, ulong *flags);
  */
 int gpio_get_number(const struct gpio_desc *desc);
 
+/**
+ * gpio_get_acpi() - Get the ACPI pin for a GPIO
+ *
+ * This converts a GPIO to an ACPI pin number for adding to the ACPI
+ * tables. If the GPIO is invalid, the pin_count and pins[0] are set to 0
+ *
+ * @desc:	GPIO description to convert
+ * @gpio:	Output ACPI GPIO information
+ * @return ACPI pin number or -ve on error
+ */
+int gpio_get_acpi(const struct gpio_desc *desc, struct acpi_gpio *gpio);
+
 #endif	/* _ASM_GENERIC_GPIO_H_ */
diff --git a/test/dm/gpio.c b/test/dm/gpio.c
index f1e14b601f..3db32baa06 100644
--- a/test/dm/gpio.c
+++ b/test/dm/gpio.c
@@ -8,6 +8,7 @@ 
 #include <dm.h>
 #include <log.h>
 #include <malloc.h>
+#include <acpi/acpi_device.h>
 #include <dm/root.h>
 #include <dm/test.h>
 #include <dm/util.h>
@@ -296,3 +297,64 @@  static int dm_test_gpio_get_dir_flags(struct unit_test_state *uts)
 	return 0;
 }
 DM_TEST(dm_test_gpio_get_dir_flags, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test of gpio_get_acpi() */
+static int dm_test_gpio_get_acpi(struct unit_test_state *uts)
+{
+	struct acpi_gpio agpio;
+	struct udevice *dev;
+	struct gpio_desc desc;
+
+	ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev));
+	ut_asserteq_str("a-test", dev->name);
+	ut_assertok(gpio_request_by_name(dev, "test-gpios", 1, &desc, 0));
+
+	/* See sb_gpio_get_acpi() */
+	ut_assertok(gpio_get_acpi(&desc, &agpio));
+	ut_asserteq(1, agpio.pin_count);
+	ut_asserteq(4, agpio.pins[0]);
+	ut_asserteq(ACPI_GPIO_TYPE_IO, agpio.type);
+	ut_asserteq(ACPI_GPIO_PULL_UP, agpio.pull);
+	ut_asserteq_str("\\_SB.PINC", agpio.resource);
+	ut_asserteq(0, agpio.interrupt_debounce_timeout);
+	ut_asserteq(0, agpio.irq.pin);
+	ut_asserteq(1234, agpio.output_drive_strength);
+	ut_asserteq(true, agpio.io_shared);
+	ut_asserteq(ACPI_GPIO_IO_RESTRICT_INPUT, agpio.io_restrict);
+	ut_asserteq(ACPI_GPIO_ACTIVE_HIGH, agpio.polarity);
+
+	return 0;
+}
+DM_TEST(dm_test_gpio_get_acpi, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
+
+/* Test of gpio_get_acpi() with an interrupt GPIO */
+static int dm_test_gpio_get_acpi_irq(struct unit_test_state *uts)
+{
+	struct acpi_gpio agpio;
+	struct udevice *dev;
+	struct gpio_desc desc;
+
+	ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev));
+	ut_asserteq_str("a-test", dev->name);
+	ut_assertok(gpio_request_by_name(dev, "test2-gpios", 2, &desc, 0));
+
+	/* See sb_gpio_get_acpi() */
+	ut_assertok(gpio_get_acpi(&desc, &agpio));
+	ut_asserteq(1, agpio.pin_count);
+	ut_asserteq(6, agpio.pins[0]);
+	ut_asserteq(ACPI_GPIO_TYPE_INTERRUPT, agpio.type);
+	ut_asserteq(ACPI_GPIO_PULL_DOWN, agpio.pull);
+	ut_asserteq_str("\\_SB.PINC", agpio.resource);
+	ut_asserteq(4321, agpio.interrupt_debounce_timeout);
+	ut_asserteq(6, agpio.irq.pin);
+	ut_asserteq(ACPI_IRQ_ACTIVE_BOTH, agpio.irq.polarity);
+	ut_asserteq(ACPI_IRQ_SHARED, agpio.irq.shared);
+	ut_asserteq(true, agpio.irq.wake);
+	ut_asserteq(0, agpio.output_drive_strength);
+	ut_asserteq(false, agpio.io_shared);
+	ut_asserteq(0, agpio.io_restrict);
+	ut_asserteq(ACPI_GPIO_ACTIVE_LOW, agpio.polarity);
+
+	return 0;
+}
+DM_TEST(dm_test_gpio_get_acpi_irq, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);
diff --git a/test/dm/irq.c b/test/dm/irq.c
index 50e505e657..51bae31b0f 100644
--- a/test/dm/irq.c
+++ b/test/dm/irq.c
@@ -87,6 +87,7 @@  static int dm_test_irq_get_acpi(struct unit_test_state *uts)
 	ut_assertok(uclass_first_device_err(UCLASS_TEST_FDT, &dev));
 	ut_assertok(irq_get_by_index(dev, 0, &irq));
 
+	/* see sandbox_get_acpi() */
 	ut_assertok(irq_get_acpi(&irq, &airq));
 	ut_asserteq(3, airq.pin);
 	ut_asserteq(ACPI_IRQ_LEVEL_TRIGGERED, airq.mode);