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@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
48struct plgpio_acpi_softc;
49
50struct 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
57struct 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
67static int	plgpio_acpi_match(device_t, cfdata_t, void *);
68static void	plgpio_acpi_attach(device_t, device_t, void *);
69
70static void	plgpio_acpi_init(struct plgpio_acpi_softc *);
71static void	plgpio_acpi_notify(void *);
72static int	plgpio_acpi_intr(void *);
73
74CFATTACH_DECL_NEW(plgpio_acpi, sizeof(struct plgpio_acpi_softc), plgpio_acpi_match, plgpio_acpi_attach, NULL, NULL);
75
76static const char * const compatible[] = {
77	"ARMH0061",
78	NULL
79};
80
81static int
82plgpio_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
92static void
93plgpio_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
142done:
143	acpi_resource_cleanup(&res);
144}
145
146static ACPI_STATUS
147plgpio_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
173static void
174plgpio_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
267static void
268plgpio_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
292static int
293plgpio_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