Home | History | Annotate | Line # | Download | only in acpi
plgpio_acpi.c revision 1.2
      1 /* $NetBSD: plgpio_acpi.c,v 1.2 2018/10/21 18:31:58 jmcneill Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2018 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 #include <sys/cdefs.h>
     33 __KERNEL_RCSID(0, "$NetBSD: plgpio_acpi.c,v 1.2 2018/10/21 18:31:58 jmcneill Exp $");
     34 
     35 #include <sys/param.h>
     36 #include <sys/bus.h>
     37 #include <sys/cpu.h>
     38 #include <sys/device.h>
     39 #include <sys/gpio.h>
     40 
     41 #include <dev/acpi/acpireg.h>
     42 #include <dev/acpi/acpivar.h>
     43 
     44 #include <dev/gpio/gpiovar.h>
     45 #include <dev/ic/pl061var.h>
     46 #include <dev/ic/pl061reg.h>
     47 
     48 struct plgpio_acpi_softc;
     49 
     50 struct plgpio_acpi_event {
     51 	struct plgpio_acpi_softc *ev_sc;
     52 	u_int			ev_pin;
     53 	ACPI_HANDLE		ev_method;
     54 	bool			ev_method_evt;
     55 };
     56 
     57 struct plgpio_acpi_softc {
     58 	struct plgpio_softc	sc_base;
     59 
     60 	ACPI_HANDLE		sc_handle;
     61 	uint32_t		sc_aei_pins;		/* bitmask */
     62 	ACPI_RESOURCE_GPIO	sc_aei_res[8];
     63 
     64 	struct plgpio_acpi_event sc_event[8];
     65 };
     66 
     67 static int	plgpio_acpi_match(device_t, cfdata_t, void *);
     68 static void	plgpio_acpi_attach(device_t, device_t, void *);
     69 
     70 static void	plgpio_acpi_init(struct plgpio_acpi_softc *);
     71 static void	plgpio_acpi_notify(void *);
     72 static int	plgpio_acpi_intr(void *);
     73 
     74 CFATTACH_DECL_NEW(plgpio_acpi, sizeof(struct plgpio_acpi_softc), plgpio_acpi_match, plgpio_acpi_attach, NULL, NULL);
     75 
     76 static const char * const compatible[] = {
     77 	"ARMH0061",
     78 	NULL
     79 };
     80 
     81 static int
     82 plgpio_acpi_match(device_t parent, cfdata_t cf, void *aux)
     83 {
     84 	struct acpi_attach_args *aa = aux;
     85 
     86 	if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE)
     87 		return 0;
     88 
     89 	return acpi_match_hid(aa->aa_node->ad_devinfo, compatible);
     90 }
     91 
     92 static void
     93 plgpio_acpi_attach(device_t parent, device_t self, void *aux)
     94 {
     95 	struct plgpio_acpi_softc * const asc = device_private(self);
     96 	struct plgpio_softc * const sc = &asc->sc_base;
     97 	struct acpi_attach_args *aa = aux;
     98 	struct acpi_resources res;
     99 	struct acpi_mem *mem;
    100 	struct acpi_irq *irq;
    101 	ACPI_STATUS rv;
    102 	int error;
    103 	void *ih;
    104 
    105 	sc->sc_dev = self;
    106 	asc->sc_handle = aa->aa_node->ad_handle;
    107 
    108 	rv = acpi_resource_parse(sc->sc_dev, aa->aa_node->ad_handle, "_CRS",
    109 	    &res, &acpi_resource_parse_ops_default);
    110 	if (ACPI_FAILURE(rv))
    111 		return;
    112 
    113 	mem = acpi_res_mem(&res, 0);
    114 	if (mem == NULL) {
    115 		aprint_error_dev(self, "couldn't find mem resource\n");
    116 		goto done;
    117 	}
    118 
    119 	irq = acpi_res_irq(&res, 0);
    120 	if (mem == NULL) {
    121 		aprint_error_dev(self, "couldn't find irq resource\n");
    122 		goto done;
    123 	}
    124 
    125 	sc->sc_dev = self;
    126 	sc->sc_bst = aa->aa_memt;
    127 	error = bus_space_map(sc->sc_bst, mem->ar_base, mem->ar_length, 0, &sc->sc_bsh);
    128 	if (error) {
    129 		aprint_error_dev(self, "couldn't map registers\n");
    130 		return;
    131 	}
    132 
    133 	plgpio_attach(sc);
    134 
    135 	const int type = (irq->ar_type == ACPI_EDGE_SENSITIVE) ? IST_EDGE : IST_LEVEL;
    136 	ih = intr_establish(irq->ar_irq, IPL_VM, type, plgpio_acpi_intr, asc);
    137 	if (ih == NULL)
    138 		aprint_error_dev(self, "couldn't establish interrupt\n");
    139 
    140 	plgpio_acpi_init(asc);
    141 
    142 done:
    143 	acpi_resource_cleanup(&res);
    144 }
    145 
    146 static ACPI_STATUS
    147 plgpio_acpi_resource_cb(ACPI_RESOURCE *res, void *ctx)
    148 {
    149 	struct plgpio_acpi_softc * const asc = ctx;
    150 	ACPI_RESOURCE_GPIO *gpio;
    151 	UINT16 pin;
    152 
    153 	if (res->Type != ACPI_RESOURCE_TYPE_GPIO)
    154 		return AE_OK;
    155 
    156 	gpio = &res->Data.Gpio;
    157 	if (gpio->ConnectionType != ACPI_RESOURCE_GPIO_TYPE_INT ||
    158 	    gpio->PinTableLength != 1)
    159 		return AE_OK;
    160 
    161 	pin = gpio->PinTable[0];
    162 	if (pin >= __arraycount(asc->sc_aei_res)) {
    163 		aprint_error_dev(asc->sc_base.sc_dev, "_AEI pin %u out of range\n", pin);
    164 		return AE_OK;
    165 	}
    166 
    167 	asc->sc_aei_pins |= __BIT(pin);
    168 	asc->sc_aei_res[pin] = *gpio;
    169 
    170 	return AE_OK;
    171 }
    172 
    173 static void
    174 plgpio_acpi_init(struct plgpio_acpi_softc *asc)
    175 {
    176 	struct plgpio_softc * const sc = &asc->sc_base;
    177 	ACPI_RESOURCE_GPIO *gpio;
    178 	ACPI_HANDLE handle;
    179 	char namebuf[5];
    180 	ACPI_STATUS rv;
    181 	uint32_t ibe, iev, is, ie;
    182 	int pin;
    183 
    184 	rv = AcpiWalkResources(asc->sc_handle, "_AEI", plgpio_acpi_resource_cb, asc);
    185 	if (ACPI_FAILURE(rv)) {
    186 		if (rv != AE_NOT_FOUND)
    187 			aprint_error_dev(asc->sc_base.sc_dev, "failed to parse _AEI: %s\n",
    188 			    AcpiFormatException(rv));
    189 		return;
    190 	}
    191 
    192 	if (!asc->sc_aei_pins)
    193 		return;
    194 
    195 	aprint_verbose_dev(asc->sc_base.sc_dev, "ACPI event pins: %#x\n", asc->sc_aei_pins);
    196 
    197 	sc->sc_reserved_mask = asc->sc_aei_pins;
    198 
    199 	/*
    200 	 * For each event pin, find the corresponding event method (_Exx, _Lxx, or _EVT).
    201 	 */
    202 	for (pin = 0; pin < __arraycount(asc->sc_aei_res); pin++) {
    203 		if ((asc->sc_aei_pins & __BIT(pin)) == 0)
    204 			continue;
    205 
    206 		gpio = &asc->sc_aei_res[pin];
    207 
    208 		const char trig = gpio->Triggering == ACPI_LEVEL_SENSITIVE ? 'L' : 'E';
    209 		handle = NULL;
    210 		snprintf(namebuf, sizeof(namebuf), "_%c%02X", trig, pin);
    211 		if (ACPI_FAILURE(AcpiGetHandle(asc->sc_handle, namebuf, &handle))) {
    212 			(void)AcpiGetHandle(asc->sc_handle, "_EVT", &handle);
    213 			if (handle != NULL)
    214 				asc->sc_event[pin].ev_method_evt = true;
    215 		}
    216 
    217 		if (handle == NULL)
    218 			continue;
    219 
    220 		asc->sc_event[pin].ev_sc = asc;
    221 		asc->sc_event[pin].ev_pin = pin;
    222 		asc->sc_event[pin].ev_method = handle;
    223 
    224 		/*
    225 		 * Configure and enable interrupts for this pin.
    226 		 */
    227 
    228 		ibe = PLGPIO_READ(sc, PL061_GPIOIBE_REG);
    229 		iev = PLGPIO_READ(sc, PL061_GPIOIEV_REG);
    230 		switch (gpio->Polarity) {
    231 		case ACPI_ACTIVE_HIGH:
    232 			ibe &= ~__BIT(pin);
    233 			iev |= __BIT(pin);
    234 			break;
    235 		case ACPI_ACTIVE_LOW:
    236 			ibe &= ~__BIT(pin);
    237 			iev &= ~__BIT(pin);
    238 			break;
    239 		case ACPI_ACTIVE_BOTH:
    240 			ibe |= __BIT(pin);
    241 			break;
    242 		}
    243 		PLGPIO_WRITE(sc, PL061_GPIOIBE_REG, ibe);
    244 		PLGPIO_WRITE(sc, PL061_GPIOIEV_REG, iev);
    245 
    246 		is = PLGPIO_READ(sc, PL061_GPIOIS_REG);
    247 		switch (gpio->Triggering) {
    248 		case ACPI_LEVEL_SENSITIVE:
    249 			is |= __BIT(pin);
    250 			break;
    251 		case ACPI_EDGE_SENSITIVE:
    252 			is &= ~__BIT(pin);
    253 			break;
    254 		}
    255 		PLGPIO_WRITE(sc, PL061_GPIOIS_REG, is);
    256 
    257 		delay(20);
    258 
    259 		PLGPIO_WRITE(sc, PL061_GPIOIC_REG, __BIT(pin));
    260 
    261 		ie = PLGPIO_READ(sc, PL061_GPIOIE_REG);
    262 		ie |= __BIT(pin);
    263 		PLGPIO_WRITE(sc, PL061_GPIOIE_REG, ie);
    264 	}
    265 }
    266 
    267 static void
    268 plgpio_acpi_notify(void *priv)
    269 {
    270 	struct plgpio_acpi_event * const ev = priv;
    271 	struct plgpio_acpi_softc * const asc = ev->ev_sc;
    272 	struct plgpio_softc * const sc = &asc->sc_base;
    273 	ACPI_STATUS rv;
    274 
    275 	if (ev->ev_method_evt) {
    276 		ACPI_OBJECT_LIST objs;
    277 		ACPI_OBJECT obj[1];
    278 		objs.Count = 1;
    279 		objs.Pointer = obj;
    280 		obj[0].Type = ACPI_TYPE_INTEGER;
    281 		obj[0].Integer.Value = ev->ev_pin;
    282 		rv = AcpiEvaluateObject(ev->ev_method, NULL, &objs, NULL);
    283 	} else {
    284 		rv = AcpiEvaluateObject(ev->ev_method, NULL, NULL, NULL);
    285 	}
    286 
    287 	if (ACPI_FAILURE(rv))
    288 		device_printf(sc->sc_dev, "failed to handle %s event: %s\n",
    289 		    acpi_name(ev->ev_method), AcpiFormatException(rv));
    290 }
    291 
    292 static int
    293 plgpio_acpi_intr(void *priv)
    294 {
    295 	struct plgpio_acpi_softc * const asc = priv;
    296 	struct plgpio_softc * const sc = &asc->sc_base;
    297 	uint32_t mis;
    298 	int bit;
    299 
    300 	mis = PLGPIO_READ(sc, PL061_GPIOMIS_REG);
    301 	PLGPIO_WRITE(sc, PL061_GPIOIC_REG, mis);
    302 
    303 	while ((bit = __builtin_ffs(mis)) != 0) {
    304 		const int pin = bit - 1;
    305 		struct plgpio_acpi_event * const ev = &asc->sc_event[pin];
    306 
    307 		KASSERT(ev->ev_method != NULL);
    308 
    309 		AcpiOsExecute(OSL_NOTIFY_HANDLER, plgpio_acpi_notify, ev);
    310 
    311 		mis &= ~__BIT(pin);
    312 	}
    313 
    314 	return 1;
    315 }
    316