Intel 100 Series laptops have the DesignWare I2C controller attaching via PCI instead of ACPI, so move the guts of dwiic(4) into ic/ and add dwiic_acpi and dwiic_pci files. Unfortunately the PCI attachment still needs to reach into ACPI to discover client devices.
There is still an issue with interrupt setup on these machines (unrelated to dwiic as far as I can tell), where ioapic interrupts registered for client devices like ihidev never fire, but those for dwiic (via PCI) do. This diff sets a flag for PCI-attached dwiic devices so that ihidev can do polling for client devices, which will come in a separate diff. I need some tests on machines where dwiic/ihidev attaches over ACPI. Index: sys/arch/amd64/conf/GENERIC =================================================================== RCS file: /cvs/src/sys/arch/amd64/conf/GENERIC,v retrieving revision 1.447 diff -u -p -u -p -r1.447 GENERIC --- sys/arch/amd64/conf/GENERIC 28 Oct 2017 01:37:52 -0000 1.447 +++ sys/arch/amd64/conf/GENERIC 3 Nov 2017 16:56:20 -0000 @@ -135,6 +135,7 @@ iic* at nviic? amdpm* at pci? # AMD-7xx/8111 and NForce SMBus controller iic* at amdpm? dwiic* at acpi? # DesignWare Synopsys i2c controller +dwiic* at pci? iic* at dwiic? itherm* at pci? # Intel 3400 Thermal Sensor Index: sys/conf/files =================================================================== RCS file: /cvs/src/sys/conf/files,v retrieving revision 1.654 diff -u -p -u -p -r1.654 files --- sys/conf/files 3 Nov 2017 13:01:20 -0000 1.654 +++ sys/conf/files 3 Nov 2017 16:56:21 -0000 @@ -524,6 +524,10 @@ file dev/spdmem.c spdmem device oaic: scsi file dev/ic/aic6250.c oaic +# Synopsys DesignWare I2C controller +device dwiic: i2cbus +file dev/ic/dwiic.c dwiic + # legitimate pseudo-devices pseudo-device vnd: disk pseudo-device rd: disk Index: sys/dev/pci/files.pci =================================================================== RCS file: /cvs/src/sys/dev/pci/files.pci,v retrieving revision 1.329 diff -u -p -u -p -r1.329 files.pci --- sys/dev/pci/files.pci 21 Jan 2017 10:58:15 -0000 1.329 +++ sys/dev/pci/files.pci 3 Nov 2017 16:56:21 -0000 @@ -807,5 +807,9 @@ file dev/pci/xspd.c xspd attach virtio at pci with virtio_pci file dev/pci/virtio_pci.c virtio_pci +# Synopsys DesignWare I2C Controller +attach dwiic at pci with dwiic_pci +file dev/pci/dwiic_pci.c dwiic_pci + include "dev/pci/files.agp" include "dev/pci/drm/files.drm" Index: sys/dev/pci/dwiic_pci.c =================================================================== RCS file: sys/dev/pci/dwiic_pci.c diff -N sys/dev/pci/dwiic_pci.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sys/dev/pci/dwiic_pci.c 3 Nov 2017 16:56:21 -0000 @@ -0,0 +1,204 @@ +/* $OpenBSD$ */ +/* + * Synopsys DesignWare I2C controller + * PCI attachment + * + * Copyright (c) 2015-2017 joshua stein <j...@openbsd.org> + * + * Permission to use, copy, modify, and/or 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. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/kthread.h> + +#include <dev/pci/pcidevs.h> +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> + +#include <dev/ic/dwiicvar.h> + +/* 13.3: I2C Additional Registers Summary */ +#define LPSS_RESETS 0x204 +#define LPSS_RESETS_I2C (1 << 0) | (1 << 1) +#define LPSS_RESETS_IDMA (1 << 2) +#define LPSS_ACTIVELTR 0x210 +#define LPSS_IDLELTR 0x214 +#define LPSS_CAPS 0x2fc +#define LPSS_CAPS_NO_IDMA (1 << 8) +#define LPSS_CAPS_TYPE_SHIFT 4 +#define LPSS_CAPS_TYPE_MASK (0xf << LPSS_CAPS_TYPE_SHIFT) + +int dwiic_pci_match(struct device *, void *, void *); +void dwiic_pci_attach(struct device *, struct device *, void *); +int dwiic_pci_activate(struct device *, int); +void dwiic_pci_bus_scan(struct device *, + struct i2cbus_attach_args *, void *); + +#include "acpi.h" +#if NACPI > 0 +struct aml_node *acpi_pci_match(struct device *dev, struct pci_attach_args *pa); +#endif + +struct cfattach dwiic_pci_ca = { + sizeof(struct dwiic_softc), + dwiic_pci_match, + dwiic_pci_attach, + NULL, + dwiic_pci_activate, +}; + +const struct pci_matchid dwiic_pci_ids[] = { + { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_100SERIES_LP_I2C_1 }, + { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_100SERIES_LP_I2C_2 }, +}; + +int +dwiic_pci_match(struct device *parent, void *match, void *aux) +{ + return (pci_matchbyid(aux, dwiic_pci_ids, nitems(dwiic_pci_ids))); +} + +void +dwiic_pci_attach(struct device *parent, struct device *self, void *aux) +{ + struct dwiic_softc *sc = (struct dwiic_softc *)self; + struct pci_attach_args *pa = aux; + bus_size_t iosize; + pci_intr_handle_t ih; + const char *intrstr = NULL; + uint8_t type; + + memcpy(&sc->sc_paa, pa, sizeof(sc->sc_paa)); + + pci_set_powerstate(pa->pa_pc, pa->pa_tag, PCI_PMCSR_STATE_D0); + + if (pci_mapreg_map(pa, PCI_MAPREG_START, PCI_MAPREG_MEM_TYPE_64BIT, 0, + &sc->sc_iot, &sc->sc_ioh, NULL, &iosize, 0)) { + printf(": can't map mem space\n"); + return; + } + + sc->sc_caps = bus_space_read_4(sc->sc_iot, sc->sc_ioh, LPSS_CAPS); + type = sc->sc_caps & LPSS_CAPS_TYPE_MASK; + type >>= LPSS_CAPS_TYPE_SHIFT; + if (type != 0) { + printf(": type %d not supported\n", type); + return; + } + + /* un-reset - page 958 */ + bus_space_write_4(sc->sc_iot, sc->sc_ioh, LPSS_RESETS, + (LPSS_RESETS_I2C | LPSS_RESETS_IDMA)); + + /* fetch timing parameters */ + sc->ss_hcnt = dwiic_read(sc, DW_IC_SS_SCL_HCNT); + sc->ss_lcnt = dwiic_read(sc, DW_IC_SS_SCL_LCNT); + sc->fs_hcnt = dwiic_read(sc, DW_IC_FS_SCL_HCNT); + sc->fs_lcnt = dwiic_read(sc, DW_IC_FS_SCL_LCNT); + sc->sda_hold_time = dwiic_read(sc, DW_IC_SDA_HOLD); + + if (dwiic_init(sc)) { + printf(": failed initializing\n"); + return; + } + + /* leave the controller disabled */ + dwiic_write(sc, DW_IC_INTR_MASK, 0); + dwiic_enable(sc, 0); + dwiic_read(sc, DW_IC_CLR_INTR); + + /* install interrupt handler */ + sc->sc_poll = 1; + if (pci_intr_map(&sc->sc_paa, &ih) == 0) { + intrstr = pci_intr_string(sc->sc_paa.pa_pc, ih); + sc->sc_ih = pci_intr_establish(sc->sc_paa.pa_pc, ih, IPL_BIO, + dwiic_intr, sc, sc->sc_dev.dv_xname); + if (sc->sc_ih != NULL) { + printf(": %s", intrstr); + sc->sc_poll = 0; + } + } + if (sc->sc_poll) + printf(": polling"); + + printf("\n"); + + rw_init(&sc->sc_i2c_lock, "iiclk"); + + /* setup and attach iic bus */ + sc->sc_i2c_tag.ic_cookie = sc; + sc->sc_i2c_tag.ic_acquire_bus = dwiic_i2c_acquire_bus; + sc->sc_i2c_tag.ic_release_bus = dwiic_i2c_release_bus; + sc->sc_i2c_tag.ic_exec = dwiic_i2c_exec; + sc->sc_i2c_tag.ic_intr_establish = dwiic_i2c_intr_establish; + sc->sc_i2c_tag.ic_intr_string = dwiic_i2c_intr_string; + + bzero(&sc->sc_iba, sizeof(sc->sc_iba)); + sc->sc_iba.iba_name = "iic"; + sc->sc_iba.iba_tag = &sc->sc_i2c_tag; + sc->sc_iba.iba_bus_scan = dwiic_pci_bus_scan; + sc->sc_iba.iba_bus_scan_arg = sc; + + config_found((struct device *)sc, &sc->sc_iba, iicbus_print); + + return; +} + +int +dwiic_pci_activate(struct device *self, int act) +{ + struct dwiic_softc *sc = (struct dwiic_softc *)self; + + switch (act) { + case DVACT_WAKEUP: + bus_space_write_4(sc->sc_iot, sc->sc_ioh, LPSS_RESETS, + (LPSS_RESETS_I2C | LPSS_RESETS_IDMA)); + + dwiic_init(sc); + + break; + } + + dwiic_activate(self, act); + + return 0; +} + +void +dwiic_pci_bus_scan(struct device *iic, struct i2cbus_attach_args *iba, + void *aux) +{ + struct dwiic_softc *sc = (struct dwiic_softc *)aux; + + sc->sc_iic = iic; + +#if NACPI > 0 + { + struct aml_node *n = acpi_pci_match((struct device *)aux, + &sc->sc_paa); + if (n == NULL) + return; + + /* + * XXX: until we can figure out why interrupts don't arrive for + * i2c slave devices on intel 100 series and newer, force + * polling for ihidev. + */ + sc->sc_poll_ihidev = 1; + + aml_find_node(n, "_HID", dwiic_acpi_found_hid, sc); + } +#endif +} Index: share/man/man4/dwiic.4 =================================================================== RCS file: /cvs/src/share/man/man4/dwiic.4,v retrieving revision 1.2 diff -u -p -u -p -r1.2 dwiic.4 --- share/man/man4/dwiic.4 30 Jul 2016 15:44:45 -0000 1.2 +++ share/man/man4/dwiic.4 3 Nov 2017 16:56:21 -0000 @@ -22,6 +22,7 @@ .Nd Synopsys DesignWare I2C controller .Sh SYNOPSIS .Cd "dwiic* at acpi?" +.Cd "dwiic* at pci?" .Cd "iic* at dwiic?" .Sh DESCRIPTION The @@ -33,6 +34,7 @@ framework. .Sh SEE ALSO .Xr acpi 4 , .Xr iic 4 +.Xr pci 4 .Sh HISTORY The .Nm Index: sys/dev/acpi/acpi.c =================================================================== RCS file: /cvs/src/sys/dev/acpi/acpi.c,v retrieving revision 1.332 diff -u -p -u -p -r1.332 acpi.c --- sys/dev/acpi/acpi.c 17 Aug 2017 05:16:27 -0000 1.332 +++ sys/dev/acpi/acpi.c 3 Nov 2017 16:56:26 -0000 @@ -71,7 +71,7 @@ int acpi_hasprocfvs; #define ACPIEN_RETRIES 15 -void acpi_pci_match(struct device *, struct pci_attach_args *); +struct aml_node *acpi_pci_match(struct device *, struct pci_attach_args *); pcireg_t acpi_pci_min_powerstate(pci_chipset_tag_t, pcitag_t); void acpi_pci_set_powerstate(pci_chipset_tag_t, pcitag_t, int, int); int acpi_pci_notify(struct aml_node *, int, void *); @@ -669,7 +669,7 @@ acpi_getpci(struct aml_node *node, void return (1); } -void +struct aml_node * acpi_pci_match(struct device *dev, struct pci_attach_args *pa) { struct acpi_pci *pdev; @@ -695,7 +695,11 @@ acpi_pci_match(struct device *dev, struc acpi_pci_set_powerstate(pa->pa_pc, pa->pa_tag, state, 0); aml_register_notify(pdev->node, NULL, acpi_pci_notify, pdev, 0); + + return pdev->node; } + + return NULL; } pcireg_t Index: sys/dev/acpi/dwiic.c =================================================================== RCS file: sys/dev/acpi/dwiic.c diff -N sys/dev/acpi/dwiic.c --- sys/dev/acpi/dwiic.c 17 Aug 2017 20:41:16 -0000 1.24 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,1095 +0,0 @@ -/* $OpenBSD: dwiic.c,v 1.24 2017/08/17 20:41:16 kettenis Exp $ */ -/* - * Synopsys DesignWare I2C controller - * - * Copyright (c) 2015, 2016 joshua stein <j...@openbsd.org> - * - * Permission to use, copy, modify, and/or 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. - */ - -#include <sys/param.h> -#include <sys/systm.h> -#include <sys/kernel.h> -#include <sys/kthread.h> - -#include <dev/acpi/acpireg.h> -#include <dev/acpi/acpivar.h> -#include <dev/acpi/acpidev.h> -#include <dev/acpi/amltypes.h> -#include <dev/acpi/dsdt.h> - -#include <dev/i2c/i2cvar.h> - -/* #define DWIIC_DEBUG */ - -#ifdef DWIIC_DEBUG -#define DPRINTF(x) printf x -#else -#define DPRINTF(x) -#endif - -/* register offsets */ -#define DW_IC_CON 0x0 -#define DW_IC_TAR 0x4 -#define DW_IC_DATA_CMD 0x10 -#define DW_IC_SS_SCL_HCNT 0x14 -#define DW_IC_SS_SCL_LCNT 0x18 -#define DW_IC_FS_SCL_HCNT 0x1c -#define DW_IC_FS_SCL_LCNT 0x20 -#define DW_IC_INTR_STAT 0x2c -#define DW_IC_INTR_MASK 0x30 -#define DW_IC_RAW_INTR_STAT 0x34 -#define DW_IC_RX_TL 0x38 -#define DW_IC_TX_TL 0x3c -#define DW_IC_CLR_INTR 0x40 -#define DW_IC_CLR_RX_UNDER 0x44 -#define DW_IC_CLR_RX_OVER 0x48 -#define DW_IC_CLR_TX_OVER 0x4c -#define DW_IC_CLR_RD_REQ 0x50 -#define DW_IC_CLR_TX_ABRT 0x54 -#define DW_IC_CLR_RX_DONE 0x58 -#define DW_IC_CLR_ACTIVITY 0x5c -#define DW_IC_CLR_STOP_DET 0x60 -#define DW_IC_CLR_START_DET 0x64 -#define DW_IC_CLR_GEN_CALL 0x68 -#define DW_IC_ENABLE 0x6c -#define DW_IC_STATUS 0x70 -#define DW_IC_TXFLR 0x74 -#define DW_IC_RXFLR 0x78 -#define DW_IC_SDA_HOLD 0x7c -#define DW_IC_TX_ABRT_SOURCE 0x80 -#define DW_IC_ENABLE_STATUS 0x9c -#define DW_IC_COMP_PARAM_1 0xf4 -#define DW_IC_COMP_VERSION 0xf8 -#define DW_IC_SDA_HOLD_MIN_VERS 0x3131312A -#define DW_IC_COMP_TYPE 0xfc -#define DW_IC_COMP_TYPE_VALUE 0x44570140 - -#define DW_IC_CON_MASTER 0x1 -#define DW_IC_CON_SPEED_STD 0x2 -#define DW_IC_CON_SPEED_FAST 0x4 -#define DW_IC_CON_10BITADDR_MASTER 0x10 -#define DW_IC_CON_RESTART_EN 0x20 -#define DW_IC_CON_SLAVE_DISABLE 0x40 - -#define DW_IC_DATA_CMD_READ 0x100 -#define DW_IC_DATA_CMD_STOP 0x200 -#define DW_IC_DATA_CMD_RESTART 0x400 - -#define DW_IC_INTR_RX_UNDER 0x001 -#define DW_IC_INTR_RX_OVER 0x002 -#define DW_IC_INTR_RX_FULL 0x004 -#define DW_IC_INTR_TX_OVER 0x008 -#define DW_IC_INTR_TX_EMPTY 0x010 -#define DW_IC_INTR_RD_REQ 0x020 -#define DW_IC_INTR_TX_ABRT 0x040 -#define DW_IC_INTR_RX_DONE 0x080 -#define DW_IC_INTR_ACTIVITY 0x100 -#define DW_IC_INTR_STOP_DET 0x200 -#define DW_IC_INTR_START_DET 0x400 -#define DW_IC_INTR_GEN_CALL 0x800 - -#define DW_IC_STATUS_ACTIVITY 0x1 - -/* hardware abort codes from the DW_IC_TX_ABRT_SOURCE register */ -#define ABRT_7B_ADDR_NOACK 0 -#define ABRT_10ADDR1_NOACK 1 -#define ABRT_10ADDR2_NOACK 2 -#define ABRT_TXDATA_NOACK 3 -#define ABRT_GCALL_NOACK 4 -#define ABRT_GCALL_READ 5 -#define ABRT_SBYTE_ACKDET 7 -#define ABRT_SBYTE_NORSTRT 9 -#define ABRT_10B_RD_NORSTRT 10 -#define ABRT_MASTER_DIS 11 -#define ARB_LOST 12 - -struct dwiic_crs { - int irq_int; - uint8_t irq_flags; - uint32_t addr_min; - uint32_t addr_bas; - uint32_t addr_len; - uint16_t i2c_addr; - struct aml_node *devnode; - struct aml_node *gpio_int_node; - uint16_t gpio_int_pin; - uint16_t gpio_int_flags; -}; - -struct dwiic_softc { - struct device sc_dev; - - bus_space_tag_t sc_iot; - bus_space_handle_t sc_ioh; - - struct acpi_softc *sc_acpi; - struct aml_node *sc_devnode; - char sc_hid[16]; - void *sc_ih; - - struct i2cbus_attach_args sc_iba; - struct device *sc_iic; - - int sc_poll; - int sc_busy; - int sc_readwait; - int sc_writewait; - - uint32_t master_cfg; - uint16_t ss_hcnt, ss_lcnt, fs_hcnt, fs_lcnt; - uint32_t sda_hold_time; - int tx_fifo_depth; - int rx_fifo_depth; - - struct i2c_controller sc_i2c_tag; - struct rwlock sc_i2c_lock; - struct { - i2c_op_t op; - void *buf; - size_t len; - int flags; - volatile int error; - } sc_i2c_xfer; -}; - -int dwiic_match(struct device *, void *, void *); -void dwiic_attach(struct device *, struct device *, void *); -int dwiic_detach(struct device *, int); -int dwiic_activate(struct device *, int); - -int dwiic_init(struct dwiic_softc *); -void dwiic_enable(struct dwiic_softc *, int); -int dwiic_intr(void *); - -void * dwiic_i2c_intr_establish(void *, void *, int, - int (*)(void *), void *, const char *); -const char * dwiic_i2c_intr_string(void *, void *); - -int dwiic_acpi_parse_crs(int, union acpi_resource *, void *); -int dwiic_acpi_found_hid(struct aml_node *, void *); -int dwiic_acpi_found_ihidev(struct dwiic_softc *, - struct aml_node *, char *, struct dwiic_crs); -int dwiic_acpi_found_iatp(struct dwiic_softc *, struct aml_node *, - char *, struct dwiic_crs); -void dwiic_acpi_get_params(struct dwiic_softc *, char *, uint16_t *, - uint16_t *, uint32_t *); -void dwiic_acpi_power(struct dwiic_softc *, int); -void dwiic_bus_scan(struct device *, struct i2cbus_attach_args *, - void *); - -int dwiic_i2c_acquire_bus(void *, int); -void dwiic_i2c_release_bus(void *, int); -uint32_t dwiic_read(struct dwiic_softc *, int); -void dwiic_write(struct dwiic_softc *, int, uint32_t); -int dwiic_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, - size_t, void *, size_t, int); -void dwiic_xfer_msg(struct dwiic_softc *); - -struct cfattach dwiic_ca = { - sizeof(struct dwiic_softc), - dwiic_match, - dwiic_attach, - NULL, - dwiic_activate -}; - -struct cfdriver dwiic_cd = { - NULL, "dwiic", DV_DULL -}; - -const char *dwiic_hids[] = { - "INT33C2", - "INT33C3", - "INT3432", - "INT3433", - "80860F41", - "808622C1", - NULL -}; - -const char *ihidev_hids[] = { - "PNP0C50", - "ACPI0C50", - NULL -}; - -const char *iatp_hids[] = { - "ATML0000", - "ATML0001", - NULL -}; - -int -dwiic_match(struct device *parent, void *match, void *aux) -{ - struct acpi_attach_args *aaa = aux; - struct cfdata *cf = match; - - return acpi_matchhids(aaa, dwiic_hids, cf->cf_driver->cd_name); -} - -void -dwiic_attach(struct device *parent, struct device *self, void *aux) -{ - struct dwiic_softc *sc = (struct dwiic_softc *)self; - struct acpi_attach_args *aa = aux; - struct aml_value res; - struct dwiic_crs crs; - - sc->sc_acpi = (struct acpi_softc *)parent; - sc->sc_devnode = aa->aaa_node; - memcpy(&sc->sc_hid, aa->aaa_dev, sizeof(sc->sc_hid)); - - printf(": %s", sc->sc_devnode->name); - - if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_CRS", 0, NULL, &res)) { - printf(", no _CRS method\n"); - return; - } - if (res.type != AML_OBJTYPE_BUFFER || res.length < 5) { - printf(", invalid _CRS object (type %d len %d)\n", - res.type, res.length); - aml_freevalue(&res); - return; - } - memset(&crs, 0, sizeof(crs)); - crs.devnode = sc->sc_devnode; - aml_parse_resource(&res, dwiic_acpi_parse_crs, &crs); - aml_freevalue(&res); - - if (crs.addr_bas == 0) { - printf(", can't find address\n"); - return; - } - - printf(" addr 0x%x/0x%x", crs.addr_bas, crs.addr_len); - - sc->sc_iot = aa->aaa_memt; - if (bus_space_map(sc->sc_iot, crs.addr_bas, crs.addr_len, 0, - &sc->sc_ioh)) { - printf(", failed mapping at 0x%x\n", crs.addr_bas); - return; - } - - /* power up the controller */ - dwiic_acpi_power(sc, 1); - - /* fetch timing parameters */ - sc->ss_hcnt = dwiic_read(sc, DW_IC_SS_SCL_HCNT); - sc->ss_lcnt = dwiic_read(sc, DW_IC_SS_SCL_LCNT); - sc->fs_hcnt = dwiic_read(sc, DW_IC_FS_SCL_HCNT); - sc->fs_lcnt = dwiic_read(sc, DW_IC_FS_SCL_LCNT); - sc->sda_hold_time = dwiic_read(sc, DW_IC_SDA_HOLD); - dwiic_acpi_get_params(sc, "SSCN", &sc->ss_hcnt, &sc->ss_lcnt, NULL); - dwiic_acpi_get_params(sc, "FMCN", &sc->fs_hcnt, &sc->fs_lcnt, - &sc->sda_hold_time); - - if (dwiic_init(sc)) { - printf(", failed initializing\n"); - bus_space_unmap(sc->sc_iot, sc->sc_ioh, crs.addr_len); - return; - } - - /* leave the controller disabled */ - dwiic_write(sc, DW_IC_INTR_MASK, 0); - dwiic_enable(sc, 0); - dwiic_read(sc, DW_IC_CLR_INTR); - - /* try to register interrupt with apic, but not fatal without it */ - if (crs.irq_int > 0) { - printf(" irq %d", crs.irq_int); - - sc->sc_ih = acpi_intr_establish(crs.irq_int, crs.irq_flags, - IPL_BIO, dwiic_intr, sc, sc->sc_dev.dv_xname); - if (sc->sc_ih == NULL) - printf(", can't establish interrupt"); - } - - printf("\n"); - - rw_init(&sc->sc_i2c_lock, "iiclk"); - - /* setup and attach iic bus */ - sc->sc_i2c_tag.ic_cookie = sc; - sc->sc_i2c_tag.ic_acquire_bus = dwiic_i2c_acquire_bus; - sc->sc_i2c_tag.ic_release_bus = dwiic_i2c_release_bus; - sc->sc_i2c_tag.ic_exec = dwiic_i2c_exec; - sc->sc_i2c_tag.ic_intr_establish = dwiic_i2c_intr_establish; - sc->sc_i2c_tag.ic_intr_string = dwiic_i2c_intr_string; - - bzero(&sc->sc_iba, sizeof(sc->sc_iba)); - sc->sc_iba.iba_name = "iic"; - sc->sc_iba.iba_tag = &sc->sc_i2c_tag; - sc->sc_iba.iba_bus_scan = dwiic_bus_scan; - sc->sc_iba.iba_bus_scan_arg = sc; - - config_found((struct device *)sc, &sc->sc_iba, iicbus_print); - - return; -} - -int -dwiic_detach(struct device *self, int flags) -{ - struct dwiic_softc *sc = (struct dwiic_softc *)self; - - if (sc->sc_ih != NULL) { - intr_disestablish(sc->sc_ih); - sc->sc_ih = NULL; - } - - return 0; -} - -int -dwiic_activate(struct device *self, int act) -{ - struct dwiic_softc *sc = (struct dwiic_softc *)self; - - switch (act) { - case DVACT_SUSPEND: - /* disable controller */ - dwiic_enable(sc, 0); - - /* disable interrupts */ - dwiic_write(sc, DW_IC_INTR_MASK, 0); - dwiic_read(sc, DW_IC_CLR_INTR); - - /* power down the controller */ - dwiic_acpi_power(sc, 0); - - break; - case DVACT_WAKEUP: - /* power up the controller */ - dwiic_acpi_power(sc, 1); - - dwiic_init(sc); - - break; - } - - config_activate_children(self, act); - - return 0; -} - -int -dwiic_acpi_parse_crs(int crsidx, union acpi_resource *crs, void *arg) -{ - struct dwiic_crs *sc_crs = arg; - struct aml_node *node; - uint16_t pin; - - switch (AML_CRSTYPE(crs)) { - case SR_IRQ: - sc_crs->irq_int = ffs(letoh16(crs->sr_irq.irq_mask)) - 1; - sc_crs->irq_flags = crs->sr_irq.irq_flags; - break; - - case LR_EXTIRQ: - sc_crs->irq_int = letoh32(crs->lr_extirq.irq[0]); - sc_crs->irq_flags = crs->lr_extirq.flags; - break; - - case LR_GPIO: - node = aml_searchname(sc_crs->devnode, - (char *)&crs->pad[crs->lr_gpio.res_off]); - pin = *(uint16_t *)&crs->pad[crs->lr_gpio.pin_off]; - if (crs->lr_gpio.type == LR_GPIO_INT) { - sc_crs->gpio_int_node = node; - sc_crs->gpio_int_pin = pin; - sc_crs->gpio_int_flags = crs->lr_gpio.tflags; - } - break; - - case LR_MEM32: - sc_crs->addr_min = letoh32(crs->lr_m32._min); - sc_crs->addr_len = letoh32(crs->lr_m32._len); - break; - - case LR_MEM32FIXED: - sc_crs->addr_bas = letoh32(crs->lr_m32fixed._bas); - sc_crs->addr_len = letoh32(crs->lr_m32fixed._len); - break; - - case LR_SERBUS: - if (crs->lr_serbus.type == LR_SERBUS_I2C) - sc_crs->i2c_addr = letoh16(crs->lr_i2cbus._adr); - break; - - default: - DPRINTF(("%s: unknown resource type %d\n", __func__, - AML_CRSTYPE(crs))); - } - - return 0; -} - -void -dwiic_acpi_get_params(struct dwiic_softc *sc, char *method, uint16_t *hcnt, - uint16_t *lcnt, uint32_t *sda_hold_time) -{ - struct aml_value res; - - if (!aml_searchname(sc->sc_devnode, method)) - return; - - if (aml_evalname(sc->sc_acpi, sc->sc_devnode, method, 0, NULL, &res)) { - printf(": eval of %s at %s failed", method, - aml_nodename(sc->sc_devnode)); - return; - } - - if (res.type != AML_OBJTYPE_PACKAGE) { - printf(": %s is not a package (%d)", method, res.type); - aml_freevalue(&res); - return; - } - - if (res.length <= 2) { - printf(": %s returned package of len %d", method, res.length); - aml_freevalue(&res); - return; - } - - *hcnt = aml_val2int(res.v_package[0]); - *lcnt = aml_val2int(res.v_package[1]); - if (sda_hold_time) - *sda_hold_time = aml_val2int(res.v_package[2]); - aml_freevalue(&res); -} - -void -dwiic_bus_scan(struct device *iic, struct i2cbus_attach_args *iba, void *aux) -{ - struct dwiic_softc *sc = (struct dwiic_softc *)aux; - - sc->sc_iic = iic; - aml_find_node(sc->sc_devnode, "_HID", dwiic_acpi_found_hid, sc); -} - -int -dwiic_i2c_print(void *aux, const char *pnp) -{ - struct i2c_attach_args *ia = aux; - - if (pnp != NULL) - printf("%s at %s", ia->ia_name, pnp); - - printf(" addr 0x%x", ia->ia_addr); - - return UNCONF; -} - -void * -dwiic_i2c_intr_establish(void *cookie, void *ih, int level, - int (*func)(void *), void *arg, const char *name) -{ - struct dwiic_crs *crs = ih; - - if (crs->gpio_int_node && crs->gpio_int_node->gpio) { - struct acpi_gpio *gpio = crs->gpio_int_node->gpio; - gpio->intr_establish(gpio->cookie, crs->gpio_int_pin, - crs->gpio_int_flags, func, arg); - return ih; - } - - return acpi_intr_establish(crs->irq_int, crs->irq_flags, - level, func, arg, name); -} - -const char * -dwiic_i2c_intr_string(void *cookie, void *ih) -{ - struct dwiic_crs *crs = ih; - static char irqstr[64]; - - if (crs->gpio_int_node && crs->gpio_int_node->gpio) - snprintf(irqstr, sizeof(irqstr), "gpio %d", crs->gpio_int_pin); - else - snprintf(irqstr, sizeof(irqstr), "irq %d", crs->irq_int); - - return irqstr; -} - -int -dwiic_matchhids(const char *hid, const char *hids[]) -{ - int i; - - for (i = 0; hids[i]; i++) - if (!strcmp(hid, hids[i])) - return (1); - - return (0); -} - -int -dwiic_acpi_found_hid(struct aml_node *node, void *arg) -{ - struct dwiic_softc *sc = (struct dwiic_softc *)arg; - struct dwiic_crs crs; - struct aml_value res; - int64_t sta; - char cdev[16], dev[16]; - - if (acpi_parsehid(node, arg, cdev, dev, 16) != 0) - return 0; - - if (aml_evalinteger(acpi_softc, node->parent, "_STA", 0, NULL, &sta)) - sta = STA_PRESENT | STA_ENABLED | STA_DEV_OK | 0x1000; - - if ((sta & STA_PRESENT) == 0) - return 0; - - DPRINTF(("%s: found HID %s at %s\n", sc->sc_dev.dv_xname, dev, - aml_nodename(node))); - - if (aml_evalname(acpi_softc, node->parent, "_CRS", 0, NULL, &res)) { - printf("%s: no _CRS method at %s\n", sc->sc_dev.dv_xname, - aml_nodename(node->parent)); - return (0); - } - if (res.type != AML_OBJTYPE_BUFFER || res.length < 5) { - printf("%s: invalid _CRS object (type %d len %d)\n", - sc->sc_dev.dv_xname, res.type, res.length); - aml_freevalue(&res); - return (0); - } - memset(&crs, 0, sizeof(crs)); - crs.devnode = sc->sc_devnode; - aml_parse_resource(&res, dwiic_acpi_parse_crs, &crs); - aml_freevalue(&res); - - acpi_attach_deps(acpi_softc, node->parent); - - if (dwiic_matchhids(cdev, ihidev_hids)) - return dwiic_acpi_found_ihidev(sc, node, dev, crs); - else if (dwiic_matchhids(dev, iatp_hids)) - return dwiic_acpi_found_iatp(sc, node, dev, crs); - - return 0; -} - -int -dwiic_acpi_found_ihidev(struct dwiic_softc *sc, struct aml_node *node, - char *dev, struct dwiic_crs crs) -{ - struct i2c_attach_args ia; - struct aml_value cmd[4], res; - - /* 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, - }; - - if (!aml_searchname(node->parent, "_DSM")) { - printf("%s: couldn't find _DSM at %s\n", sc->sc_dev.dv_xname, - aml_nodename(node->parent)); - return 0; - } - - bzero(&cmd, sizeof(cmd)); - cmd[0].type = AML_OBJTYPE_BUFFER; - cmd[0].v_buffer = (uint8_t *)&i2c_hid_guid; - cmd[0].length = sizeof(i2c_hid_guid); - /* rev */ - cmd[1].type = AML_OBJTYPE_INTEGER; - cmd[1].v_integer = 1; - cmd[1].length = 1; - /* func */ - cmd[2].type = AML_OBJTYPE_INTEGER; - cmd[2].v_integer = 1; /* HID */ - cmd[2].length = 1; - /* not used */ - cmd[3].type = AML_OBJTYPE_PACKAGE; - cmd[3].length = 0; - - if (aml_evalname(acpi_softc, node->parent, "_DSM", 4, cmd, &res)) { - printf("%s: eval of _DSM at %s failed\n", - sc->sc_dev.dv_xname, aml_nodename(node->parent)); - return 0; - } - - if (res.type != AML_OBJTYPE_INTEGER) { - printf("%s: bad _DSM result at %s: %d\n", - sc->sc_dev.dv_xname, aml_nodename(node->parent), res.type); - aml_freevalue(&res); - return 0; - } - - memset(&ia, 0, sizeof(ia)); - ia.ia_tag = sc->sc_iba.iba_tag; - ia.ia_size = 1; - ia.ia_name = "ihidev"; - ia.ia_size = aml_val2int(&res); /* hid descriptor address */ - ia.ia_addr = crs.i2c_addr; - ia.ia_cookie = dev; - - aml_freevalue(&res); - - if (crs.irq_int <= 0 && crs.gpio_int_node == NULL) { - printf("%s: couldn't find irq for %s\n", sc->sc_dev.dv_xname, - aml_nodename(node->parent)); - return 0; - } - ia.ia_intr = &crs; - - if (config_found(sc->sc_iic, &ia, dwiic_i2c_print)) { - node->parent->attached = 1; - return 0; - } - - return 1; -} - -int -dwiic_acpi_found_iatp(struct dwiic_softc *sc, struct aml_node *node, char *dev, - struct dwiic_crs crs) -{ - struct i2c_attach_args ia; - struct aml_value res; - - if (aml_evalname(acpi_softc, node->parent, "GPIO", 0, NULL, &res)) - /* no gpio, assume this is the bootloader interface */ - return (0); - - memset(&ia, 0, sizeof(ia)); - ia.ia_tag = sc->sc_iba.iba_tag; - ia.ia_size = 1; - ia.ia_name = "iatp"; - ia.ia_addr = crs.i2c_addr; - ia.ia_cookie = dev; - - if (crs.irq_int <= 0 && crs.gpio_int_node == NULL) { - printf("%s: couldn't find irq for %s\n", sc->sc_dev.dv_xname, - aml_nodename(node->parent)); - return 0; - } - ia.ia_intr = &crs; - - if (config_found(sc->sc_iic, &ia, dwiic_i2c_print)) { - node->parent->attached = 1; - return 0; - } - - return 1; -} - -uint32_t -dwiic_read(struct dwiic_softc *sc, int offset) -{ - u_int32_t b = bus_space_read_4(sc->sc_iot, sc->sc_ioh, offset); - - DPRINTF(("%s: read at 0x%x = 0x%x\n", sc->sc_dev.dv_xname, offset, b)); - - return b; -} - -void -dwiic_write(struct dwiic_softc *sc, int offset, uint32_t val) -{ - bus_space_write_4(sc->sc_iot, sc->sc_ioh, offset, val); - - DPRINTF(("%s: write at 0x%x: 0x%x\n", sc->sc_dev.dv_xname, offset, - val)); -} - -int -dwiic_i2c_acquire_bus(void *cookie, int flags) -{ - struct dwiic_softc *sc = cookie; - - if (cold || sc->sc_poll || (flags & I2C_F_POLL)) - return (0); - - return rw_enter(&sc->sc_i2c_lock, RW_WRITE | RW_INTR); -} - -void -dwiic_i2c_release_bus(void *cookie, int flags) -{ - struct dwiic_softc *sc = cookie; - - if (cold || sc->sc_poll || (flags & I2C_F_POLL)) - return; - - rw_exit(&sc->sc_i2c_lock); -} - -int -dwiic_init(struct dwiic_softc *sc) -{ - uint32_t reg; - - /* make sure we're talking to a device we know */ - reg = dwiic_read(sc, DW_IC_COMP_TYPE); - if (reg != DW_IC_COMP_TYPE_VALUE) { - DPRINTF(("%s: invalid component type 0x%x\n", - sc->sc_dev.dv_xname, reg)); - return 1; - } - - /* disable the adapter */ - dwiic_enable(sc, 0); - - /* write standard-mode SCL timing parameters */ - dwiic_write(sc, DW_IC_SS_SCL_HCNT, sc->ss_hcnt); - dwiic_write(sc, DW_IC_SS_SCL_LCNT, sc->ss_lcnt); - - /* and fast-mode SCL timing parameters */ - dwiic_write(sc, DW_IC_FS_SCL_HCNT, sc->fs_hcnt); - dwiic_write(sc, DW_IC_FS_SCL_LCNT, sc->fs_lcnt); - - /* SDA hold time */ - reg = dwiic_read(sc, DW_IC_COMP_VERSION); - if (reg >= DW_IC_SDA_HOLD_MIN_VERS) - dwiic_write(sc, DW_IC_SDA_HOLD, sc->sda_hold_time); - - /* FIFO threshold levels */ - sc->tx_fifo_depth = 32; - sc->rx_fifo_depth = 32; - dwiic_write(sc, DW_IC_TX_TL, sc->tx_fifo_depth / 2); - dwiic_write(sc, DW_IC_RX_TL, 0); - - /* configure as i2c master with fast speed */ - sc->master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE | - DW_IC_CON_RESTART_EN | DW_IC_CON_SPEED_FAST; - dwiic_write(sc, DW_IC_CON, sc->master_cfg); - - return 0; -} - -void -dwiic_enable(struct dwiic_softc *sc, int enable) -{ - int retries; - - for (retries = 100; retries > 0; retries--) { - dwiic_write(sc, DW_IC_ENABLE, enable); - if ((dwiic_read(sc, DW_IC_ENABLE_STATUS) & 1) == enable) - return; - - DELAY(25); - } - - printf("%s: failed to %sable\n", sc->sc_dev.dv_xname, - (enable ? "en" : "dis")); -} - -void -dwiic_acpi_power(struct dwiic_softc *sc, int power) -{ - char ps[] = "_PS0"; - - if (!power) - ps[3] = '3'; - - if (aml_searchname(sc->sc_devnode, ps)) { - if (aml_evalname(sc->sc_acpi, sc->sc_devnode, ps, 0, NULL, - NULL)) { - printf("%s: failed powering %s with %s\n", - sc->sc_dev.dv_xname, power ? "on" : "off", - ps); - return; - } - - DELAY(10000); /* 10 milliseconds */ - } else - DPRINTF(("%s: no %s method\n", sc->sc_dev.dv_xname, ps)); - - if (strcmp(sc->sc_hid, "INT3432") == 0 || - strcmp(sc->sc_hid, "INT3433") == 0) { - /* - * XXX: broadwell i2c devices may need this for initial power - * up and/or after s3 resume. - * - * linux does this write via LPSS -> clk_register_gate -> - * clk_gate_enable -> clk_gate_endisable -> clk_writel - */ - dwiic_write(sc, 0x800, 1); - } -} - -int -dwiic_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmdbuf, - size_t cmdlen, void *buf, size_t len, int flags) -{ - struct dwiic_softc *sc = cookie; - u_int32_t ic_con, st, cmd, resp; - int retries, tx_limit, rx_avail, x, readpos; - uint8_t *b; - - if (sc->sc_busy) - return 1; - - sc->sc_busy++; - - DPRINTF(("%s: %s: op %d, addr 0x%02x, cmdlen %zu, len %zu, " - "flags 0x%02x\n", sc->sc_dev.dv_xname, __func__, op, addr, cmdlen, - len, flags)); - - /* setup transfer */ - sc->sc_i2c_xfer.op = op; - sc->sc_i2c_xfer.buf = buf; - sc->sc_i2c_xfer.len = len; - sc->sc_i2c_xfer.flags = flags; - sc->sc_i2c_xfer.error = 0; - - /* wait for bus to be idle */ - for (retries = 100; retries > 0; retries--) { - st = dwiic_read(sc, DW_IC_STATUS); - if (!(st & DW_IC_STATUS_ACTIVITY)) - break; - DELAY(1000); - } - DPRINTF(("%s: %s: status 0x%x\n", sc->sc_dev.dv_xname, __func__, st)); - if (st & DW_IC_STATUS_ACTIVITY) { - sc->sc_busy = 0; - return (1); - } - - if (cold || sc->sc_poll) - flags |= I2C_F_POLL; - - /* disable controller */ - dwiic_enable(sc, 0); - - /* set slave address */ - ic_con = dwiic_read(sc, DW_IC_CON); - ic_con &= ~DW_IC_CON_10BITADDR_MASTER; - dwiic_write(sc, DW_IC_CON, ic_con); - dwiic_write(sc, DW_IC_TAR, addr); - - /* disable interrupts */ - dwiic_write(sc, DW_IC_INTR_MASK, 0); - dwiic_read(sc, DW_IC_CLR_INTR); - - /* enable controller */ - dwiic_enable(sc, 1); - - /* wait until the controller is ready for commands */ - if (flags & I2C_F_POLL) - DELAY(200); - else { - dwiic_read(sc, DW_IC_CLR_INTR); - dwiic_write(sc, DW_IC_INTR_MASK, DW_IC_INTR_TX_EMPTY); - - if (tsleep(&sc->sc_writewait, PRIBIO, "dwiic", hz / 2) != 0) - printf("%s: timed out waiting for tx_empty intr\n", - sc->sc_dev.dv_xname); - } - - /* send our command, one byte at a time */ - if (cmdlen > 0) { - b = (void *)cmdbuf; - - DPRINTF(("%s: %s: sending cmd (len %zu):", sc->sc_dev.dv_xname, - __func__, cmdlen)); - for (x = 0; x < cmdlen; x++) - DPRINTF((" %02x", b[x])); - DPRINTF(("\n")); - - tx_limit = sc->tx_fifo_depth - dwiic_read(sc, DW_IC_TXFLR); - if (cmdlen > tx_limit) { - /* TODO */ - printf("%s: can't write %zu (> %d)\n", - sc->sc_dev.dv_xname, cmdlen, tx_limit); - sc->sc_i2c_xfer.error = 1; - sc->sc_busy = 0; - return (1); - } - - for (x = 0; x < cmdlen; x++) { - cmd = b[x]; - /* - * Generate STOP condition if this is the last - * byte of the transfer. - */ - if (x == (cmdlen - 1) && len == 0 && I2C_OP_STOP_P(op)) - cmd |= DW_IC_DATA_CMD_STOP; - dwiic_write(sc, DW_IC_DATA_CMD, cmd); - } - } - - b = (void *)buf; - x = readpos = 0; - tx_limit = sc->tx_fifo_depth - dwiic_read(sc, DW_IC_TXFLR); - - DPRINTF(("%s: %s: need to read %zu bytes, can send %d read reqs\n", - sc->sc_dev.dv_xname, __func__, len, tx_limit)); - - while (x < len) { - if (I2C_OP_WRITE_P(op)) - cmd = b[x]; - else - cmd = DW_IC_DATA_CMD_READ; - - /* - * Generate RESTART condition if we're reversing - * direction. - */ - if (x == 0 && cmdlen > 0 && I2C_OP_READ_P(op)) - cmd |= DW_IC_DATA_CMD_RESTART; - /* - * Generate STOP conditon on the last byte of the - * transfer. - */ - if (x == (len - 1) && I2C_OP_STOP_P(op)) - cmd |= DW_IC_DATA_CMD_STOP; - - dwiic_write(sc, DW_IC_DATA_CMD, cmd); - - tx_limit--; - x++; - - /* - * As TXFLR fills up, we need to clear it out by reading all - * available data. - */ - while (tx_limit == 0 || x == len) { - DPRINTF(("%s: %s: tx_limit %d, sent %d read reqs\n", - sc->sc_dev.dv_xname, __func__, tx_limit, x)); - - if (flags & I2C_F_POLL) { - for (retries = 100; retries > 0; retries--) { - rx_avail = dwiic_read(sc, DW_IC_RXFLR); - if (rx_avail > 0) - break; - DELAY(50); - } - } else { - dwiic_read(sc, DW_IC_CLR_INTR); - dwiic_write(sc, DW_IC_INTR_MASK, - DW_IC_INTR_RX_FULL); - - if (tsleep(&sc->sc_readwait, PRIBIO, "dwiic", - hz / 2) != 0) - printf("%s: timed out waiting for " - "rx_full intr\n", - sc->sc_dev.dv_xname); - - rx_avail = dwiic_read(sc, DW_IC_RXFLR); - } - - if (rx_avail == 0) { - printf("%s: timed out reading remaining %d\n", - sc->sc_dev.dv_xname, - (int)(len - 1 - readpos)); - sc->sc_i2c_xfer.error = 1; - sc->sc_busy = 0; - - return (1); - } - - DPRINTF(("%s: %s: %d avail to read (%zu remaining)\n", - sc->sc_dev.dv_xname, __func__, rx_avail, - len - readpos)); - - while (rx_avail > 0) { - resp = dwiic_read(sc, DW_IC_DATA_CMD); - if (readpos < len) { - b[readpos] = resp; - readpos++; - } - rx_avail--; - } - - if (readpos >= len) - break; - - DPRINTF(("%s: still need to read %d bytes\n", - sc->sc_dev.dv_xname, (int)(len - readpos))); - tx_limit = sc->tx_fifo_depth - - dwiic_read(sc, DW_IC_TXFLR); - } - } - - sc->sc_busy = 0; - - return 0; -} - -uint32_t -dwiic_read_clear_intrbits(struct dwiic_softc *sc) -{ - uint32_t stat; - - stat = dwiic_read(sc, DW_IC_INTR_STAT); - - if (stat & DW_IC_INTR_RX_UNDER) - dwiic_read(sc, DW_IC_CLR_RX_UNDER); - if (stat & DW_IC_INTR_RX_OVER) - dwiic_read(sc, DW_IC_CLR_RX_OVER); - if (stat & DW_IC_INTR_TX_OVER) - dwiic_read(sc, DW_IC_CLR_TX_OVER); - if (stat & DW_IC_INTR_RD_REQ) - dwiic_read(sc, DW_IC_CLR_RD_REQ); - if (stat & DW_IC_INTR_TX_ABRT) - dwiic_read(sc, DW_IC_CLR_TX_ABRT); - if (stat & DW_IC_INTR_RX_DONE) - dwiic_read(sc, DW_IC_CLR_RX_DONE); - if (stat & DW_IC_INTR_ACTIVITY) - dwiic_read(sc, DW_IC_CLR_ACTIVITY); - if (stat & DW_IC_INTR_STOP_DET) - dwiic_read(sc, DW_IC_CLR_STOP_DET); - if (stat & DW_IC_INTR_START_DET) - dwiic_read(sc, DW_IC_CLR_START_DET); - if (stat & DW_IC_INTR_GEN_CALL) - dwiic_read(sc, DW_IC_CLR_GEN_CALL); - - return stat; -} - -int -dwiic_intr(void *arg) -{ - struct dwiic_softc *sc = arg; - uint32_t en, stat; - - en = dwiic_read(sc, DW_IC_ENABLE); - /* probably for the other controller */ - if (!en) - return 0; - - stat = dwiic_read_clear_intrbits(sc); - DPRINTF(("%s: %s: enabled=0x%x stat=0x%x\n", sc->sc_dev.dv_xname, - __func__, en, stat)); - if (!(stat & ~DW_IC_INTR_ACTIVITY)) - return 1; - - if (stat & DW_IC_INTR_TX_ABRT) - sc->sc_i2c_xfer.error = 1; - - if (sc->sc_i2c_xfer.flags & I2C_F_POLL) - DPRINTF(("%s: %s: intr in poll mode?\n", sc->sc_dev.dv_xname, - __func__)); - else { - if (stat & DW_IC_INTR_RX_FULL) { - dwiic_write(sc, DW_IC_INTR_MASK, 0); - DPRINTF(("%s: %s: waking up reader\n", - sc->sc_dev.dv_xname, __func__)); - wakeup(&sc->sc_readwait); - } - if (stat & DW_IC_INTR_TX_EMPTY) { - dwiic_write(sc, DW_IC_INTR_MASK, 0); - DPRINTF(("%s: %s: waking up writer\n", - sc->sc_dev.dv_xname, __func__)); - wakeup(&sc->sc_writewait); - } - } - - return 1; -} Index: sys/dev/acpi/dwiic_acpi.c =================================================================== RCS file: sys/dev/acpi/dwiic_acpi.c diff -N sys/dev/acpi/dwiic_acpi.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sys/dev/acpi/dwiic_acpi.c 3 Nov 2017 16:56:29 -0000 @@ -0,0 +1,534 @@ +/* $OpenBSD: dwiic.c,v 1.22 2016/10/25 06:48:58 pirofti Exp $ */ +/* + * Synopsys DesignWare I2C controller + * + * Copyright (c) 2015, 2016 joshua stein <j...@openbsd.org> + * + * Permission to use, copy, modify, and/or 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. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/kthread.h> + +#include <dev/acpi/acpireg.h> +#include <dev/acpi/acpivar.h> +#include <dev/acpi/acpidev.h> +#include <dev/acpi/amltypes.h> +#include <dev/acpi/dsdt.h> + +#include <dev/ic/dwiicvar.h> + +struct dwiic_crs { + int irq_int; + uint8_t irq_flags; + uint32_t addr_min; + uint32_t addr_bas; + uint32_t addr_len; + uint16_t i2c_addr; + struct aml_node *devnode; + struct aml_node *gpio_int_node; + uint16_t gpio_int_pin; + uint16_t gpio_int_flags; +}; + +int dwiic_acpi_match(struct device *, void *, void *); +void dwiic_acpi_attach(struct device *, struct device *, void *); + +int dwiic_init(struct dwiic_softc *); +void dwiic_enable(struct dwiic_softc *, int); +int dwiic_intr(void *); + +void * dwiic_i2c_intr_establish(void *, void *, int, + int (*)(void *), void *, const char *); +const char * dwiic_i2c_intr_string(void *, void *); + +int dwiic_acpi_parse_crs(int, union acpi_resource *, void *); +int dwiic_acpi_found_ihidev(struct dwiic_softc *, + struct aml_node *, char *, struct dwiic_crs); +int dwiic_acpi_found_iatp(struct dwiic_softc *, struct aml_node *, + char *, struct dwiic_crs); +void dwiic_acpi_get_params(struct dwiic_softc *, char *, uint16_t *, + uint16_t *, uint32_t *); +void dwiic_acpi_power(struct dwiic_softc *, int); +void dwiic_acpi_bus_scan(struct device *, + struct i2cbus_attach_args *, void *); + +int dwiic_i2c_acquire_bus(void *, int); +void dwiic_i2c_release_bus(void *, int); +uint32_t dwiic_read(struct dwiic_softc *, int); +void dwiic_write(struct dwiic_softc *, int, uint32_t); +int dwiic_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, + size_t, void *, size_t, int); +void dwiic_xfer_msg(struct dwiic_softc *); + +struct cfattach dwiic_acpi_ca = { + sizeof(struct dwiic_softc), + dwiic_acpi_match, + dwiic_acpi_attach, + NULL, + dwiic_activate +}; + +const char *dwiic_hids[] = { + "INT33C2", + "INT33C3", + "INT3432", + "INT3433", + "80860F41", + "808622C1", + NULL +}; + +const char *ihidev_hids[] = { + "PNP0C50", + "ACPI0C50", + NULL +}; + +const char *iatp_hids[] = { + "ATML0000", + "ATML0001", + NULL +}; + +int +dwiic_acpi_match(struct device *parent, void *match, void *aux) +{ + struct acpi_attach_args *aaa = aux; + struct cfdata *cf = match; + + return acpi_matchhids(aaa, dwiic_hids, cf->cf_driver->cd_name); +} + +void +dwiic_acpi_attach(struct device *parent, struct device *self, void *aux) +{ + struct dwiic_softc *sc = (struct dwiic_softc *)self; + struct acpi_attach_args *aa = aux; + struct aml_value res; + struct dwiic_crs crs; + + sc->sc_acpi = (struct acpi_softc *)parent; + sc->sc_devnode = aa->aaa_node; + memcpy(&sc->sc_hid, aa->aaa_dev, sizeof(sc->sc_hid)); + + printf(": %s", sc->sc_devnode->name); + + if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_CRS", 0, NULL, &res)) { + printf(", no _CRS method\n"); + return; + } + if (res.type != AML_OBJTYPE_BUFFER || res.length < 5) { + printf(", invalid _CRS object (type %d len %d)\n", + res.type, res.length); + aml_freevalue(&res); + return; + } + memset(&crs, 0, sizeof(crs)); + crs.devnode = sc->sc_devnode; + aml_parse_resource(&res, dwiic_acpi_parse_crs, &crs); + aml_freevalue(&res); + + if (crs.addr_bas == 0) { + printf(", can't find address\n"); + return; + } + + printf(" addr 0x%x/0x%x", crs.addr_bas, crs.addr_len); + + sc->sc_iot = aa->aaa_memt; + if (bus_space_map(sc->sc_iot, crs.addr_bas, crs.addr_len, 0, + &sc->sc_ioh)) { + printf(", failed mapping at 0x%x\n", crs.addr_bas); + return; + } + + /* power up the controller */ + dwiic_acpi_power(sc, 1); + + /* fetch timing parameters */ + sc->ss_hcnt = dwiic_read(sc, DW_IC_SS_SCL_HCNT); + sc->ss_lcnt = dwiic_read(sc, DW_IC_SS_SCL_LCNT); + sc->fs_hcnt = dwiic_read(sc, DW_IC_FS_SCL_HCNT); + sc->fs_lcnt = dwiic_read(sc, DW_IC_FS_SCL_LCNT); + sc->sda_hold_time = dwiic_read(sc, DW_IC_SDA_HOLD); + dwiic_acpi_get_params(sc, "SSCN", &sc->ss_hcnt, &sc->ss_lcnt, NULL); + dwiic_acpi_get_params(sc, "FMCN", &sc->fs_hcnt, &sc->fs_lcnt, + &sc->sda_hold_time); + + if (dwiic_init(sc)) { + printf(", failed initializing\n"); + bus_space_unmap(sc->sc_iot, sc->sc_ioh, crs.addr_len); + return; + } + + /* leave the controller disabled */ + dwiic_write(sc, DW_IC_INTR_MASK, 0); + dwiic_enable(sc, 0); + dwiic_read(sc, DW_IC_CLR_INTR); + + /* try to register interrupt with apic, but not fatal without it */ + if (crs.irq_int > 0) { + printf(" irq %d", crs.irq_int); + + sc->sc_ih = acpi_intr_establish(crs.irq_int, crs.irq_flags, + IPL_BIO, dwiic_intr, sc, sc->sc_dev.dv_xname); + if (sc->sc_ih == NULL) + printf(", can't establish interrupt"); + } + + printf("\n"); + + rw_init(&sc->sc_i2c_lock, "iiclk"); + + /* setup and attach iic bus */ + sc->sc_i2c_tag.ic_cookie = sc; + sc->sc_i2c_tag.ic_acquire_bus = dwiic_i2c_acquire_bus; + sc->sc_i2c_tag.ic_release_bus = dwiic_i2c_release_bus; + sc->sc_i2c_tag.ic_exec = dwiic_i2c_exec; + sc->sc_i2c_tag.ic_intr_establish = dwiic_i2c_intr_establish; + sc->sc_i2c_tag.ic_intr_string = dwiic_i2c_intr_string; + + bzero(&sc->sc_iba, sizeof(sc->sc_iba)); + sc->sc_iba.iba_name = "iic"; + sc->sc_iba.iba_tag = &sc->sc_i2c_tag; + sc->sc_iba.iba_bus_scan = dwiic_acpi_bus_scan; + sc->sc_iba.iba_bus_scan_arg = sc; + + config_found((struct device *)sc, &sc->sc_iba, iicbus_print); + + return; +} + +int +dwiic_acpi_parse_crs(int crsidx, union acpi_resource *crs, void *arg) +{ + struct dwiic_crs *sc_crs = arg; + struct aml_node *node; + uint16_t pin; + + switch (AML_CRSTYPE(crs)) { + case SR_IRQ: + sc_crs->irq_int = ffs(letoh16(crs->sr_irq.irq_mask)) - 1; + sc_crs->irq_flags = crs->sr_irq.irq_flags; + break; + + case LR_EXTIRQ: + sc_crs->irq_int = letoh32(crs->lr_extirq.irq[0]); + sc_crs->irq_flags = crs->lr_extirq.flags; + break; + + case LR_GPIO: + node = aml_searchname(sc_crs->devnode, + (char *)&crs->pad[crs->lr_gpio.res_off]); + pin = *(uint16_t *)&crs->pad[crs->lr_gpio.pin_off]; + if (crs->lr_gpio.type == LR_GPIO_INT) { + sc_crs->gpio_int_node = node; + sc_crs->gpio_int_pin = pin; + sc_crs->gpio_int_flags = crs->lr_gpio.tflags; + } + break; + + case LR_MEM32: + sc_crs->addr_min = letoh32(crs->lr_m32._min); + sc_crs->addr_len = letoh32(crs->lr_m32._len); + break; + + case LR_MEM32FIXED: + sc_crs->addr_bas = letoh32(crs->lr_m32fixed._bas); + sc_crs->addr_len = letoh32(crs->lr_m32fixed._len); + break; + + case LR_SERBUS: + if (crs->lr_serbus.type == LR_SERBUS_I2C) + sc_crs->i2c_addr = letoh16(crs->lr_i2cbus._adr); + break; + + default: + DPRINTF(("%s: unknown resource type %d\n", __func__, + AML_CRSTYPE(crs))); + } + + return 0; +} + +void +dwiic_acpi_get_params(struct dwiic_softc *sc, char *method, uint16_t *hcnt, + uint16_t *lcnt, uint32_t *sda_hold_time) +{ + struct aml_value res; + + if (!aml_searchname(sc->sc_devnode, method)) + return; + + if (aml_evalname(sc->sc_acpi, sc->sc_devnode, method, 0, NULL, &res)) { + printf(": eval of %s at %s failed", method, + aml_nodename(sc->sc_devnode)); + return; + } + + if (res.type != AML_OBJTYPE_PACKAGE) { + printf(": %s is not a package (%d)", method, res.type); + aml_freevalue(&res); + return; + } + + if (res.length <= 2) { + printf(": %s returned package of len %d", method, res.length); + aml_freevalue(&res); + return; + } + + *hcnt = aml_val2int(res.v_package[0]); + *lcnt = aml_val2int(res.v_package[1]); + if (sda_hold_time) + *sda_hold_time = aml_val2int(res.v_package[2]); + aml_freevalue(&res); +} + +void +dwiic_acpi_bus_scan(struct device *iic, struct i2cbus_attach_args *iba, + void *aux) +{ + struct dwiic_softc *sc = (struct dwiic_softc *)aux; + + sc->sc_iic = iic; + aml_find_node(sc->sc_devnode, "_HID", dwiic_acpi_found_hid, sc); +} + +void * +dwiic_i2c_intr_establish(void *cookie, void *ih, int level, + int (*func)(void *), void *arg, const char *name) +{ + struct dwiic_crs *crs = ih; + + if (crs->gpio_int_node && crs->gpio_int_node->gpio) { + struct acpi_gpio *gpio = crs->gpio_int_node->gpio; + gpio->intr_establish(gpio->cookie, crs->gpio_int_pin, + crs->gpio_int_flags, func, arg); + return ih; + } + + return acpi_intr_establish(crs->irq_int, crs->irq_flags, + level, func, arg, name); +} + +const char * +dwiic_i2c_intr_string(void *cookie, void *ih) +{ + struct dwiic_crs *crs = ih; + static char irqstr[64]; + + if (crs->gpio_int_node && crs->gpio_int_node->gpio) + snprintf(irqstr, sizeof(irqstr), "gpio %d", crs->gpio_int_pin); + else + snprintf(irqstr, sizeof(irqstr), "irq %d", crs->irq_int); + + return irqstr; +} + +int +dwiic_matchhids(const char *hid, const char *hids[]) +{ + int i; + + for (i = 0; hids[i]; i++) + if (!strcmp(hid, hids[i])) + return (1); + + return (0); +} + +int +dwiic_acpi_found_hid(struct aml_node *node, void *arg) +{ + struct dwiic_softc *sc = (struct dwiic_softc *)arg; + struct dwiic_crs crs; + struct aml_value res; + int64_t sta; + char cdev[16], dev[16]; + + if (acpi_parsehid(node, arg, cdev, dev, 16) != 0) + return 0; + + if (aml_evalinteger(acpi_softc, node->parent, "_STA", 0, NULL, &sta)) + sta = STA_PRESENT | STA_ENABLED | STA_DEV_OK | 0x1000; + + if ((sta & STA_PRESENT) == 0) + return 0; + + DPRINTF(("%s: found HID %s at %s\n", sc->sc_dev.dv_xname, dev, + aml_nodename(node))); + + if (aml_evalname(acpi_softc, node->parent, "_CRS", 0, NULL, &res)) { + printf("%s: no _CRS method at %s\n", sc->sc_dev.dv_xname, + aml_nodename(node->parent)); + return (0); + } + if (res.type != AML_OBJTYPE_BUFFER || res.length < 5) { + printf("%s: invalid _CRS object (type %d len %d)\n", + sc->sc_dev.dv_xname, res.type, res.length); + aml_freevalue(&res); + return (0); + } + memset(&crs, 0, sizeof(crs)); + crs.devnode = sc->sc_devnode; + aml_parse_resource(&res, dwiic_acpi_parse_crs, &crs); + aml_freevalue(&res); + + if (dwiic_matchhids(cdev, ihidev_hids)) + return dwiic_acpi_found_ihidev(sc, node, dev, crs); + else if (dwiic_matchhids(dev, iatp_hids)) + return dwiic_acpi_found_iatp(sc, node, dev, crs); + + return 0; +} + +int +dwiic_acpi_found_ihidev(struct dwiic_softc *sc, struct aml_node *node, + char *dev, struct dwiic_crs crs) +{ + struct i2c_attach_args ia; + struct aml_value cmd[4], res; + + /* 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, + }; + + if (!aml_searchname(node->parent, "_DSM")) { + printf("%s: couldn't find _DSM at %s\n", sc->sc_dev.dv_xname, + aml_nodename(node->parent)); + return 0; + } + + bzero(&cmd, sizeof(cmd)); + cmd[0].type = AML_OBJTYPE_BUFFER; + cmd[0].v_buffer = (uint8_t *)&i2c_hid_guid; + cmd[0].length = sizeof(i2c_hid_guid); + /* rev */ + cmd[1].type = AML_OBJTYPE_INTEGER; + cmd[1].v_integer = 1; + cmd[1].length = 1; + /* func */ + cmd[2].type = AML_OBJTYPE_INTEGER; + cmd[2].v_integer = 1; /* HID */ + cmd[2].length = 1; + /* not used */ + cmd[3].type = AML_OBJTYPE_PACKAGE; + cmd[3].length = 0; + + if (aml_evalname(acpi_softc, node->parent, "_DSM", 4, cmd, &res)) { + printf("%s: eval of _DSM at %s failed\n", + sc->sc_dev.dv_xname, aml_nodename(node->parent)); + return 0; + } + + if (res.type != AML_OBJTYPE_INTEGER) { + printf("%s: bad _DSM result at %s: %d\n", + sc->sc_dev.dv_xname, aml_nodename(node->parent), res.type); + aml_freevalue(&res); + return 0; + } + + memset(&ia, 0, sizeof(ia)); + ia.ia_tag = sc->sc_iba.iba_tag; + ia.ia_size = 1; + ia.ia_name = "ihidev"; + ia.ia_size = aml_val2int(&res); /* hid descriptor address */ + ia.ia_addr = crs.i2c_addr; + ia.ia_cookie = dev; + + aml_freevalue(&res); + + if (!sc->sc_poll_ihidev && + !(crs.irq_int == 0 && crs.gpio_int_node == NULL)) + ia.ia_intr = &crs; + + if (config_found(sc->sc_iic, &ia, dwiic_i2c_print)) { + node->parent->attached = 1; + return 0; + } + + return 1; +} + +int +dwiic_acpi_found_iatp(struct dwiic_softc *sc, struct aml_node *node, char *dev, + struct dwiic_crs crs) +{ + struct i2c_attach_args ia; + struct aml_value res; + + if (aml_evalname(acpi_softc, node->parent, "GPIO", 0, NULL, &res)) + /* no gpio, assume this is the bootloader interface */ + return (0); + + memset(&ia, 0, sizeof(ia)); + ia.ia_tag = sc->sc_iba.iba_tag; + ia.ia_size = 1; + ia.ia_name = "iatp"; + ia.ia_addr = crs.i2c_addr; + ia.ia_cookie = dev; + + if (crs.irq_int <= 0 && crs.gpio_int_node == NULL) { + printf("%s: couldn't find irq for %s\n", sc->sc_dev.dv_xname, + aml_nodename(node->parent)); + return 0; + } + ia.ia_intr = &crs; + + if (config_found(sc->sc_iic, &ia, dwiic_i2c_print)) { + node->parent->attached = 1; + return 0; + } + + return 1; +} + +void +dwiic_acpi_power(struct dwiic_softc *sc, int power) +{ + char ps[] = "_PS0"; + + if (!power) + ps[3] = '3'; + + if (aml_searchname(sc->sc_devnode, ps)) { + if (aml_evalname(sc->sc_acpi, sc->sc_devnode, ps, 0, NULL, + NULL)) { + printf("%s: failed powering %s with %s\n", + sc->sc_dev.dv_xname, power ? "on" : "off", + ps); + return; + } + + DELAY(10000); /* 10 milliseconds */ + } else + DPRINTF(("%s: no %s method\n", sc->sc_dev.dv_xname, ps)); + + if (strcmp(sc->sc_hid, "INT3432") == 0 || + strcmp(sc->sc_hid, "INT3433") == 0) { + /* + * XXX: broadwell i2c devices may need this for initial power + * up and/or after s3 resume. + * + * linux does this write via LPSS -> clk_register_gate -> + * clk_gate_enable -> clk_gate_endisable -> clk_writel + */ + dwiic_write(sc, 0x800, 1); + } +} Index: sys/dev/acpi/files.acpi =================================================================== RCS file: /cvs/src/sys/dev/acpi/files.acpi,v retrieving revision 1.37 diff -u -p -u -p -r1.37 files.acpi --- sys/dev/acpi/files.acpi 22 Feb 2017 16:39:56 -0000 1.37 +++ sys/dev/acpi/files.acpi 3 Nov 2017 16:56:29 -0000 @@ -127,9 +127,8 @@ attach sdhc at acpi with sdhc_acpi file dev/acpi/sdhc_acpi.c sdhc_acpi # Synopsys DesignWare I2C controller -device dwiic: i2cbus -attach dwiic at acpi -file dev/acpi/dwiic.c dwiic +attach dwiic at acpi with dwiic_acpi +file dev/acpi/dwiic_acpi.c dwiic_acpi # Chromebook keyboard backlight device acpicbkbd Index: sys/dev/ic/dwiic.c =================================================================== RCS file: sys/dev/ic/dwiic.c diff -N sys/dev/ic/dwiic.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sys/dev/ic/dwiic.c 3 Nov 2017 16:56:29 -0000 @@ -0,0 +1,490 @@ +/* $OpenBSD: dwiic.c,v 1.22 2016/10/25 06:48:58 pirofti Exp $ */ +/* + * Synopsys DesignWare I2C controller + * + * Copyright (c) 2015-2017 joshua stein <j...@openbsd.org> + * + * Permission to use, copy, modify, and/or 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. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/kthread.h> + +#include <dev/acpi/acpireg.h> +#include <dev/acpi/acpivar.h> +#include <dev/acpi/acpidev.h> +#include <dev/acpi/amltypes.h> +#include <dev/acpi/dsdt.h> + +#include <dev/i2c/i2cvar.h> + +#include <dev/ic/dwiicvar.h> + +int dwiic_match(struct device *, void *, void *); +void dwiic_attach(struct device *, struct device *, void *); +int dwiic_detach(struct device *, int); +int dwiic_activate(struct device *, int); + +int dwiic_init(struct dwiic_softc *); +void dwiic_enable(struct dwiic_softc *, int); +int dwiic_intr(void *); + +void * dwiic_i2c_intr_establish(void *, void *, int, + int (*)(void *), void *, const char *); +const char * dwiic_i2c_intr_string(void *, void *); + +int dwiic_i2c_acquire_bus(void *, int); +void dwiic_i2c_release_bus(void *, int); +uint32_t dwiic_read(struct dwiic_softc *, int); +void dwiic_write(struct dwiic_softc *, int, uint32_t); +int dwiic_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, + size_t, void *, size_t, int); +void dwiic_xfer_msg(struct dwiic_softc *); + +struct cfdriver dwiic_cd = { + NULL, "dwiic", DV_DULL +}; + +int +dwiic_detach(struct device *self, int flags) +{ + struct dwiic_softc *sc = (struct dwiic_softc *)self; + + if (sc->sc_ih != NULL) { + intr_disestablish(sc->sc_ih); + sc->sc_ih = NULL; + } + + return 0; +} + +int +dwiic_activate(struct device *self, int act) +{ + struct dwiic_softc *sc = (struct dwiic_softc *)self; + + switch (act) { + case DVACT_SUSPEND: + /* disable controller */ + dwiic_enable(sc, 0); + + /* disable interrupts */ + dwiic_write(sc, DW_IC_INTR_MASK, 0); + dwiic_read(sc, DW_IC_CLR_INTR); + +#if notyet + /* power down the controller */ + dwiic_acpi_power(sc, 0); +#endif + break; + case DVACT_WAKEUP: +#if notyet + /* power up the controller */ + dwiic_acpi_power(sc, 1); +#endif + dwiic_init(sc); + + break; + } + + config_activate_children(self, act); + + return 0; +} + +int +dwiic_i2c_print(void *aux, const char *pnp) +{ + struct i2c_attach_args *ia = aux; + + if (pnp != NULL) + printf("%s at %s", ia->ia_name, pnp); + + printf(" addr 0x%x", ia->ia_addr); + + return UNCONF; +} + +uint32_t +dwiic_read(struct dwiic_softc *sc, int offset) +{ + u_int32_t b = bus_space_read_4(sc->sc_iot, sc->sc_ioh, offset); + + DPRINTF(("%s: read at 0x%x = 0x%x\n", sc->sc_dev.dv_xname, offset, b)); + + return b; +} + +void +dwiic_write(struct dwiic_softc *sc, int offset, uint32_t val) +{ + DPRINTF(("%s: write at 0x%x: 0x%x\n", sc->sc_dev.dv_xname, offset, + val)); + + bus_space_write_4(sc->sc_iot, sc->sc_ioh, offset, val); +} + +int +dwiic_i2c_acquire_bus(void *cookie, int flags) +{ + struct dwiic_softc *sc = cookie; + + if (cold || sc->sc_poll || (flags & I2C_F_POLL)) + return (0); + + return rw_enter(&sc->sc_i2c_lock, RW_WRITE | RW_INTR); +} + +void +dwiic_i2c_release_bus(void *cookie, int flags) +{ + struct dwiic_softc *sc = cookie; + + if (cold || sc->sc_poll || (flags & I2C_F_POLL)) + return; + + rw_exit(&sc->sc_i2c_lock); +} + +int +dwiic_init(struct dwiic_softc *sc) +{ + uint32_t reg; + + /* make sure we're talking to a device we know */ + reg = dwiic_read(sc, DW_IC_COMP_TYPE); + if (reg != DW_IC_COMP_TYPE_VALUE) { + DPRINTF(("%s: invalid component type 0x%x\n", + sc->sc_dev.dv_xname, reg)); + return 1; + } + + /* disable the adapter */ + dwiic_enable(sc, 0); + + /* write standard-mode SCL timing parameters */ + dwiic_write(sc, DW_IC_SS_SCL_HCNT, sc->ss_hcnt); + dwiic_write(sc, DW_IC_SS_SCL_LCNT, sc->ss_lcnt); + + /* and fast-mode SCL timing parameters */ + dwiic_write(sc, DW_IC_FS_SCL_HCNT, sc->fs_hcnt); + dwiic_write(sc, DW_IC_FS_SCL_LCNT, sc->fs_lcnt); + + /* SDA hold time */ + reg = dwiic_read(sc, DW_IC_COMP_VERSION); + if (reg >= DW_IC_SDA_HOLD_MIN_VERS) + dwiic_write(sc, DW_IC_SDA_HOLD, sc->sda_hold_time); + + /* FIFO threshold levels */ + sc->tx_fifo_depth = 32; + sc->rx_fifo_depth = 32; + dwiic_write(sc, DW_IC_TX_TL, sc->tx_fifo_depth / 2); + dwiic_write(sc, DW_IC_RX_TL, 0); + + /* configure as i2c master with fast speed */ + sc->master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE | + DW_IC_CON_RESTART_EN | DW_IC_CON_SPEED_FAST; + dwiic_write(sc, DW_IC_CON, sc->master_cfg); + + return 0; +} + +void +dwiic_enable(struct dwiic_softc *sc, int enable) +{ + int retries; + + for (retries = 100; retries > 0; retries--) { + dwiic_write(sc, DW_IC_ENABLE, enable); + if ((dwiic_read(sc, DW_IC_ENABLE_STATUS) & 1) == enable) + return; + + DELAY(25); + } + + printf("%s: failed to %sable\n", sc->sc_dev.dv_xname, + (enable ? "en" : "dis")); +} + +int +dwiic_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmdbuf, + size_t cmdlen, void *buf, size_t len, int flags) +{ + struct dwiic_softc *sc = cookie; + u_int32_t ic_con, st, cmd, resp; + int retries, tx_limit, rx_avail, x, readpos; + uint8_t *b; + + if (sc->sc_busy) + return 1; + + sc->sc_busy++; + + DPRINTF(("%s: %s: op %d, addr 0x%02x, cmdlen %zu, len %zu, " + "flags 0x%02x\n", sc->sc_dev.dv_xname, __func__, op, addr, cmdlen, + len, flags)); + + /* setup transfer */ + sc->sc_i2c_xfer.op = op; + sc->sc_i2c_xfer.buf = buf; + sc->sc_i2c_xfer.len = len; + sc->sc_i2c_xfer.flags = flags; + sc->sc_i2c_xfer.error = 0; + + /* wait for bus to be idle */ + for (retries = 100; retries > 0; retries--) { + st = dwiic_read(sc, DW_IC_STATUS); + if (!(st & DW_IC_STATUS_ACTIVITY)) + break; + DELAY(1000); + } + DPRINTF(("%s: %s: status 0x%x\n", sc->sc_dev.dv_xname, __func__, st)); + if (st & DW_IC_STATUS_ACTIVITY) { + sc->sc_busy = 0; + return (1); + } + + if (cold || sc->sc_poll) + flags |= I2C_F_POLL; + + /* disable controller */ + dwiic_enable(sc, 0); + + /* set slave address */ + ic_con = dwiic_read(sc, DW_IC_CON); + ic_con &= ~DW_IC_CON_10BITADDR_MASTER; + dwiic_write(sc, DW_IC_CON, ic_con); + dwiic_write(sc, DW_IC_TAR, addr); + + /* disable interrupts */ + dwiic_write(sc, DW_IC_INTR_MASK, 0); + dwiic_read(sc, DW_IC_CLR_INTR); + + /* enable controller */ + dwiic_enable(sc, 1); + + /* wait until the controller is ready for commands */ + if (flags & I2C_F_POLL) + DELAY(200); + else { + dwiic_read(sc, DW_IC_CLR_INTR); + dwiic_write(sc, DW_IC_INTR_MASK, DW_IC_INTR_TX_EMPTY); + + if (tsleep(&sc->sc_writewait, PRIBIO, "dwiic", hz / 2) != 0) + printf("%s: timed out waiting for tx_empty intr\n", + sc->sc_dev.dv_xname); + } + + /* send our command, one byte at a time */ + if (cmdlen > 0) { + b = (void *)cmdbuf; + + DPRINTF(("%s: %s: sending cmd (len %zu):", sc->sc_dev.dv_xname, + __func__, cmdlen)); + for (x = 0; x < cmdlen; x++) + DPRINTF((" %02x", b[x])); + DPRINTF(("\n")); + + tx_limit = sc->tx_fifo_depth - dwiic_read(sc, DW_IC_TXFLR); + if (cmdlen > tx_limit) { + /* TODO */ + printf("%s: can't write %zu (> %d)\n", + sc->sc_dev.dv_xname, cmdlen, tx_limit); + sc->sc_i2c_xfer.error = 1; + sc->sc_busy = 0; + return (1); + } + + for (x = 0; x < cmdlen; x++) { + cmd = b[x]; + /* + * Generate STOP condition if this is the last + * byte of the transfer. + */ + if (x == (cmdlen - 1) && len == 0 && I2C_OP_STOP_P(op)) + cmd |= DW_IC_DATA_CMD_STOP; + dwiic_write(sc, DW_IC_DATA_CMD, cmd); + } + } + + b = (void *)buf; + x = readpos = 0; + tx_limit = sc->tx_fifo_depth - dwiic_read(sc, DW_IC_TXFLR); + + DPRINTF(("%s: %s: need to read %zu bytes, can send %d read reqs\n", + sc->sc_dev.dv_xname, __func__, len, tx_limit)); + + while (x < len) { + if (I2C_OP_WRITE_P(op)) + cmd = b[x]; + else + cmd = DW_IC_DATA_CMD_READ; + + /* + * Generate RESTART condition if we're reversing + * direction. + */ + if (x == 0 && cmdlen > 0 && I2C_OP_READ_P(op)) + cmd |= DW_IC_DATA_CMD_RESTART; + /* + * Generate STOP conditon on the last byte of the + * transfer. + */ + if (x == (len - 1) && I2C_OP_STOP_P(op)) + cmd |= DW_IC_DATA_CMD_STOP; + + dwiic_write(sc, DW_IC_DATA_CMD, cmd); + + tx_limit--; + x++; + + /* + * As TXFLR fills up, we need to clear it out by reading all + * available data. + */ + while (tx_limit == 0 || x == len) { + DPRINTF(("%s: %s: tx_limit %d, sent %d read reqs\n", + sc->sc_dev.dv_xname, __func__, tx_limit, x)); + + if (flags & I2C_F_POLL) { + for (retries = 100; retries > 0; retries--) { + rx_avail = dwiic_read(sc, DW_IC_RXFLR); + if (rx_avail > 0) + break; + DELAY(50); + } + } else { + dwiic_read(sc, DW_IC_CLR_INTR); + dwiic_write(sc, DW_IC_INTR_MASK, + DW_IC_INTR_RX_FULL); + + if (tsleep(&sc->sc_readwait, PRIBIO, "dwiic", + hz / 2) != 0) + printf("%s: timed out waiting for " + "rx_full intr\n", + sc->sc_dev.dv_xname); + + rx_avail = dwiic_read(sc, DW_IC_RXFLR); + } + + if (rx_avail == 0) { + printf("%s: timed out reading remaining %d\n", + sc->sc_dev.dv_xname, + (int)(len - 1 - readpos)); + sc->sc_i2c_xfer.error = 1; + sc->sc_busy = 0; + + return (1); + } + + DPRINTF(("%s: %s: %d avail to read (%zu remaining)\n", + sc->sc_dev.dv_xname, __func__, rx_avail, + len - readpos)); + + while (rx_avail > 0) { + resp = dwiic_read(sc, DW_IC_DATA_CMD); + if (readpos < len) { + b[readpos] = resp; + readpos++; + } + rx_avail--; + } + + if (readpos >= len) + break; + + DPRINTF(("%s: still need to read %d bytes\n", + sc->sc_dev.dv_xname, (int)(len - readpos))); + tx_limit = sc->tx_fifo_depth - + dwiic_read(sc, DW_IC_TXFLR); + } + } + + sc->sc_busy = 0; + + return 0; +} + +uint32_t +dwiic_read_clear_intrbits(struct dwiic_softc *sc) +{ + uint32_t stat; + + stat = dwiic_read(sc, DW_IC_INTR_STAT); + + if (stat & DW_IC_INTR_RX_UNDER) + dwiic_read(sc, DW_IC_CLR_RX_UNDER); + if (stat & DW_IC_INTR_RX_OVER) + dwiic_read(sc, DW_IC_CLR_RX_OVER); + if (stat & DW_IC_INTR_TX_OVER) + dwiic_read(sc, DW_IC_CLR_TX_OVER); + if (stat & DW_IC_INTR_RD_REQ) + dwiic_read(sc, DW_IC_CLR_RD_REQ); + if (stat & DW_IC_INTR_TX_ABRT) + dwiic_read(sc, DW_IC_CLR_TX_ABRT); + if (stat & DW_IC_INTR_RX_DONE) + dwiic_read(sc, DW_IC_CLR_RX_DONE); + if (stat & DW_IC_INTR_ACTIVITY) + dwiic_read(sc, DW_IC_CLR_ACTIVITY); + if (stat & DW_IC_INTR_STOP_DET) + dwiic_read(sc, DW_IC_CLR_STOP_DET); + if (stat & DW_IC_INTR_START_DET) + dwiic_read(sc, DW_IC_CLR_START_DET); + if (stat & DW_IC_INTR_GEN_CALL) + dwiic_read(sc, DW_IC_CLR_GEN_CALL); + + return stat; +} + +int +dwiic_intr(void *arg) +{ + struct dwiic_softc *sc = arg; + uint32_t en, stat; + + en = dwiic_read(sc, DW_IC_ENABLE); + /* probably for the other controller */ + if (!en) + return 0; + + stat = dwiic_read_clear_intrbits(sc); + DPRINTF(("%s: %s: enabled=0x%x stat=0x%x\n", sc->sc_dev.dv_xname, + __func__, en, stat)); + if (!(stat & ~DW_IC_INTR_ACTIVITY)) + return 1; + + if (stat & DW_IC_INTR_TX_ABRT) + sc->sc_i2c_xfer.error = 1; + + if (sc->sc_i2c_xfer.flags & I2C_F_POLL) + DPRINTF(("%s: %s: intr in poll mode?\n", sc->sc_dev.dv_xname, + __func__)); + else { + if (stat & DW_IC_INTR_RX_FULL) { + dwiic_write(sc, DW_IC_INTR_MASK, 0); + DPRINTF(("%s: %s: waking up reader\n", + sc->sc_dev.dv_xname, __func__)); + wakeup(&sc->sc_readwait); + } + if (stat & DW_IC_INTR_TX_EMPTY) { + dwiic_write(sc, DW_IC_INTR_MASK, 0); + DPRINTF(("%s: %s: waking up writer\n", + sc->sc_dev.dv_xname, __func__)); + wakeup(&sc->sc_writewait); + } + } + + return 1; +} Index: sys/dev/ic/dwiicreg.h =================================================================== RCS file: sys/dev/ic/dwiicreg.h diff -N sys/dev/ic/dwiicreg.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sys/dev/ic/dwiicreg.h 3 Nov 2017 16:56:29 -0000 @@ -0,0 +1,94 @@ +/* $OpenBSD$ */ +/* + * Synopsys DesignWare I2C controller + * + * Copyright (c) 2015, 2016 joshua stein <j...@openbsd.org> + * + * Permission to use, copy, modify, and/or 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. + */ + +/* register offsets */ +#define DW_IC_CON 0x0 +#define DW_IC_TAR 0x4 +#define DW_IC_DATA_CMD 0x10 +#define DW_IC_SS_SCL_HCNT 0x14 +#define DW_IC_SS_SCL_LCNT 0x18 +#define DW_IC_FS_SCL_HCNT 0x1c +#define DW_IC_FS_SCL_LCNT 0x20 +#define DW_IC_INTR_STAT 0x2c +#define DW_IC_INTR_MASK 0x30 +#define DW_IC_RAW_INTR_STAT 0x34 +#define DW_IC_RX_TL 0x38 +#define DW_IC_TX_TL 0x3c +#define DW_IC_CLR_INTR 0x40 +#define DW_IC_CLR_RX_UNDER 0x44 +#define DW_IC_CLR_RX_OVER 0x48 +#define DW_IC_CLR_TX_OVER 0x4c +#define DW_IC_CLR_RD_REQ 0x50 +#define DW_IC_CLR_TX_ABRT 0x54 +#define DW_IC_CLR_RX_DONE 0x58 +#define DW_IC_CLR_ACTIVITY 0x5c +#define DW_IC_CLR_STOP_DET 0x60 +#define DW_IC_CLR_START_DET 0x64 +#define DW_IC_CLR_GEN_CALL 0x68 +#define DW_IC_ENABLE 0x6c +#define DW_IC_STATUS 0x70 +#define DW_IC_TXFLR 0x74 +#define DW_IC_RXFLR 0x78 +#define DW_IC_SDA_HOLD 0x7c +#define DW_IC_TX_ABRT_SOURCE 0x80 +#define DW_IC_ENABLE_STATUS 0x9c +#define DW_IC_COMP_PARAM_1 0xf4 +#define DW_IC_COMP_VERSION 0xf8 +#define DW_IC_SDA_HOLD_MIN_VERS 0x3131312A +#define DW_IC_COMP_TYPE 0xfc +#define DW_IC_COMP_TYPE_VALUE 0x44570140 + +#define DW_IC_CON_MASTER 0x1 +#define DW_IC_CON_SPEED_STD 0x2 +#define DW_IC_CON_SPEED_FAST 0x4 +#define DW_IC_CON_10BITADDR_MASTER 0x10 +#define DW_IC_CON_RESTART_EN 0x20 +#define DW_IC_CON_SLAVE_DISABLE 0x40 + +#define DW_IC_DATA_CMD_READ 0x100 +#define DW_IC_DATA_CMD_STOP 0x200 +#define DW_IC_DATA_CMD_RESTART 0x400 + +#define DW_IC_INTR_RX_UNDER 0x001 +#define DW_IC_INTR_RX_OVER 0x002 +#define DW_IC_INTR_RX_FULL 0x004 +#define DW_IC_INTR_TX_OVER 0x008 +#define DW_IC_INTR_TX_EMPTY 0x010 +#define DW_IC_INTR_RD_REQ 0x020 +#define DW_IC_INTR_TX_ABRT 0x040 +#define DW_IC_INTR_RX_DONE 0x080 +#define DW_IC_INTR_ACTIVITY 0x100 +#define DW_IC_INTR_STOP_DET 0x200 +#define DW_IC_INTR_START_DET 0x400 +#define DW_IC_INTR_GEN_CALL 0x800 + +#define DW_IC_STATUS_ACTIVITY 0x1 + +/* hardware abort codes from the DW_IC_TX_ABRT_SOURCE register */ +#define ABRT_7B_ADDR_NOACK 0 +#define ABRT_10ADDR1_NOACK 1 +#define ABRT_10ADDR2_NOACK 2 +#define ABRT_TXDATA_NOACK 3 +#define ABRT_GCALL_NOACK 4 +#define ABRT_GCALL_READ 5 +#define ABRT_SBYTE_ACKDET 7 +#define ABRT_SBYTE_NORSTRT 9 +#define ABRT_10B_RD_NORSTRT 10 +#define ABRT_MASTER_DIS 11 +#define ARB_LOST 12 Index: sys/dev/ic/dwiicvar.h =================================================================== RCS file: sys/dev/ic/dwiicvar.h diff -N sys/dev/ic/dwiicvar.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sys/dev/ic/dwiicvar.h 3 Nov 2017 16:56:29 -0000 @@ -0,0 +1,103 @@ +/* $OpenBSD: dwiic.c,v 1.22 2016/10/25 06:48:58 pirofti Exp $ */ +/* + * Synopsys DesignWare I2C controller + * + * Copyright (c) 2015, 2016 joshua stein <j...@openbsd.org> + * + * Permission to use, copy, modify, and/or 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. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/kthread.h> + +#include <dev/acpi/acpireg.h> +#include <dev/acpi/acpivar.h> +#include <dev/acpi/acpidev.h> +#include <dev/acpi/amltypes.h> +#include <dev/acpi/dsdt.h> + +#include <dev/pci/pcivar.h> + +#include <dev/i2c/i2cvar.h> + +#include <dev/ic/dwiicreg.h> + +/* #define DWIIC_DEBUG */ + +#ifdef DWIIC_DEBUG +#define DPRINTF(x) printf x +#else +#define DPRINTF(x) +#endif + +struct dwiic_softc { + struct device sc_dev; + + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + + struct acpi_softc *sc_acpi; + struct aml_node *sc_devnode; + char sc_hid[16]; + void *sc_ih; + + struct pci_attach_args sc_paa; + + struct i2cbus_attach_args sc_iba; + struct device *sc_iic; + + u_int32_t sc_caps; + int sc_poll; + int sc_poll_ihidev; + int sc_busy; + int sc_readwait; + int sc_writewait; + + uint32_t master_cfg; + uint16_t ss_hcnt, ss_lcnt, fs_hcnt, fs_lcnt; + uint32_t sda_hold_time; + int tx_fifo_depth; + int rx_fifo_depth; + + struct i2c_controller sc_i2c_tag; + struct rwlock sc_i2c_lock; + struct { + i2c_op_t op; + void *buf; + size_t len; + int flags; + volatile int error; + } sc_i2c_xfer; +}; + +int dwiic_activate(struct device *, int); +int dwiic_init(struct dwiic_softc *); +void dwiic_enable(struct dwiic_softc *, int); +int dwiic_intr(void *); + +void * dwiic_i2c_intr_establish(void *, void *, int, + int (*)(void *), void *, const char *); +const char * dwiic_i2c_intr_string(void *, void *); +int dwiic_i2c_print(void *, const char *); + +int dwiic_i2c_acquire_bus(void *, int); +void dwiic_i2c_release_bus(void *, int); +uint32_t dwiic_read(struct dwiic_softc *, int); +void dwiic_write(struct dwiic_softc *, int, uint32_t); +int dwiic_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, + size_t, void *, size_t, int); +void dwiic_xfer_msg(struct dwiic_softc *); + +int dwiic_acpi_found_hid(struct aml_node *node, void *arg);