Home | History | Annotate | Line # | Download | only in acpi
acpi_gpio.c revision 1.2
      1 /* $NetBSD: acpi_gpio.c,v 1.2 2024/12/09 22:10:25 jmcneill Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2024 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Jared McNeill <jmcneill (at) invisible.ca>.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  * POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 /*
     33  * ACPI GPIO resource support.
     34  */
     35 
     36 #include <sys/cdefs.h>
     37 __KERNEL_RCSID(0, "$NetBSD: acpi_gpio.c,v 1.2 2024/12/09 22:10:25 jmcneill Exp $");
     38 
     39 #include <sys/param.h>
     40 #include <sys/gpio.h>
     41 
     42 #include <dev/acpi/acpireg.h>
     43 #include <dev/acpi/acpivar.h>
     44 #include <dev/acpi/acpi_gpio.h>
     45 
     46 int
     47 acpi_gpio_register(struct acpi_devnode *ad, device_t dev,
     48     int (*translate)(void *, ACPI_RESOURCE_GPIO *, void **), void *priv)
     49 {
     50 	if (ad->ad_gpiodev != NULL) {
     51 		device_printf(dev, "%s already registered\n",
     52 		    device_xname(ad->ad_gpiodev));
     53 		return EBUSY;
     54 	}
     55 
     56 	ad->ad_gpiodev = dev;
     57 	ad->ad_gpio_translate = translate;
     58 	ad->ad_gpio_priv = priv;
     59 
     60 	return 0;
     61 }
     62 
     63 static ACPI_STATUS
     64 acpi_gpio_translate(ACPI_RESOURCE_GPIO *res, void **gpiop, int *pin)
     65 {
     66 	struct acpi_devnode *ad, *gpioad = NULL;
     67 	ACPI_HANDLE hdl;
     68 	ACPI_RESOURCE_SOURCE *rs;
     69 	ACPI_STATUS rv;
     70 	int xpin;
     71 
     72 	/* Find the device node providing the GPIO resource. */
     73 	rs = &res->ResourceSource;
     74 	if (rs->StringPtr == NULL) {
     75 		return AE_NOT_FOUND;
     76 	}
     77 	rv = AcpiGetHandle(NULL, rs->StringPtr, &hdl);
     78 	if (ACPI_FAILURE(rv)) {
     79 		return rv;
     80 	}
     81 	SIMPLEQ_FOREACH(ad, &acpi_softc->sc_head, ad_list) {
     82 		if (ad->ad_handle == hdl) {
     83 			gpioad = ad;
     84 			break;
     85 		}
     86 	}
     87 	if (gpioad == NULL) {
     88 		/* No device node found. */
     89 		return AE_NOT_FOUND;
     90 	}
     91 
     92 	if (gpioad->ad_gpiodev == NULL) {
     93 		/* No resource provider is registered. */
     94 		return AE_NO_HANDLER;
     95 	}
     96 
     97 	xpin = gpioad->ad_gpio_translate(gpioad->ad_gpio_priv,
     98 	    res, gpiop);
     99 	if (xpin == -1) {
    100 		/* Pin could not be translated. */
    101 		return AE_NOT_IMPLEMENTED;
    102 	}
    103 
    104 	*pin = xpin;
    105 
    106 	return AE_OK;
    107 }
    108 
    109 struct acpi_gpio_resource_context {
    110 	u_int index;
    111 	u_int conntype;
    112 	u_int curindex;
    113 	ACPI_RESOURCE_GPIO *res;
    114 };
    115 
    116 static ACPI_STATUS
    117 acpi_gpio_parse(ACPI_RESOURCE *res, void *context)
    118 {
    119 	struct acpi_gpio_resource_context *ctx = context;
    120 
    121 	if (res->Type != ACPI_RESOURCE_TYPE_GPIO) {
    122 		return AE_OK;
    123 	}
    124 	if (res->Data.Gpio.ConnectionType != ctx->conntype) {
    125 		return AE_OK;
    126 	}
    127 	if (ctx->curindex == ctx->index) {
    128 		ctx->res = &res->Data.Gpio;
    129 		return AE_CTRL_TERMINATE;
    130 	}
    131 	ctx->curindex++;
    132 	return AE_OK;
    133 
    134 }
    135 
    136 ACPI_STATUS
    137 acpi_gpio_get_int(ACPI_HANDLE hdl, u_int index, void **gpiop, int *pin,
    138     int *irqmode)
    139 {
    140 	struct acpi_gpio_resource_context ctx = {
    141 		.index = index,
    142 		.conntype = ACPI_RESOURCE_GPIO_TYPE_INT,
    143 	};
    144 	ACPI_RESOURCE_GPIO *gpio;
    145 	ACPI_STATUS rv;
    146 
    147 	rv = AcpiWalkResources(hdl, "_CRS", acpi_gpio_parse, &ctx);
    148 	if (ACPI_FAILURE(rv)) {
    149 		return rv;
    150 	}
    151 	gpio = ctx.res;
    152 
    153 	rv = acpi_gpio_translate(gpio, gpiop, pin);
    154 	if (ACPI_FAILURE(rv)) {
    155 		printf("%s: translate failed: %s\n", __func__,
    156 		    AcpiFormatException(rv));
    157 		return rv;
    158 	}
    159 
    160         if (gpio->Triggering == ACPI_LEVEL_SENSITIVE) {
    161                 *irqmode = gpio->Polarity == ACPI_ACTIVE_HIGH ?
    162                     GPIO_INTR_HIGH_LEVEL : GPIO_INTR_LOW_LEVEL;
    163         } else {
    164                 KASSERT(gpio->Triggering == ACPI_EDGE_SENSITIVE);
    165                 if (gpio->Polarity == ACPI_ACTIVE_LOW) {
    166                         *irqmode = GPIO_INTR_NEG_EDGE;
    167                 } else if (gpio->Polarity == ACPI_ACTIVE_HIGH) {
    168                         *irqmode = GPIO_INTR_POS_EDGE;
    169                 } else {
    170                         KASSERT(gpio->Polarity == ACPI_ACTIVE_BOTH);
    171                         *irqmode = GPIO_INTR_DOUBLE_EDGE;
    172                 }
    173         }
    174 
    175 	return AE_OK;
    176 }
    177 
    178 ACPI_STATUS
    179 acpi_gpio_get_io(ACPI_HANDLE hdl, u_int index, void **gpiop, int *pin)
    180 {
    181 	struct acpi_gpio_resource_context ctx = {
    182 		.index = index,
    183 		.conntype = ACPI_RESOURCE_GPIO_TYPE_INT,
    184 	};
    185 	ACPI_STATUS rv;
    186 
    187 	rv = AcpiWalkResources(hdl, "_CRS", acpi_gpio_parse, &ctx);
    188 	if (ACPI_FAILURE(rv)) {
    189 		return rv;
    190 	}
    191 
    192 	return acpi_gpio_translate(ctx.res, gpiop, pin);
    193 }
    194