Index | Thread | Search

From:
Miod Vallat <miod@online.fr>
Subject:
Re: return of the chromebook keyboard diff
To:
tech@openbsd.org
Date:
Fri, 31 Jan 2025 12:54:04 +0000

Download raw body.

Thread
Thanks to off-list debugging with Miguel Landaeta, I have been able to
improve these changes a lot.

The previous iteration of this diff would not attach on his system
because the keyboard controller is found before the GPIO controller.
This is now fixed by postponing GPIO interrupt registration a bit.

While I was working with interrupts, I have also reworked the attachment
logic.
It is now as follows: if your pckbc@acpi node has GPIO interrupts, or
regular interrupts not configured in legacy, ISA-compatible (i.e.
edge-triggered, rising front), then the legacy pckbc@isa has no chance
to behave properly, and we attach; otherwise we let it claim the
keyboard unless you change the device flags to 1 to force attachment.

This should remove the need for a list of "known to need acpi
attachment" devices, and in fact, I have removed it now.

Please test.

Index: share/man/man4/acpi.4
===================================================================
RCS file: /OpenBSD/src/share/man/man4/acpi.4,v
diff -u -p -u -p -r1.76 acpi.4
--- share/man/man4/acpi.4	5 Nov 2024 11:12:48 -0000	1.76
+++ share/man/man4/acpi.4	31 Jan 2025 12:05:19 -0000
@@ -132,6 +132,8 @@ Intel OnChip System Fabric device
 Intelligent Platform Management Interface driver
 .It Xr pchgpio 4
 Intel PCH GPIO controller
+.It Xr pckbc 4
+Keyboard controller
 .It Xr pluart 4
 ARM PrimeCell PL011 UART
 .It Xr qcgpio 4
Index: share/man/man4/pckbc.4
===================================================================
RCS file: /OpenBSD/src/share/man/man4/pckbc.4,v
diff -u -p -u -p -r1.18 pckbc.4
--- share/man/man4/pckbc.4	26 Sep 2010 20:39:08 -0000	1.18
+++ share/man/man4/pckbc.4	31 Jan 2025 12:05:19 -0000
@@ -33,6 +33,7 @@
 .Nd PC (ISA) keyboard controller driver
 .Sh SYNOPSIS
 .Cd "pckbc* at isa? flags 0x00           " Pq "alpha, amd64, i386, loongson"
+.Cd "pckbc* at acpi? flags 0x00          " Pq "amd64"
 .Cd "pckbc* at ebus?                     " Pq "sparc64"
 .Cd "pckbd* at pckbc?"
 .Cd "pms*   at pckbc?"
@@ -40,21 +41,17 @@
 The
 .Nm
 driver handles resource allocation and device attachment for the
-traditional PC/AT keyboard controller.
-It provides two logical connections for child devices, the
+traditional PC/AT keyboard controller, or emulations thereof.
+It provides up to two logical connections for child devices, the
 .Dq keyboard
 slot for a keyboard and the
 .Dq auxiliary
-slot for mice (the latter might be missing in older keyboard controllers).
-.\" .Pp
-.\" The optional
-.\" .Dq slot
-.\" locator argument can be used to force unusual connections of devices to
-.\" logical slots.
-.\" This feature is for experimentation only, it will not be
-.\" useful in normal operation.
+slot for mice (the latter might be missing in older keyboard controllers,
+or recent emulations).
 .Pp
 To avoid attaching a phantom PS/2 keyboard device, the
+.Xr isa 4
+attachment of the
 .Nm
 driver will attempt to detect USB legacy keyboard emulation on amd64 and i386
 systems.
@@ -62,7 +59,18 @@ Unfortunately, the detection heuristics 
 PS/2 keyboard.
 The keyboard can be forced to attach on these systems, by changing the
 device flags to 1.
+.Pp
+The
+.Xr acpi 4
+attachment of the
+.Nm
+driver defaults to attach only where it would perform better than its legacy
+.Xr isa 4
+attachment.
+Should this logic be insufficient, it is possible to force it to always attach,
+by changing its device flags to 1.
 .Sh SEE ALSO
+.Xr acpi 4 ,
 .Xr ebus 4 ,
 .Xr intro 4 ,
 .Xr isa 4 ,
Index: sys/arch/amd64/conf/GENERIC
===================================================================
RCS file: /OpenBSD/src/sys/arch/amd64/conf/GENERIC,v
diff -u -p -u -p -r1.530 GENERIC
--- sys/arch/amd64/conf/GENERIC	26 Nov 2024 21:45:35 -0000	1.530
+++ sys/arch/amd64/conf/GENERIC	31 Jan 2025 12:05:19 -0000
@@ -86,6 +86,7 @@ ipmi0		at acpi? disable
 ccpmic*		at iic?
 tipmic*		at iic?
 intelpmc*	at acpi?
+pckbc*		at acpi?
 
 efi0		at bios0
 mpbios0		at bios0
Index: sys/arch/amd64/conf/RAMDISK
===================================================================
RCS file: /OpenBSD/src/sys/arch/amd64/conf/RAMDISK,v
diff -u -p -u -p -r1.87 RAMDISK
--- sys/arch/amd64/conf/RAMDISK	12 Aug 2024 18:43:41 -0000	1.87
+++ sys/arch/amd64/conf/RAMDISK	31 Jan 2025 12:05:19 -0000
@@ -37,6 +37,7 @@ acpimadt0	at acpi?
 com0		at acpi? addr 0x3f8
 com1		at acpi? addr 0x2f8
 com*		at acpi?
+pckbc*		at acpi?
 
 mpbios0		at bios0
 
Index: sys/arch/amd64/conf/RAMDISK_CD
===================================================================
RCS file: /OpenBSD/src/sys/arch/amd64/conf/RAMDISK_CD,v
diff -u -p -u -p -r1.209 RAMDISK_CD
--- sys/arch/amd64/conf/RAMDISK_CD	26 Nov 2024 21:45:35 -0000	1.209
+++ sys/arch/amd64/conf/RAMDISK_CD	31 Jan 2025 12:05:19 -0000
@@ -56,6 +56,7 @@ com1		at acpi? addr 0x2f8
 com2		at acpi? addr 0x3e8
 com*		at acpi?
 glkgpio*	at acpi?
+pckbc*		at acpi?
 
 mpbios0		at bios0
 
Index: sys/dev/acpi/acpi.c
===================================================================
RCS file: /OpenBSD/src/sys/dev/acpi/acpi.c,v
diff -u -p -u -p -r1.440 acpi.c
--- sys/dev/acpi/acpi.c	23 Jan 2025 11:24:34 -0000	1.440
+++ sys/dev/acpi/acpi.c	31 Jan 2025 12:05:19 -0000
@@ -3037,12 +3037,9 @@ const char *acpi_skip_hids[] = {
 
 /* ISA devices for which we attach a driver later */
 const char *acpi_isa_hids[] = {
-	"PNP0303",	/* IBM Enhanced Keyboard (101/102-key, PS/2 Mouse) */
 	"PNP0400",	/* Standard LPT Parallel Port */
 	"PNP0401",	/* ECP Parallel Port */
 	"PNP0700",	/* PC-class Floppy Disk Controller */
-	"PNP0F03",	/* Microsoft PS/2-style Mouse */
-	"PNP0F13",	/* PS/2 Mouse */
 	NULL
 };
 
Index: sys/dev/acpi/files.acpi
===================================================================
RCS file: /OpenBSD/src/sys/dev/acpi/files.acpi,v
diff -u -p -u -p -r1.71 files.acpi
--- sys/dev/acpi/files.acpi	4 Aug 2024 11:05:18 -0000	1.71
+++ sys/dev/acpi/files.acpi	31 Jan 2025 12:05:19 -0000
@@ -289,3 +289,7 @@ file	dev/acpi/iosf_acpi.c		iosf_acpi
 device	intelpmc
 attach	intelpmc at acpi
 file	dev/acpi/intelpmc.c		intelpmc
+
+# PS/2 Keyboard Controller
+attach	pckbc at acpi with pckbc_acpi
+file	dev/acpi/pckbc_acpi.c		pckbc_acpi
Index: sys/dev/acpi/pckbc_acpi.c
===================================================================
RCS file: sys/dev/acpi/pckbc_acpi.c
diff -N sys/dev/acpi/pckbc_acpi.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ sys/dev/acpi/pckbc_acpi.c	31 Jan 2025 12:05:19 -0000
@@ -0,0 +1,569 @@
+/*	$OpenBSD$	*/
+/*
+ * Copyright (c) 2024, 2025, Miodrag Vallat.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/*
+ * Copyright (c) 1998
+ *	Matthias Drochner.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/malloc.h>
+
+#include <machine/bus.h>
+
+#include <dev/acpi/acpidev.h>
+#include <dev/acpi/acpivar.h>
+#include <dev/acpi/amltypes.h>
+#include <dev/acpi/dsdt.h>
+
+#include <dev/ic/i8042reg.h>
+#include <dev/ic/pckbcvar.h>
+
+/*
+ * This driver is more complicated than it should have to be, as it needs
+ * to gather the needs of pckbc (2 I/O ports, and up to 2 interrupts),
+ * which may be scattered across two ACPI nodes.
+ * Because of this, it is able to attach to two different ACPI nodes,
+ * but the second attachment "hijacks" the first one's softc struct, so
+ * that all the required data is gathered in one place.
+ */
+
+int	pckbc_acpi_match(struct device *, void *, void *);
+void	pckbc_acpi_attach(struct device *, struct device *, void *);
+int	pckbc_acpi_activate(struct device *, int);
+
+struct pckbc_acpi_gpio_intr {
+	struct aml_node *node;
+	uint16_t pin;
+	uint16_t flags;
+};
+
+struct pckbc_acpi_softc {
+	struct pckbc_softc sc;
+	/* regular interrupts */
+	void *sc_ih[2];
+	/* gpio interrupts */
+	struct pckbc_acpi_gpio_intr sc_gpioint[2];
+	unsigned int sc_nints, sc_ngpioints;
+};
+
+const struct cfattach pckbc_acpi_ca = {
+	.ca_devsize = sizeof(struct pckbc_acpi_softc),
+	.ca_match = pckbc_acpi_match,
+	.ca_attach = pckbc_acpi_attach,
+	.ca_activate = pckbc_acpi_activate
+};
+
+struct pckbc_acpi_crs_data {
+	struct aml_node *basenode;
+	struct pckbc_acpi_gpio_intr intrs[2];
+	unsigned int nints;
+};
+
+int	pckbc_acpi_match_kbd(struct device *, void *, void *);
+int	pckbc_acpi_match_mou(struct device *, void *, void *,
+	    struct pckbc_acpi_softc *);
+void	pckbc_acpi_attach_kbd(struct device *, struct device *, void *);
+void	pckbc_acpi_attach_mou(struct device *, struct device *, void *);
+struct pckbc_acpi_softc *pckbc_acpi_find(int);
+void	pckbc_acpi_crs_walk(struct device *, struct aml_node *,
+	    struct pckbc_acpi_crs_data *,
+	    int (*)(int, union acpi_resource *, void *));
+int	pckbc_acpi_getgpioirqcount(int, union acpi_resource *, void *);
+int	pckbc_acpi_getgpioirqdata(int, union acpi_resource *, void *);
+void	pckbc_acpi_register_gpio_intrs(struct device *);
+int	pckbc_acpi_gpio_intr_wrapper(void *);
+
+int
+pckbc_acpi_match(struct device *parent, void *match, void *aux)
+{
+	struct pckbc_acpi_softc *sc = pckbc_acpi_find(1);
+	if (sc == NULL)	/* no pckbc@acpi attachment yet */
+		return pckbc_acpi_match_kbd(parent, match, aux);
+	else
+		return pckbc_acpi_match_mou(parent, match, aux, sc);
+}
+
+void
+pckbc_acpi_attach(struct device *parent, struct device *self, void *aux)
+{
+	struct pckbc_acpi_softc *sc = pckbc_acpi_find(2);
+	if (sc == NULL)	/* no second pckbc@acpi attachment yet */
+		return pckbc_acpi_attach_kbd(parent, self, aux);
+	else
+		return pckbc_acpi_attach_mou(parent, self, aux);
+}
+
+const char *pckbc_acpi_cids_kbd[] = {
+	"PNP0303",	/* IBM Enhanced Keyboard (101/102-key, PS/2 Mouse) */
+	NULL
+};
+
+/*
+ * Matching logic for the keyboard node. We want two I/O ports, at least
+ * one interrupt, and either a HID match or a CID match (if explicitly
+ * allowed, or if the interrupt is a GPIO interrupt).
+ */
+int
+pckbc_acpi_match_kbd(struct device *parent, void *match, void *aux)
+{
+	struct acpi_attach_args *aaa = aux;
+	struct cfdata *cf = match;
+	struct pckbc_acpi_crs_data crsdata;
+	int irq, rv;
+
+	if (aaa->aaa_naddr < 2)
+		return 0;
+	rv = acpi_matchhids(aaa, pckbc_acpi_cids_kbd, cf->cf_driver->cd_name);
+	if (rv == 0)
+		return 0;
+	/*
+	 * If this device uses non-GPIO interrupts in an ISA-compatible
+	 * way (i.e. edge interrupt, active high), then do not attach
+	 * unless explicitly required by device flags.
+	 */
+	if (cf->cf_flags & 0x0001)
+		return rv;
+	if (aaa->aaa_nirq != 0) {
+		for (irq = 0; irq < aaa->aaa_nirq; irq++) {
+			if ((aaa->aaa_irq_flags[irq] &
+			    (LR_EXTIRQ_MODE | LR_EXTIRQ_POLARITY)) !=
+			    LR_EXTIRQ_MODE)
+				break;
+		}
+		if (irq == aaa->aaa_nirq)
+			return 0;	/* all legacy */
+	} else {
+		pckbc_acpi_crs_walk(parent, aaa->aaa_node, &crsdata,
+		    pckbc_acpi_getgpioirqcount);
+		if (crsdata.nints == 0)
+			return 0;	/* interrupt, where art thou? */
+	}
+
+	if (rv == 0 && (aaa->aaa_nirq == 0 || (cf->cf_flags & 0x0001) != 0))
+		rv = acpi_matchhids(aaa,
+		    pckbc_acpi_cids_kbd + nitems(pckbc_acpi_cids_kbd) - 2,
+		    cf->cf_driver->cd_name);
+	if (rv == 0)
+		return 0;
+	/* perform the expensive checks last */
+	if (aaa->aaa_nirq == 0) {
+		pckbc_acpi_crs_walk(parent, aaa->aaa_node, &crsdata,
+		    pckbc_acpi_getgpioirqcount);
+		if (crsdata.nints == 0)
+			return 0;
+	}
+	return rv;
+}
+
+const char *pckbc_acpi_cids_mou[] = {
+	"PNP0F03",	/* Microsoft PS/2-style Mouse */
+	"PNP0F13",	/* PS/2 Mouse */
+	NULL
+};
+
+/*
+ * Matching logic for the mouse node. We want a previous keyboard to have
+ * attached, and want an interrupt if the keyboard node didn't have two.
+ */
+int
+pckbc_acpi_match_mou(struct device *parent, void *match, void *aux,
+    struct pckbc_acpi_softc *pasc)
+{
+	struct acpi_attach_args *aaa = aux;
+	struct cfdata *cf = match;
+	struct pckbc_acpi_crs_data crsdata;
+	int rv;
+
+	/*
+	 * We only need to attach the mouse node if the keyboard attachment
+	 * succeeded, we need interrupt information for the aux slot, and
+	 * this acpi node provides it.
+	 */
+	if ((pasc->sc_nints == 0 && pasc->sc_ngpioints == 0) ||
+	    (pasc->sc_nints + pasc->sc_ngpioints == 2))
+		return 0;
+	rv = acpi_matchhids(aaa, pckbc_acpi_cids_mou, cf->cf_driver->cd_name);
+	if (rv == 0)
+		return 0;
+	/* perform the expensive checks last */
+	if (aaa->aaa_nirq == 0) {
+		pckbc_acpi_crs_walk(parent, aaa->aaa_node, &crsdata,
+		    pckbc_acpi_getgpioirqcount);
+		if (crsdata.nints == 0)
+			return 0;
+	}
+	return rv;
+}
+
+int
+pckbc_acpi_activate(struct device *self, int act)
+{
+	struct pckbc_acpi_softc *pasc = (struct pckbc_acpi_softc *)self;
+	struct pckbc_softc *sc = &pasc->sc;
+	int rv = 0;
+
+	switch (act) {
+	case DVACT_SUSPEND:
+		rv = config_activate_children(self, act);
+		pckbc_stop(sc);
+		break;
+	case DVACT_RESUME:
+		pckbc_reset(sc);
+		rv = config_activate_children(self, act);
+		break;
+	default:
+		rv = config_activate_children(self, act);
+		break;
+	}
+	return rv;
+}
+
+void
+pckbc_acpi_attach_kbd(struct device *parent, struct device *self, void *aux)
+{
+	struct pckbc_acpi_softc *pasc = (struct pckbc_acpi_softc *)self;
+	struct pckbc_softc *sc = &pasc->sc;
+	struct acpi_attach_args *aaa = aux;
+	struct pckbc_internal *t;
+	struct pckbc_acpi_crs_data crsdata;
+	bus_space_handle_t ioh_d, ioh_c;
+	int irq, rv;
+
+	if (aaa->aaa_nirq == 0)
+		pckbc_acpi_crs_walk(parent, aaa->aaa_node, &crsdata,
+		    pckbc_acpi_getgpioirqdata);
+
+	printf(" addr 0x%llx/0x%llx 0x%llx/0x%llx", aaa->aaa_addr[0],
+	    aaa->aaa_size[0], aaa->aaa_addr[1], aaa->aaa_size[1]);
+	if (aaa->aaa_nirq == 0) {
+		printf(" gpio irq pin");
+		for (irq = 0; irq < crsdata.nints &&
+		    irq < nitems(pasc->sc_gpioint); irq++)
+			printf(" %d", crsdata.intrs[irq].pin);
+	} else {
+		printf(" irq");
+		for (irq = 0; irq < aaa->aaa_nirq && irq < nitems(pasc->sc_ih);
+		    irq++)
+			printf(" %d", aaa->aaa_irq[irq]);
+	}
+	printf(": \"%s\"\n", aaa->aaa_dev);
+
+	if (aaa->aaa_nirq == 0) {
+		/* Remember GPIO interrupt details for later setup */
+		for (irq = 0; irq < crsdata.nints; irq++) {
+			if (pasc->sc_ngpioints == nitems(pasc->sc_gpioint))
+				break;
+			pasc->sc_gpioint[pasc->sc_ngpioints++] =
+			    crsdata.intrs[irq];	/* struct copy */
+		}
+	} else {
+		for (irq = 0; irq < aaa->aaa_nirq; irq++) {
+			if (pasc->sc_nints == nitems(pasc->sc_ih))
+				break;
+			pasc->sc_ih[pasc->sc_nints] = acpi_intr_establish(
+			    aaa->aaa_irq[irq], aaa->aaa_irq_flags[irq], IPL_TTY,
+			    pckbcintr, pasc, self->dv_xname);
+			if (pasc->sc_ih[pasc->sc_nints] == NULL) {
+				printf("%s: can't establish interrupt %d\n",
+				    self->dv_xname, aaa->aaa_irq[irq]);
+				goto fail_intr;
+			}
+			pasc->sc_nints++;
+		}
+	}
+
+	if (pckbc_is_console(aaa->aaa_bst[0], aaa->aaa_addr[0])) {
+		t = &pckbc_consdata;
+		pckbc_console_attached = 1;
+		/* t->t_cmdbyte was initialized by cnattach */
+	} else {
+		if ((rv = bus_space_map(aaa->aaa_bst[0], aaa->aaa_addr[0], 1, 0,
+		    &ioh_d)) != 0) {
+			printf("%s: couldn't map data port (%d)\n",
+			    self->dv_xname, rv);
+			goto fail_mapd;
+		}
+		if ((rv = bus_space_map(aaa->aaa_bst[1], aaa->aaa_addr[1], 1, 0,
+		    &ioh_c)) != 0) {
+			printf("%s: couldn't map command port (%d)\n",
+			    self->dv_xname, rv);
+			goto fail_mapc;
+		}
+
+		t = malloc(sizeof(*t), M_DEVBUF, M_WAITOK | M_ZERO);
+		/*
+		 * pckbc should theoretically be updated to use separate
+		 * bus_space_tag_t for the data and command ports, since on
+		 * this particular attachment they appear as separate I/O
+		 * resources. But since these are I/O resources, all
+		 * aaa_bst[] are identical, so we can avoid this change
+		 * for the time being as long as the logic in
+		 * acpi_parse_resources() does not change.
+		 */
+		t->t_iot = aaa->aaa_bst[0];
+		t->t_ioh_d = ioh_d;
+		t->t_ioh_c = ioh_c;
+		t->t_cmdbyte = KC8_CPU; /* Enable ports */
+	}
+
+	t->t_sc = sc;
+	sc->id = t;
+
+	/*
+	 * Make sure pckbc@isa will not try to attach.
+	 */
+	{
+		extern int pckbc_acpi;
+		pckbc_acpi = 1;
+	}
+
+	pckbc_attach(sc, 0);
+	config_defer(self, pckbc_acpi_register_gpio_intrs);
+	return;
+
+ fail_mapc:
+	bus_space_unmap(aaa->aaa_bst[0], ioh_d, 1);
+ fail_mapd:
+ fail_intr:
+	if (aaa->aaa_nirq != 0) {
+		for (irq = pasc->sc_nints - 1; irq >= 0; irq--)
+			acpi_intr_disestablish(pasc->sc_ih[irq]);
+		pasc->sc_nints = 0;
+	}
+}
+
+void
+pckbc_acpi_attach_mou(struct device *parent, struct device *self, void *aux)
+{
+	struct pckbc_acpi_softc *pasc = pckbc_acpi_find(1);
+	struct acpi_attach_args *aaa = aux;
+	struct pckbc_acpi_crs_data crsdata;
+	int irq, base;
+
+	if (aaa->aaa_nirq == 0)
+		pckbc_acpi_crs_walk(parent, aaa->aaa_node, &crsdata,
+		    pckbc_acpi_getgpioirqdata);
+
+	if (aaa->aaa_nirq == 0) {
+		printf(" gpio irq pin");
+		for (irq = 0; irq < crsdata.nints &&
+		    irq < nitems(pasc->sc_gpioint) - pasc->sc_ngpioints; irq++)
+			printf(" %d", crsdata.intrs[irq].pin);
+	} else {
+		printf(" irq");
+		for (irq = 0; irq < aaa->aaa_nirq &&
+		    irq < nitems(pasc->sc_ih) - pasc->sc_nints; irq++)
+			printf(" %d", aaa->aaa_irq[irq]);
+	}
+	printf(": \"%s\"\n", aaa->aaa_dev);
+
+	if (aaa->aaa_nirq == 0) {
+		/* Remember GPIO interrupt details for later setup */
+		for (irq = 0; irq < crsdata.nints; irq++) {
+			if (pasc->sc_ngpioints == nitems(pasc->sc_gpioint))
+				break;
+			pasc->sc_gpioint[pasc->sc_ngpioints++] =
+			    crsdata.intrs[irq];	/* struct copy */
+		}
+	} else {
+		base = pasc->sc_nints;
+		for (irq = 0; irq < aaa->aaa_nirq; irq++) {
+			if (pasc->sc_nints == nitems(pasc->sc_ih))
+				break;
+			pasc->sc_ih[pasc->sc_nints] = acpi_intr_establish(
+			    aaa->aaa_irq[irq], aaa->aaa_irq_flags[irq], IPL_TTY,
+			    pckbcintr, pasc, self->dv_xname);
+			if (pasc->sc_ih[pasc->sc_nints] == NULL) {
+				printf("%s: can't establish interrupt %d\n",
+				    self->dv_xname, aaa->aaa_irq[irq]);
+				goto fail_intr;
+			}
+			pasc->sc_nints++;
+		}
+	}
+
+	return;
+
+ fail_intr:
+	if (aaa->aaa_nirq != 0) {
+		for (irq = pasc->sc_nints - 1; irq >= base; irq--)
+			acpi_intr_disestablish(pasc->sc_ih[irq]);
+		pasc->sc_nints = base;
+	}
+}
+
+/*
+ * Register all GPIO interrupts.
+ * This is done after all acpi devices have attached, as the GPIO handler
+ * may attach after us.
+ */
+void
+pckbc_acpi_register_gpio_intrs(struct device *dev)
+{
+	struct pckbc_acpi_softc *sc = (struct pckbc_acpi_softc *)dev;
+	int irq;
+
+	for (irq = 0; irq < sc->sc_ngpioints; irq++) {
+		struct acpi_gpio *gpio = sc->sc_gpioint[irq].node->gpio;
+		if (gpio == NULL) {
+			printf("%s: unable to setup gpio pin %d interrupt\n",
+			    dev->dv_xname, sc->sc_gpioint[irq].pin);
+			continue;
+		}
+		gpio->intr_establish(gpio->cookie,
+		    sc->sc_gpioint[irq].pin, sc->sc_gpioint[irq].flags,
+		    pckbc_acpi_gpio_intr_wrapper, sc);
+	}
+}
+
+/*
+ * Iterate over all pckbc attachments. The `nth' argument tells us which
+ * pckbc@acpi device to return.
+ *
+ * Note that, at `match' time, the device we may end up attaching is not
+ * found, but at `attach' time, it will be found.
+ */
+struct pckbc_acpi_softc *
+pckbc_acpi_find(int nth)
+{
+	extern struct cfdriver pckbc_cd;
+	struct device *sc;
+	int devno;
+
+	for (devno = 0; devno < pckbc_cd.cd_ndevs; devno++) {
+		if ((sc = pckbc_cd.cd_devs[devno]) == NULL)
+			continue;
+		if (sc->dv_cfdata->cf_attach != &pckbc_acpi_ca)
+			continue;
+		if (--nth == 0)
+			return (struct pckbc_acpi_softc *)sc;
+	}
+
+	return NULL;
+}
+
+/*
+ * _CRS resource walker.
+ */
+void
+pckbc_acpi_crs_walk(struct device *acpidev, struct aml_node *basenode,
+    struct pckbc_acpi_crs_data *crsdata,
+    int (*walker)(int, union acpi_resource *, void *))
+{
+	struct aml_value val;
+
+	memset(crsdata, 0, sizeof *crsdata);
+	crsdata->basenode = basenode;
+	if (aml_evalname((struct acpi_softc *)acpidev, basenode, "_CRS",
+	    0, NULL, &val) != 0)
+		return;
+	if (val.type == AML_OBJTYPE_BUFFER && val.length >= 5)
+		aml_parse_resource(&val, walker, crsdata);
+	aml_freevalue(&val);
+}
+
+/*
+ * _CRS walker callback, which counts GPIO interrupts.
+ */
+int
+pckbc_acpi_getgpioirqcount(int crsidx, union acpi_resource *crs, void *arg)
+{
+	struct pckbc_acpi_crs_data *crsdata = arg;
+	struct aml_node *node;
+
+	if (crsdata->nints == nitems(crsdata->intrs))
+		return 0;
+
+	switch (AML_CRSTYPE(crs)) {
+	case LR_GPIO:
+		if (crs->lr_gpio.type != LR_GPIO_INT)
+			break;
+		node = aml_searchname(crsdata->basenode,
+		    (char *)&crs->pad[crs->lr_gpio.res_off]);
+		if (node != NULL)
+			crsdata->nints++;
+		break;
+	}
+	return 0;
+}
+
+/*
+ * _CRS walker callback, which registers GPIO interrupt details.
+ */
+int
+pckbc_acpi_getgpioirqdata(int crsidx, union acpi_resource *crs, void *arg)
+{
+	struct pckbc_acpi_crs_data *crsdata = arg;
+	struct aml_node *node;
+
+	if (crsdata->nints == nitems(crsdata->intrs))
+		return 0;
+
+	switch (AML_CRSTYPE(crs)) {
+	case LR_GPIO:
+		if (crs->lr_gpio.type != LR_GPIO_INT)
+			break;
+		node = aml_searchname(crsdata->basenode,
+		    (char *)&crs->pad[crs->lr_gpio.res_off]);
+		if (node != NULL) {
+			crsdata->intrs[crsdata->nints].node = node;
+			crsdata->intrs[crsdata->nints].pin =
+			    *(uint16_t *)&crs->pad[crs->lr_gpio.pin_off];
+			crsdata->intrs[crsdata->nints].flags =
+			    crs->lr_gpio.tflags;
+			crsdata->nints++;
+		}
+		break;
+	}
+	return 0;
+}
+
+/*
+ * Wrapper for GPIO interrupts, to enforce IPL_TTY.
+ */
+int
+pckbc_acpi_gpio_intr_wrapper(void *arg)
+{
+	int s, rv;
+
+	s = spltty();
+	rv = pckbcintr(arg);
+	splx(s);
+	return rv;
+}
Index: sys/dev/ic/pckbc.c
===================================================================
RCS file: /OpenBSD/src/sys/dev/ic/pckbc.c,v
diff -u -p -u -p -r1.55 pckbc.c
--- sys/dev/ic/pckbc.c	26 Aug 2023 15:01:00 -0000	1.55
+++ sys/dev/ic/pckbc.c	31 Jan 2025 12:05:19 -0000
@@ -89,6 +89,10 @@ int pckbc_console_attached;
 int pckbc_console;
 static struct pckbc_slotdata pckbc_cons_slotdata;
 
+#ifdef __HAVE_ACPI
+int pckbc_acpi;
+#endif
+
 static int pckbc_wait_output(bus_space_tag_t, bus_space_handle_t);
 
 static int pckbc_get8042cmd(struct pckbc_internal *);
Index: sys/dev/isa/pckbc_isa.c
===================================================================
RCS file: /OpenBSD/src/sys/dev/isa/pckbc_isa.c,v
diff -u -p -u -p -r1.19 pckbc_isa.c
--- sys/dev/isa/pckbc_isa.c	18 Aug 2015 06:54:00 -0000	1.19
+++ sys/dev/isa/pckbc_isa.c	31 Jan 2025 12:05:19 -0000
@@ -56,6 +56,15 @@ pckbc_isa_match(struct device *parent, v
 	bus_space_handle_t ioh_d, ioh_c;
 	int res;
 
+#ifdef __HAVE_ACPI
+	/* Don't try anything if pckbc@acpi has claimed the keyboard. */
+	{
+		extern int pckbc_acpi;
+		if (pckbc_acpi)
+			return 0;
+	}
+#endif
+
 	/* If values are hardwired to something that they can't be, punt. */
 	if ((ia->ia_iobase != IOBASEUNK && ia->ia_iobase != IO_KBD) ||
 	    ia->ia_maddr != MADDRUNK ||