tech-kern archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

HID over I2C: ACPI changes



Hello,
newer laptops have touchpad connected to an I2C controller instead of
PS/2 or USB, and we don't support this yet.
To support this we need a few adjustements to existing subsystems.
This mail is about the changes to ACPI. Changes on I2C and USB/HID will
follow in separate mails.

On recent laptops the touchpad (and eventually other HID devices, including
keyboards) implement the HID over I2C protocol:
https://msdn.microsoft.com/en-us/library/windows/hardware/dn642101%28v=vs.85%29.aspx

On Intel hardware it is connected to a I2C controller implemented in the
PCH, and a GPIO of the PCH used as interrupt pin. The ACPI describe
to which i2c bus HID devices are connected to, with which I2C address and
interrupt mappings. the I2C controller itself needs a PCI or ACPI attachement
depending on hardware.

So the I2C driver needs to walk to ACPI table to fill the property array
to be passed to the I2C bus. For this I implemented a helper
acpi_enter_i2c_devs() in dev/acpi/acpi_i2c.[ch] (see attached).
It takes the acpi_devnode of the i2c controller as input, and returns
prop_array_t of I2C devices to be attached to the child I2C bus. See attached
dwiic_pci.c for an example.

We have to fill the prop_array_t with name and compat strings. The ACPI
name is used for the name property. Obviously the addr property is the
I2C address we did find in the ACPI node. The cookie property is set
to the ad_handle of the acpi_devnode (more on this below).

Some devices have specific properties. For HID devices this is the HID
descriptor address, found in the _DSM. In a device tree this would
be named "hid-descr-addr". We could callback from the I2C driver to get
this information, but it's more natural to provide it as a property
before attaching the i2c driver (it's probably also more natural on
a FDT-based platform). So we need to add device-specific ACPI parsing and
device properly.
For this I added a table acpi_i2c_ids[], which for a given HID or CID
can provide an extra ACPI parsing function. acpi_enter_i2c_hid() will
add a "hid-descr-addr" property with the value found the _DSM.

For the I2C driver to attach we have to provide a compatible property
with the appropriate string. On the i2c side, this comes from the
linux device tree, as used e.g. in the ARM world. As we already have the
acpi_i2c_ids[] table, I use it to translate from ACPI HID or CID to
a FDT compatible property.

The next problem is the interrupt. the HID device can trigger and interrupt
via a GPIO and we'd like to have an interrupt handler in the device driver.
The existing iic_smbus_intr() framework is not appropriate because
the interrupt is registered in the bus driver, and calls all i2c
device driver handlers for the bus. Here we can have several devices on the
bus, each with its own interrupt line.

This is why we have the ad_handle as cookie in the I2C property.
I added acpi_intr_establish(), acpi_intr_disestablish() and acpi_intr_string()
in acpi_util.c and acpi_intr.h (a separate .h so clients don't need the whole
ACPI includes), see attached. 
The I2C drivers will call theses to get their interrupt handler registerred.

I'm not completely happy with this, as we have to #ifdef I2C drivers for
ACPI availability. Several I2C drivers have #ifdef FDT, which I don't
like much either. lm75 even has a #ifdef macppc for openfirmware.
I think we should have
void *platform_intr_establish(device_t, uint64_t,
			unsigned int (*intr)(void *), void *);
void  platform_intr_disestablish(void *, unsigned int (*intr)(void *));
const char *platform_intr_string(void *, char *, size_t len);
(or some variant)
always available for MI drivers. By default platform_intr_establish() and
platform_intr_string() would return NULL, and platform_intr_disestablish()
do nothing. MD could provide these functions where appropriate
(e.g call acpi_intr_* on x86, fdtbus_intr_* on FDT platforms, and so on).

But for the time being, I think calling acpi_intr* from i2c drivers is the
less intrusive way to get it working, if we want to pull it up to netbsd-8
(I think we have to).

Comments ?

-- 
Manuel Bouyer <bouyer%antioche.eu.org@localhost>
     NetBSD: 26 ans d'experience feront toujours la difference
--
/* $NetBSD: dwiic.c,v 1.24 2017/08/17 20:41:16 kettenis Exp $ */

/*-
 * Copyright (c) 2017 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Manuel Bouyer.
 *
 * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``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 FOUNDATION OR CONTRIBUTORS
 * 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/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: acpi_debug.c,v 1.5 2014/02/25 18:30:09 pooka Exp $");

#include <dev/acpi/acpireg.h>
#include <dev/acpi/acpivar.h>
#include <dev/acpi/acpi_i2c.h>

static void
acpi_enter_i2c_hid(struct acpi_devnode *devnode, prop_dictionary_t dev)
{
	ACPI_OBJECT_LIST arg;
	ACPI_OBJECT obj[4];
	ACPI_OBJECT *osc;
	ACPI_BUFFER buf;
	ACPI_STATUS rv;
	/* 3cdff6f7-4267-4555-ad05-b30a3d8938de */
	static uint8_t i2c_hid_guid[] = {
		0xF7, 0xF6, 0xDF, 0x3C, 0x67, 0x42, 0x55, 0x45,
		0xAD, 0x05, 0xB3, 0x0A, 0x3D, 0x89, 0x38, 0xDE,
	};

	arg.Count = 4;
	arg.Pointer = obj;

	obj[0].Type = ACPI_TYPE_BUFFER;
	obj[0].Buffer.Length = sizeof(i2c_hid_guid);
	obj[0].Buffer.Pointer = i2c_hid_guid;

	/* rev */
	obj[1].Type = ACPI_TYPE_INTEGER;
	obj[1].Integer.Value = 1;

	/* func */
	obj[2].Type = ACPI_TYPE_INTEGER;
	obj[2].Integer.Value = 1;

	obj[3].Type = ACPI_TYPE_ANY;
	obj[3].Buffer.Length = 0;

	buf.Pointer = NULL;
	buf.Length = ACPI_ALLOCATE_LOCAL_BUFFER;

	rv = AcpiEvaluateObject(devnode->ad_handle, "_DSM", &arg, &buf);

	if (ACPI_FAILURE(rv)) {
		aprint_error("failed to evaluate _DSM for %s: %s\n",
		    devnode->ad_name, AcpiFormatException(rv));
		return;
	}

	osc = buf.Pointer;
	if (osc->Type != ACPI_TYPE_INTEGER) {
		aprint_error("bad _DSM return type %d for %s\n",
		    osc->Type, devnode->ad_name);
		return;
	}
	prop_dictionary_set_uint32(dev, "hid-descr-addr", osc->Integer.Value);
}

struct acpi_i2c_id {
	const char *id;
	const char *compat;
	const int compatlen;
	void (*parse)(struct acpi_devnode *, prop_dictionary_t);
};

static const struct acpi_i2c_id acpi_i2c_ids[] = {
	{
		.id = "PNP0C50",
		.compat = "hid-over-i2c",
		.compatlen = 13,
		.parse = acpi_enter_i2c_hid
	},
	{
		.id = "ACPI0C50",
		.compat = "hid-over-i2c",
		.compatlen = 13,
		.parse = acpi_enter_i2c_hid
	},
	{
		.id = NULL,
		.compat = NULL,
		.compatlen = 0,
		.parse = NULL
	}
};

static const struct acpi_i2c_id *
acpi_i2c_search(const char *name)
{
	int i;
	for (i = 0; acpi_i2c_ids[i].id != NULL; i++) {
		if (strcmp(name, acpi_i2c_ids[i].id) == 0)
			return &acpi_i2c_ids[i];
	}
	return NULL;
}

struct acpi_i2c_context {
	uint16_t i2c_addr;
};

static ACPI_STATUS
acpi_i2c_resource_parse_callback(ACPI_RESOURCE *res, void *context)
{
	struct acpi_i2c_context *i2cc = context;

	switch (res->Type) {
	case ACPI_RESOURCE_TYPE_END_TAG:
		break;
	case ACPI_RESOURCE_TYPE_SERIAL_BUS:
		switch (res->Data.I2cSerialBus.Type) {
		case ACPI_RESOURCE_SERIAL_TYPE_I2C:
			i2cc->i2c_addr = res->Data.I2cSerialBus.SlaveAddress;
			break;
		}
		break;
	case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
		break;
	default:
		printf("ressource type 0x%x ignored\n", res->Type);
	}
	return_ACPI_STATUS(AE_OK);
}

static void
acpi_enter_i2c_device(struct acpi_devnode *ad, prop_array_t array)
{
	prop_dictionary_t dev;
	struct acpi_i2c_context i2cc;
	ACPI_STATUS rv;
	int cidi;
	ACPI_PNP_DEVICE_ID_LIST *idlist;
	const char *name;
	static const struct acpi_i2c_id *i2c_id;

	memset(&i2cc, 0, sizeof(i2cc));
	rv = AcpiWalkResources(ad->ad_handle, "_CRS",
	     acpi_i2c_resource_parse_callback, &i2cc);
	if (ACPI_FAILURE(rv)) {
		aprint_error("ACPI: unable to get resources "
		   "for %s: %s\n", ad->ad_name,
		   AcpiFormatException(rv));
		return;
	}
	if (i2cc.i2c_addr == 0)
		return;
	dev = prop_dictionary_create();
	if (dev == NULL) {
		aprint_error("ignoring device %s (no memory)\n",
		    ad->ad_name);
		return;
	}
	if ((ad->ad_devinfo->Valid &  ACPI_VALID_HID) == 0)
		name = ad->ad_name;
	else
		name = ad->ad_devinfo->HardwareId.String;
	prop_dictionary_set_cstring(dev, "name", name);
	prop_dictionary_set_uint32(dev, "addr", i2cc.i2c_addr);
	prop_dictionary_set_uint64(dev, "cookie", (uintptr_t)ad->ad_handle);
	/* first search by name, then by CID */
	i2c_id = acpi_i2c_search(name);
	idlist = &ad->ad_devinfo->CompatibleIdList;
	for (cidi = 0;
	    cidi < idlist->Count && i2c_id == NULL;
	    cidi++) {
		i2c_id = acpi_i2c_search(idlist->Ids[cidi].String);
	}
	if (i2c_id != NULL) {
		if (i2c_id->compat != NULL) {
			prop_data_t data;
			data = prop_data_create_data(i2c_id->compat,
			    i2c_id->compatlen);
			prop_dictionary_set(dev, "compatible", data);
			prop_object_release(data);
		}
		if (i2c_id->parse != NULL)
			i2c_id->parse(ad, dev);
	}
	prop_array_add(array, dev);
	prop_object_release(dev);
}


prop_array_t
acpi_enter_i2c_devs(struct acpi_devnode *devnode)
{
	struct acpi_devnode *ad;
	prop_array_t array = prop_array_create();

	if (array == NULL)
		return NULL;

	SIMPLEQ_FOREACH(ad, &devnode->ad_child_head, ad_child_list) {
		if ((ad->ad_devinfo->Valid &  ACPI_VALID_STA) == 0)
			continue;
		if ((ad->ad_devinfo->CurrentStatus &  ACPI_STA_OK) !=
		    ACPI_STA_OK)
			continue;
		if (ad->ad_devinfo->Type != ACPI_TYPE_DEVICE)
			continue;
		acpi_enter_i2c_device(ad, array);
	}
	return array;
}
/* $NetBSD: dwiic.c,v 1.24 2017/08/17 20:41:16 kettenis Exp $ */

/*-
 * Copyright (c) 2017 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Manuel Bouyer.
 *
 * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``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 FOUNDATION OR CONTRIBUTORS
 * 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.
 */


#ifndef _SYS_DEV_ACPI_ACPI_I2C_H
#define _SYS_DEV_ACPI_ACPI_I2C_H
#include <prop/proplib.h>

prop_array_t acpi_enter_i2c_devs(struct acpi_devnode *);
#endif /*  _SYS_DEV_ACPI_ACPI_I2C_H */
/* $NetBSD: dwiic.c,v 1.24 2017/08/17 20:41:16 kettenis Exp $ */

/*-
 * Copyright (c) 2017 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Manuel Bouyer.
 *
 * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``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 FOUNDATION OR CONTRIBUTORS
 * 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.
 */
/*
 * Synopsys DesignWare I2C controller, PCI front-end
 */

#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ahcisata_core.c,v 1.59 2017/10/20 07:06:07 jdolecek Exp $");

#include <sys/param.h>
#include <sys/systm.h>

#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcidevs.h>

#include <dev/acpi/acpivar.h>
#include <dev/acpi/acpi_pci.h>
#include <dev/acpi/acpi_util.h>
#include <dev/acpi/acpi_i2c.h>

#include <dev/ic/dwiic_var.h>
#include <arch/x86/pci/lpssreg.h>

//#define DWIIC_DEBUG

#ifdef DWIIC_DEBUG
#define DPRINTF(x) printf x
#else
#define DPRINTF(x)
#endif

struct pci_dwiic_softc {
	struct dwiic_softc	sc_dwiic;
	pci_chipset_tag_t	sc_pc;
	pcitag_t		sc_ptag;
	struct acpi_devnode	*sc_acpinode;
};

static uint32_t
lpss_read(struct pci_dwiic_softc *sc, int offset)
{
	u_int32_t b = bus_space_read_4(sc->sc_dwiic.sc_iot, sc->sc_dwiic.sc_ioh,
	     offset);
	return b;
}

static void
lpss_write(struct pci_dwiic_softc *sc, int offset, uint32_t val)
{
	bus_space_write_4(sc->sc_dwiic.sc_iot, sc->sc_dwiic.sc_ioh,
	    offset, val);
}

static int	pci_dwiic_match(device_t, cfdata_t, void *);
static void	pci_dwiic_attach(device_t, device_t, void *);
static bool	dwiic_pci_power(struct dwiic_softc *, int);

CFATTACH_DECL_NEW(pcidwiic, sizeof(struct pci_dwiic_softc),
    pci_dwiic_match, pci_dwiic_attach, dwiic_detach, NULL);


int
pci_dwiic_match(device_t parent, cfdata_t match, void *aux)
{
	struct pci_attach_args *pa = aux;

	if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_INTEL)
		return 0;

	if (PCI_PRODUCT(pa->pa_id) < PCI_PRODUCT_INTEL_100SERIES_LP_I2C_0 ||
	    PCI_PRODUCT(pa->pa_id) > PCI_PRODUCT_INTEL_100SERIES_LP_I2C_3)
		return 0;

	return 1;
}

void
pci_dwiic_attach(device_t parent, device_t self, void *aux)
{
	struct pci_dwiic_softc *sc = device_private(self);
	struct pci_attach_args *pa = aux;
	const char *intrstr;
	pci_intr_handle_t intrhandle;
	char intrbuf[PCI_INTRSTR_LEN];
	pcireg_t memtype;
	pcireg_t csr;
	uint32_t caps;

	sc->sc_dwiic.sc_dev = self;
	sc->sc_dwiic.sc_power = dwiic_pci_power;
	sc->sc_dwiic.sc_type = dwiic_type_sunrisepoint;

	sc->sc_pc = pa->pa_pc;
	sc->sc_ptag = pa->pa_tag;

	/* register access not enabled by BIOS */
	csr = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG);
	pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG,
	    csr | PCI_COMMAND_MEM_ENABLE);

	memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, PCI_BAR0);
	if (pci_mapreg_map(pa, PCI_BAR0, memtype, 0, &sc->sc_dwiic.sc_iot,
	    &sc->sc_dwiic.sc_ioh, NULL, NULL) != 0) {
		aprint_error(": can't map register space\n");
		goto out;
	}
	dwiic_pci_power(&sc->sc_dwiic, 1);

	caps = lpss_read(sc, LPSS_CAP);

	aprint_naive(": I2C controller\n");
	aprint_normal(": I2C controller instance %d\n",
	    (int)(caps & LPSS_CAP_INSTANCE));

	if (pci_intr_map(pa, &intrhandle)) {
		aprint_error_dev(self, "can't map interrupt\n");
		goto out;
	}
	intrstr = pci_intr_string(pa->pa_pc, intrhandle,
	    intrbuf, sizeof(intrbuf));

	sc->sc_dwiic.sc_ih = pci_intr_establish(pa->pa_pc, intrhandle,
	    IPL_VM, dwiic_intr, sc);
	if (sc->sc_dwiic.sc_ih == NULL) {
		aprint_error_dev(self, "couldn't establish interrupt");
		if (intrstr != NULL)
			aprint_error(" at %s", intrstr);
		aprint_error("\n");
		goto out;
	}
	aprint_normal_dev(self, "interrupting at %s\n", intrstr);

	lpss_write(sc, LPSS_RESET, LPSS_RESET_CTRL_REL);
	lpss_write(sc, LPSS_REMAP_LO,
	    pci_conf_read(sc->sc_pc, sc->sc_ptag, PCI_BAR0));
	lpss_write(sc, LPSS_REMAP_HI,
	    pci_conf_read(sc->sc_pc, sc->sc_ptag, PCI_BAR0 + 0x4));

	sc->sc_acpinode = acpi_pcidev_find(0 /*XXX segment*/,
	    pa->pa_bus, pa->pa_device, pa->pa_function);

	if (sc->sc_acpinode) {
		sc->sc_dwiic.sc_iba.iba_child_devices = 
		    acpi_enter_i2c_devs(sc->sc_acpinode);
	} else {
		aprint_verbose_dev(self, "no matching ACPI node\n");
	}

	pmf_device_register(self, dwiic_suspend, dwiic_resume);

	dwiic_attach(&sc->sc_dwiic);

out:
	return;
}

static bool
dwiic_pci_power(struct dwiic_softc *dwsc, int power)
{
	struct pci_dwiic_softc *sc = (void *)dwsc;
	int pmreg;

	if (pci_get_capability(sc->sc_pc, sc->sc_ptag, PCI_CAP_PWRMGMT,
	    &pmreg, NULL)) {
		DPRINTF(("%s: power status 0x%x", device_xname(dwsc->sc_dev),
		    pci_conf_read(sc->sc_pc, sc->sc_ptag, pmreg + PCI_PMCSR)));
		pci_conf_write(sc->sc_pc, sc->sc_ptag, pmreg + PCI_PMCSR,
		    power ? PCI_PMCSR_STATE_D0 : PCI_PMCSR_STATE_D3);
		DELAY(10000); /* 10 milliseconds */
		DPRINTF((" -> 0x%x\n", 
		    pci_conf_read(sc->sc_pc, sc->sc_ptag, pmreg + PCI_PMCSR)));
	}
	lpss_write(sc, LPSS_CLKGATE,
	    power ? LPSS_CLKGATE_CTRL_ON : LPSS_CLKGATE_CTRL_OFF);
	return true;
}
/* $NetBSD: dwiic.c,v 1.24 2017/08/17 20:41:16 kettenis Exp $ */

/*-
 * Copyright (c) 2017 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Manuel Bouyer.
 *
 * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``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 FOUNDATION OR CONTRIBUTORS
 * 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.
 */
void *		acpi_intr_establish(device_t, uint64_t,
		    unsigned int (*intr)(void *), void *);
void 		acpi_intr_disestablish(void *, unsigned int (*intr)(void *));
const char * 	acpi_intr_string(void *, char *, size_t len);
Index: dev/acpi/acpi_util.c
===================================================================
RCS file: /cvsroot/src/sys/dev/acpi/acpi_util.c,v
retrieving revision 1.8
diff -u -p -u -r1.8 acpi_util.c
--- dev/acpi/acpi_util.c	21 Jun 2011 03:37:21 -0000	1.8
+++ dev/acpi/acpi_util.c	1 Dec 2017 15:19:30 -0000
@@ -68,9 +68,11 @@
 __KERNEL_RCSID(0, "$NetBSD: acpi_util.c,v 1.8 2011/06/21 03:37:21 jruoho Exp $");
 
 #include <sys/param.h>
+#include <sys/kmem.h>
 
 #include <dev/acpi/acpireg.h>
 #include <dev/acpi/acpivar.h>
+#include <dev/acpi/acpi_intr.h>
 
 #define _COMPONENT	ACPI_BUS_COMPONENT
 ACPI_MODULE_NAME	("acpi_util")
@@ -506,3 +508,63 @@ out:
 
 	return ci;
 }
+
+struct acpi_irq_handler {
+	ACPI_HANDLE aih_hdl;
+	uint32_t aih_irq;
+	int (*aih_intr)(void *);
+};
+
+void *
+acpi_intr_establish(device_t dev, uint64_t c,
+    unsigned int (*intr)(void *), void *iarg)
+{
+	ACPI_STATUS rv;
+	ACPI_HANDLE hdl = (void *)c;
+	struct acpi_resources res;
+	struct acpi_irq *irq;
+	struct acpi_irq_handler *aih = NULL;
+
+	rv = acpi_resource_parse(dev, hdl, "_CRS", &res,
+	    &acpi_resource_parse_ops_quiet);
+	if (ACPI_FAILURE(rv))
+		return NULL;
+
+	irq = acpi_res_irq(&res, 0);
+	if (irq == NULL)
+		goto end;
+
+	aih = kmem_alloc(sizeof(struct acpi_irq_handler), KM_NOSLEEP);
+	if (aih == NULL)
+		goto end;
+
+	aih->aih_hdl = hdl;
+	aih->aih_irq = irq->ar_irq;
+	rv = AcpiOsInstallInterruptHandler(irq->ar_irq, intr, iarg);
+	if (ACPI_FAILURE(rv)) {
+		kmem_free(aih, sizeof(struct acpi_irq_handler));
+		aih = NULL;
+	}
+end:
+	acpi_resource_cleanup(&res);
+	return aih;
+}
+
+void
+acpi_intr_disestablish(void *c, unsigned int (*intr)(void *))
+{
+	struct acpi_irq_handler *aih = c;
+
+	AcpiOsRemoveInterruptHandler(aih->aih_irq, intr);
+	kmem_free(aih, sizeof(struct acpi_irq_handler));
+	return;
+}
+
+const char *
+acpi_intr_string(void *c, char *buf, size_t size)
+{
+	struct acpi_irq_handler *aih = c;
+	intr_handle_t ih = aih->aih_irq;
+
+	return intr_string(ih, buf, size);
+}


Home | Main Index | Thread Index | Old Index