Home | History | Annotate | Line # | Download | only in acpi
      1 /* $NetBSD: acpi_gpio.c,v 1.5 2024/12/15 10:15:55 hannken 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 "gpio.h"
     37 
     38 #include <sys/cdefs.h>
     39 __KERNEL_RCSID(0, "$NetBSD: acpi_gpio.c,v 1.5 2024/12/15 10:15:55 hannken Exp $");
     40 
     41 #include <sys/param.h>
     42 #include <sys/kmem.h>
     43 #include <sys/gpio.h>
     44 
     45 #include <dev/gpio/gpiovar.h>
     46 
     47 #include <dev/acpi/acpireg.h>
     48 #include <dev/acpi/acpivar.h>
     49 #include <dev/acpi/acpi_gpio.h>
     50 
     51 #if NGPIO > 0
     52 
     53 #define _COMPONENT	ACPI_RESOURCE_COMPONENT
     54 ACPI_MODULE_NAME	("acpi_gpio")
     55 
     56 struct acpi_gpio_address_space_context {
     57 	ACPI_CONNECTION_INFO conn_info;	/* must be first */
     58 	struct acpi_devnode *ad;
     59 };
     60 
     61 static ACPI_STATUS
     62 acpi_gpio_address_space_init(ACPI_HANDLE region_hdl, UINT32 function,
     63     void *handler_ctx, void **region_ctx)
     64 {
     65 	if (function == ACPI_REGION_DEACTIVATE) {
     66 		*region_ctx = NULL;
     67 	} else {
     68 		*region_ctx = region_hdl;
     69 	}
     70 	return AE_OK;
     71 }
     72 
     73 static ACPI_STATUS
     74 acpi_gpio_address_space_handler(UINT32 function,
     75     ACPI_PHYSICAL_ADDRESS address, UINT32 bit_width, UINT64 *value,
     76     void *handler_ctx, void *region_ctx)
     77 {
     78 	ACPI_OPERAND_OBJECT *region_obj = region_ctx;
     79 	struct acpi_gpio_address_space_context *context = handler_ctx;
     80 	ACPI_CONNECTION_INFO *conn_info = &context->conn_info;
     81 	struct acpi_devnode *ad = context->ad;
     82 	ACPI_RESOURCE *res;
     83 	ACPI_STATUS rv;
     84 	struct gpio_pinmap pinmap;
     85 	int pins[1];
     86 	void *gpiop;
     87 	int pin;
     88 
     89 	if (region_obj->Region.Type != ACPI_TYPE_REGION) {
     90 		return AE_OK;
     91 	}
     92 
     93 	if (ad->ad_gpiodev == NULL) {
     94 		return AE_NO_HANDLER;
     95 	}
     96 
     97 	rv = AcpiBufferToResource(conn_info->Connection,
     98 	    conn_info->Length, &res);
     99 	if (ACPI_FAILURE(rv)) {
    100 		return rv;
    101 	}
    102 
    103 	if (res->Data.Gpio.PinTableLength != 1) {
    104 		/* TODO */
    105 		aprint_debug_dev(ad->ad_gpiodev,
    106 		    "Pin table length %u not implemented\n",
    107 		    res->Data.Gpio.PinTableLength);
    108 		rv = AE_NOT_IMPLEMENTED;
    109 		goto done;
    110 	}
    111 
    112 	pin = ad->ad_gpio_translate(ad->ad_gpio_priv,
    113 	    &res->Data.Gpio, &gpiop);
    114 	if (pin == -1) {
    115 		/* Pin could not be translated. */
    116 		rv = AE_SUPPORT;
    117 		goto done;
    118 	}
    119 
    120 	pinmap.pm_map = pins;
    121 	if (gpio_pin_map(gpiop, pin, 1, &pinmap) != 0) {
    122 		rv = AE_NOT_ACQUIRED;
    123 		goto done;
    124 	}
    125 	if (function & ACPI_IO_MASK) {
    126 		gpio_pin_write(gpiop, &pinmap, 0, *value & 1);
    127 	} else {
    128 		*value = gpio_pin_read(gpiop, &pinmap, 0);
    129 	}
    130 	gpio_pin_unmap(gpiop, &pinmap);
    131 
    132 done:
    133 	ACPI_FREE(res);
    134 
    135 	return rv;
    136 }
    137 #endif
    138 
    139 ACPI_STATUS
    140 acpi_gpio_register(struct acpi_devnode *ad, device_t dev,
    141     int (*translate)(void *, ACPI_RESOURCE_GPIO *, void **), void *priv)
    142 {
    143 #if NGPIO > 0
    144 	struct acpi_gpio_address_space_context *context;
    145 	ACPI_STATUS rv;
    146 
    147 	if (ad->ad_gpiodev != NULL) {
    148 		device_printf(dev, "%s already registered\n",
    149 		    device_xname(ad->ad_gpiodev));
    150 		return AE_ALREADY_EXISTS;
    151 	}
    152 
    153 	context = kmem_zalloc(sizeof(*context), KM_SLEEP);
    154 	context->ad = ad;
    155 
    156 	rv = AcpiInstallAddressSpaceHandler(ad->ad_handle,
    157 	    ACPI_ADR_SPACE_GPIO,
    158 	    acpi_gpio_address_space_handler,
    159 	    acpi_gpio_address_space_init,
    160 	    context);
    161 	if (ACPI_FAILURE(rv)) {
    162 		aprint_error_dev(dev,
    163 		    "couldn't install address space handler: %s",
    164 		    AcpiFormatException(rv));
    165 		return rv;
    166 	}
    167 
    168 	ad->ad_gpiodev = dev;
    169 	ad->ad_gpio_translate = translate;
    170 	ad->ad_gpio_priv = priv;
    171 
    172 	return AE_OK;
    173 #else
    174 	return AE_NOT_CONFIGURED;
    175 #endif
    176 }
    177 
    178 static ACPI_STATUS
    179 acpi_gpio_translate(ACPI_RESOURCE_GPIO *res, void **gpiop, int *pin)
    180 {
    181 	struct acpi_devnode *ad, *gpioad = NULL;
    182 	ACPI_HANDLE hdl;
    183 	ACPI_RESOURCE_SOURCE *rs;
    184 	ACPI_STATUS rv;
    185 	int xpin;
    186 
    187 	/* Find the device node providing the GPIO resource. */
    188 	rs = &res->ResourceSource;
    189 	if (rs->StringPtr == NULL) {
    190 		return AE_NOT_FOUND;
    191 	}
    192 	rv = AcpiGetHandle(NULL, rs->StringPtr, &hdl);
    193 	if (ACPI_FAILURE(rv)) {
    194 		return rv;
    195 	}
    196 	SIMPLEQ_FOREACH(ad, &acpi_softc->sc_head, ad_list) {
    197 		if (ad->ad_handle == hdl) {
    198 			gpioad = ad;
    199 			break;
    200 		}
    201 	}
    202 	if (gpioad == NULL) {
    203 		/* No device node found. */
    204 		return AE_NOT_FOUND;
    205 	}
    206 
    207 	if (gpioad->ad_gpiodev == NULL) {
    208 		/* No resource provider is registered. */
    209 		return AE_NO_HANDLER;
    210 	}
    211 
    212 	xpin = gpioad->ad_gpio_translate(gpioad->ad_gpio_priv,
    213 	    res, gpiop);
    214 	if (xpin == -1) {
    215 		/* Pin could not be translated. */
    216 		return AE_SUPPORT;
    217 	}
    218 
    219 	*pin = xpin;
    220 
    221 	return AE_OK;
    222 }
    223 
    224 struct acpi_gpio_resource_context {
    225 	u_int index;
    226 	u_int conntype;
    227 	u_int curindex;
    228 	ACPI_RESOURCE_GPIO *res;
    229 };
    230 
    231 static ACPI_STATUS
    232 acpi_gpio_parse(ACPI_RESOURCE *res, void *context)
    233 {
    234 	struct acpi_gpio_resource_context *ctx = context;
    235 
    236 	if (res->Type != ACPI_RESOURCE_TYPE_GPIO) {
    237 		return AE_OK;
    238 	}
    239 	if (res->Data.Gpio.ConnectionType != ctx->conntype) {
    240 		return AE_OK;
    241 	}
    242 	if (ctx->curindex == ctx->index) {
    243 		ctx->res = &res->Data.Gpio;
    244 		return AE_CTRL_TERMINATE;
    245 	}
    246 	ctx->curindex++;
    247 	return AE_OK;
    248 
    249 }
    250 
    251 ACPI_STATUS
    252 acpi_gpio_get_int(ACPI_HANDLE hdl, u_int index, void **gpiop, int *pin,
    253     int *irqmode)
    254 {
    255 	struct acpi_gpio_resource_context ctx = {
    256 		.index = index,
    257 		.conntype = ACPI_RESOURCE_GPIO_TYPE_INT,
    258 	};
    259 	ACPI_RESOURCE_GPIO *gpio;
    260 	ACPI_STATUS rv;
    261 
    262 	rv = AcpiWalkResources(hdl, "_CRS", acpi_gpio_parse, &ctx);
    263 	if (ACPI_FAILURE(rv)) {
    264 		return rv;
    265 	}
    266 	gpio = ctx.res;
    267 
    268 	rv = acpi_gpio_translate(gpio, gpiop, pin);
    269 	if (ACPI_FAILURE(rv)) {
    270 		printf("%s: translate failed: %s\n", __func__,
    271 		    AcpiFormatException(rv));
    272 		return rv;
    273 	}
    274 
    275         if (gpio->Triggering == ACPI_LEVEL_SENSITIVE) {
    276                 *irqmode = gpio->Polarity == ACPI_ACTIVE_HIGH ?
    277                     GPIO_INTR_HIGH_LEVEL : GPIO_INTR_LOW_LEVEL;
    278         } else {
    279                 KASSERT(gpio->Triggering == ACPI_EDGE_SENSITIVE);
    280                 if (gpio->Polarity == ACPI_ACTIVE_LOW) {
    281                         *irqmode = GPIO_INTR_NEG_EDGE;
    282                 } else if (gpio->Polarity == ACPI_ACTIVE_HIGH) {
    283                         *irqmode = GPIO_INTR_POS_EDGE;
    284                 } else {
    285                         KASSERT(gpio->Polarity == ACPI_ACTIVE_BOTH);
    286                         *irqmode = GPIO_INTR_DOUBLE_EDGE;
    287                 }
    288         }
    289 
    290 	return AE_OK;
    291 }
    292 
    293 ACPI_STATUS
    294 acpi_gpio_get_io(ACPI_HANDLE hdl, u_int index, void **gpiop, int *pin)
    295 {
    296 	struct acpi_gpio_resource_context ctx = {
    297 		.index = index,
    298 		.conntype = ACPI_RESOURCE_GPIO_TYPE_INT,
    299 	};
    300 	ACPI_STATUS rv;
    301 
    302 	rv = AcpiWalkResources(hdl, "_CRS", acpi_gpio_parse, &ctx);
    303 	if (ACPI_FAILURE(rv)) {
    304 		return rv;
    305 	}
    306 
    307 	return acpi_gpio_translate(ctx.res, gpiop, pin);
    308 }
    309