Module Name:    src
Committed By:   jmcneill
Date:           Thu Dec 12 12:47:57 UTC 2024

Modified Files:
        src/sys/dev/acpi: qcomgpio.c

Log Message:
qcomgpio: Translate virtual GPIO numbers using ACPI table data.

The scheme used by this device node appears to follow the following
conventions for mapping ACPI virtual pin numbers to hardware pin numbers:

1) If the virtual pin number is < the number of hardware pin numbers,
   it represents a hardware pin number.
2) If the virtual pin number is greater than this, divide it by 64. This
   gives us an index into the Interrupt resources returned by _CRS. The
   IRQ number in this resource can be used to lookup the hardware pin
   number in the _DSM PDC method call "CIPR".

As an added bonus, use the _DSM GPIO method call to return the number
of pins instead of hard-coding it for X1E.


To generate a diff of this commit:
cvs rdiff -u -r1.3 -r1.4 src/sys/dev/acpi/qcomgpio.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/dev/acpi/qcomgpio.c
diff -u src/sys/dev/acpi/qcomgpio.c:1.3 src/sys/dev/acpi/qcomgpio.c:1.4
--- src/sys/dev/acpi/qcomgpio.c:1.3	Wed Dec 11 00:59:16 2024
+++ src/sys/dev/acpi/qcomgpio.c	Thu Dec 12 12:47:57 2024
@@ -1,4 +1,4 @@
-/* $NetBSD: qcomgpio.c,v 1.3 2024/12/11 00:59:16 jmcneill Exp $ */
+/* $NetBSD: qcomgpio.c,v 1.4 2024/12/12 12:47:57 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2024 The NetBSD Foundation, Inc.
@@ -30,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: qcomgpio.c,v 1.3 2024/12/11 00:59:16 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: qcomgpio.c,v 1.4 2024/12/12 12:47:57 jmcneill Exp $");
 
 #include <sys/param.h>
 #include <sys/bus.h>
@@ -60,8 +60,6 @@ struct qcomgpio_reserved {
 };
 
 struct qcomgpio_config {
-	u_int	num_pins;
-	int	(*translate)(ACPI_RESOURCE_GPIO *);
 	struct qcomgpio_reserved *reserved;
 	u_int	num_reserved;
 };
@@ -74,6 +72,11 @@ struct qcomgpio_intr_handler {
 	LIST_ENTRY(qcomgpio_intr_handler) ih_list;
 };
 
+struct qcomgpio_pdcmap {
+	int	pm_pin;
+	u_int	pm_irq;
+};
+
 struct qcomgpio_softc {
 	device_t			sc_dev;
 	device_t			sc_gpiodev;
@@ -82,8 +85,12 @@ struct qcomgpio_softc {
 	const struct qcomgpio_config	*sc_config;
 	struct gpio_chipset_tag		sc_gc;
 	gpio_pin_t			*sc_pins;
+	u_int				sc_npins;
 	LIST_HEAD(, qcomgpio_intr_handler) sc_intrs;
 	kmutex_t			sc_lock;
+
+	struct qcomgpio_pdcmap		*sc_pdcmap;
+	u_int				sc_npdcmap;
 };
 
 #define RD4(sc, reg)		\
@@ -105,6 +112,9 @@ static bool	qcomgpio_intr_str(void *, in
 static void	qcomgpio_intr_mask(void *, void *);
 static void	qcomgpio_intr_unmask(void *, void *);
 
+static u_int	qcomgpio_acpi_num_pins(device_t, ACPI_HANDLE);
+static void	qcomgpio_acpi_fill_pdcmap(struct qcomgpio_softc *,
+					  ACPI_HANDLE);
 static int	qcomgpio_acpi_translate(void *, ACPI_RESOURCE_GPIO *, void **);
 static void	qcomgpio_register_event(void *, struct acpi_event *,
 					ACPI_RESOURCE_GPIO *);
@@ -113,7 +123,19 @@ static int	qcomgpio_intr(void *);
 CFATTACH_DECL_NEW(qcomgpio, sizeof(struct qcomgpio_softc),
     qcomgpio_match, qcomgpio_attach, NULL, NULL);
 
-#define X1E_NUM_PINS	239
+static UINT8 qcomgpio_gpio_dsm_uuid[ACPI_UUID_LENGTH] = {
+	0xa4, 0xb2, 0xb9, 0x98, 0x63, 0x16, 0x5f, 0x4a,
+	0x82, 0xf2, 0xc6, 0xc9, 0x9a, 0x39, 0x47, 0x26
+};
+#define QCOMGPIO_GPIO_DSM_REV		0
+#define QCOMGPIO_GPIO_DSM_FUNC_NUM_PINS	2
+
+static UINT8 qcomgpio_pdc_dsm_uuid[ACPI_UUID_LENGTH] = {
+	0xd4, 0x0f, 0x1b, 0x92, 0x7c, 0x56, 0xa0, 0x43,
+	0xbb, 0x14, 0x26, 0x48, 0xf7, 0xb2, 0xa1, 0x8c
+};
+#define QCOMGPIO_PDC_DSM_REV		0
+#define QCOMGPIO_PDC_DSM_FUNC_CIPR	2
 
 static struct qcomgpio_reserved qcomgpio_x1e_reserved[] = {
 	{ .start = 34, .count = 2 },
@@ -122,30 +144,7 @@ static struct qcomgpio_reserved qcomgpio
 	{ .start = 238, .count = 1 },
 };
 
-static int
-qcomgpio_x1e_translate(ACPI_RESOURCE_GPIO *gpio)
-{
-	const ACPI_INTEGER pin = gpio->PinTable[0];
-
-	if (pin < X1E_NUM_PINS) {
-		return gpio->PinTable[0];
-	}
-
-	switch (pin) {
-	case 0x180:
-		return 67;
-	case 0x340:
-		return 92;
-	case 0x380:
-		return 3;
-	default:
-		return -1;
-	}
-}
-
 static struct qcomgpio_config qcomgpio_x1e_config = {
-	.num_pins = X1E_NUM_PINS,
-	.translate = qcomgpio_x1e_translate,
 	.reserved = qcomgpio_x1e_reserved,
 	.num_reserved = __arraycount(qcomgpio_x1e_reserved),
 };
@@ -174,7 +173,7 @@ qcomgpio_attach(device_t parent, device_
 	struct acpi_mem *mem;
 	struct acpi_irq *irq;
 	ACPI_STATUS rv;
-	int error, pin;
+	int error, pin, n;
 	void *ih;
 
 	sc->sc_dev = self;
@@ -209,9 +208,25 @@ qcomgpio_attach(device_t parent, device_
 		goto done;
 	}
 
-	sc->sc_pins = kmem_zalloc(sizeof(*sc->sc_pins) *
-	    sc->sc_config->num_pins, KM_SLEEP);
-	for (pin = 0; pin < sc->sc_config->num_pins; pin++) {
+	sc->sc_npdcmap = res.ar_nirq;
+	sc->sc_pdcmap = kmem_zalloc(sizeof(*sc->sc_pdcmap) * sc->sc_npdcmap,
+	    KM_SLEEP);
+	for (n = 0; n < sc->sc_npdcmap; n++) {
+		sc->sc_pdcmap[n].pm_irq = acpi_res_irq(&res, n)->ar_irq;
+		sc->sc_pdcmap[n].pm_pin = -1;
+		aprint_debug_dev(self, "IRQ resource %u -> %#x\n",
+		    n, sc->sc_pdcmap[n].pm_irq);
+	}
+	qcomgpio_acpi_fill_pdcmap(sc, hdl);
+
+	sc->sc_npins = qcomgpio_acpi_num_pins(self, hdl);
+	if (sc->sc_npins == 0) {
+		aprint_error_dev(self, "couldn't determine pin count!\n");
+		goto done;
+	}
+	sc->sc_pins = kmem_zalloc(sizeof(*sc->sc_pins) * sc->sc_npins,
+	    KM_SLEEP);
+	for (pin = 0; pin < sc->sc_npins; pin++) {
 		sc->sc_pins[pin].pin_caps = qcomgpio_pin_reserved(sc, pin) ?
 		    0 : (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT);
 		sc->sc_pins[pin].pin_num = pin;
@@ -250,7 +265,7 @@ qcomgpio_attach(device_t parent, device_
 	memset(&gba, 0, sizeof(gba));
 	gba.gba_gc = &sc->sc_gc;
 	gba.gba_pins = sc->sc_pins;
-	gba.gba_npins = sc->sc_config->num_pins;
+	gba.gba_npins = sc->sc_npins;
 	sc->sc_gpiodev = config_found(self, &gba, gpiobus_print,
 	    CFARGS(.iattr = "gpiobus"));
 	if (sc->sc_gpiodev != NULL) {
@@ -262,28 +277,100 @@ done:
 	acpi_resource_cleanup(&res);
 }
 
+static u_int
+qcomgpio_acpi_num_pins(device_t dev, ACPI_HANDLE hdl)
+{
+	ACPI_STATUS rv;
+	ACPI_INTEGER npins;
+
+	rv = acpi_dsm_integer(hdl, qcomgpio_gpio_dsm_uuid, 
+	    QCOMGPIO_GPIO_DSM_REV, QCOMGPIO_GPIO_DSM_FUNC_NUM_PINS,
+	    NULL, &npins);
+	if (ACPI_FAILURE(rv)) {
+		aprint_error_dev(dev, "GPIO _DSM failed: %s\n",
+		    AcpiFormatException(rv));
+		return 0;
+	}
+
+	aprint_debug_dev(dev, "GPIO pin count: %u\n", (u_int)npins);
+
+	return (u_int)npins;
+}
+
+static void
+qcomgpio_acpi_fill_pdcmap(struct qcomgpio_softc *sc,
+    ACPI_HANDLE hdl)
+{
+	ACPI_STATUS rv;
+	ACPI_OBJECT *obj;
+	u_int n;
+
+	rv = acpi_dsm_typed(hdl, qcomgpio_pdc_dsm_uuid,
+	    QCOMGPIO_PDC_DSM_REV, QCOMGPIO_PDC_DSM_FUNC_CIPR,
+	    NULL, ACPI_TYPE_PACKAGE, &obj);
+	if (ACPI_FAILURE(rv)) {
+		aprint_error_dev(sc->sc_dev, "PDC _DSM failed: %s\n",
+		    AcpiFormatException(rv));
+		return;
+	}
+
+	for (n = 0; n < obj->Package.Count; n++) {
+		ACPI_OBJECT *map = &obj->Package.Elements[n];
+		u_int irq, pdc;
+		int pin;
+
+		if (map->Type != ACPI_TYPE_PACKAGE ||
+		    map->Package.Count < 3 ||
+		    map->Package.Elements[0].Type != ACPI_TYPE_INTEGER ||
+		    map->Package.Elements[1].Type != ACPI_TYPE_INTEGER ||
+		    map->Package.Elements[2].Type != ACPI_TYPE_INTEGER) {
+			continue;
+		}
+
+		irq = (u_int)map->Package.Elements[2].Integer.Value;
+		pin = (int)map->Package.Elements[1].Integer.Value;
+		for (pdc = 0; pdc < sc->sc_npdcmap; pdc++) {
+			if (sc->sc_pdcmap[pdc].pm_irq == irq) {
+				sc->sc_pdcmap[pdc].pm_pin = pin;
+				break;
+			}
+		}
+		aprint_debug_dev(sc->sc_dev,
+		    "PDC irq %#x -> pin %d%s\n", irq, pin,
+		    pdc == sc->sc_npdcmap ? " (unused)" : "");
+	}
+
+	ACPI_FREE(obj);
+}
+
 static int
 qcomgpio_acpi_translate(void *priv, ACPI_RESOURCE_GPIO *gpio, void **gpiop)
 {
 	struct qcomgpio_softc * const sc = priv;
-	const ACPI_INTEGER pin = gpio->PinTable[0];
-	int xpin;
+	const ACPI_INTEGER vpin = gpio->PinTable[0];
+	int pin = -1;
 
-	xpin = sc->sc_config->translate(gpio);
+	if (vpin < sc->sc_npins) {
+		/* Virtual pin number is 1:1 mapping with hardware. */
+		pin = vpin;
+	} else if (vpin / 64 < sc->sc_npdcmap) {
+		/* Translate the virtual pin number to a hardware pin. */
+		pin = sc->sc_pdcmap[vpin / 64].pm_pin;
+	}
 
-	aprint_debug_dev(sc->sc_dev, "translate %#lx -> %u\n", pin, xpin);
+	aprint_debug_dev(sc->sc_dev, "translate %#lx -> %u\n", vpin, pin);
 
 	if (gpiop != NULL) {
 		if (sc->sc_gpiodev != NULL) {
 			*gpiop = device_private(sc->sc_gpiodev);
 		} else {
 			device_printf(sc->sc_dev,
-			    "no gpiodev for pin %#lx -> %u\n", pin, xpin);
-			xpin = -1;
+			    "no gpiodev for pin %#lx -> %u\n", vpin, pin);
+			pin = -1;
 		}
 	}
 
-	return xpin;
+	return pin;
 }
 
 static int
@@ -363,7 +450,7 @@ qcomgpio_pin_read(void *priv, int pin)
 	struct qcomgpio_softc * const sc = priv;
 	uint32_t val;
 
-	if (pin < 0 || pin >= sc->sc_config->num_pins) {
+	if (pin < 0 || pin >= sc->sc_npins) {
 		return 0;
 	}
 	if ((sc->sc_pins[pin].pin_caps & GPIO_PIN_INPUT) == 0) {
@@ -380,7 +467,7 @@ qcomgpio_pin_write(void *priv, int pin, 
 	struct qcomgpio_softc * const sc = priv;
 	uint32_t val;
 
-	if (pin < 0 || pin >= sc->sc_config->num_pins) {
+	if (pin < 0 || pin >= sc->sc_npins) {
 		return;
 	}
 	if ((sc->sc_pins[pin].pin_caps & GPIO_PIN_OUTPUT) == 0) {
@@ -411,7 +498,7 @@ qcomgpio_intr_establish(void *priv, int 
 	uint32_t dect, pol;
 	uint32_t val;
 
-	if (pin < 0 || pin >= sc->sc_config->num_pins) {
+	if (pin < 0 || pin >= sc->sc_npins) {
 		return NULL;
 	}
 	if (ipl != IPL_VM) {

Reply via email to