Module Name:    src
Committed By:   bouyer
Date:           Sun Dec 10 17:12:54 UTC 2017

Modified Files:
        src/sys/arch/x86/pci: files.pci
        src/sys/conf: files
Added Files:
        src/sys/arch/x86/pci: dwiic_pci.c lpssreg.h
        src/sys/dev/ic: dwiic.c dwiic_var.h

Log Message:
Add support for I2C designware controllers (as found in Intel PCH devices),
with a pci front-end.
The pci front-end is tied to ACPI and Intel-specific, so it's in arch/x86/pci
and not dev/pci.
Core driver from OpenBSD, PCI front-end by me.


To generate a diff of this commit:
cvs rdiff -u -r0 -r1.1 src/sys/arch/x86/pci/dwiic_pci.c \
    src/sys/arch/x86/pci/lpssreg.h
cvs rdiff -u -r1.20 -r1.21 src/sys/arch/x86/pci/files.pci
cvs rdiff -u -r1.1186 -r1.1187 src/sys/conf/files
cvs rdiff -u -r0 -r1.1 src/sys/dev/ic/dwiic.c src/sys/dev/ic/dwiic_var.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/arch/x86/pci/files.pci
diff -u src/sys/arch/x86/pci/files.pci:1.20 src/sys/arch/x86/pci/files.pci:1.21
--- src/sys/arch/x86/pci/files.pci:1.20	Sun May  3 02:50:59 2015
+++ src/sys/arch/x86/pci/files.pci	Sun Dec 10 17:12:54 2017
@@ -1,4 +1,4 @@
-#	$NetBSD: files.pci,v 1.20 2015/05/03 02:50:59 pgoyette Exp $
+#	$NetBSD: files.pci,v 1.21 2017/12/10 17:12:54 bouyer Exp $
 
 device 	aapic
 attach 	aapic at pci
@@ -39,6 +39,9 @@ device	amdtemp: sysmon_envsys
 attach	amdtemp at amdnb_miscbus
 file	arch/x86/pci/amdtemp.c		amdtemp
 
+attach  dwiic at pci with pcidwiic
+file    arch/x86/pci/dwiic_pci.c	pcidwiic
+
 # PCI-LPC bridges
 device rdcpcib: isabus, sysmon_wdog
 attach rdcpcib at pci

Index: src/sys/conf/files
diff -u src/sys/conf/files:1.1186 src/sys/conf/files:1.1187
--- src/sys/conf/files:1.1186	Sun Dec 10 17:03:07 2017
+++ src/sys/conf/files	Sun Dec 10 17:12:54 2017
@@ -1,4 +1,4 @@
-#	$NetBSD: files,v 1.1186 2017/12/10 17:03:07 bouyer Exp $
+#	$NetBSD: files,v 1.1187 2017/12/10 17:12:54 bouyer Exp $
 #	@(#)files.newconf	7.5 (Berkeley) 5/10/93
 
 version 	20171118
@@ -1128,6 +1128,11 @@ file	dev/ic/pcf8584.c		pcf8584
 define	pca9564
 file	dev/ic/pca9564.c		pca9564
 
+# Synopsys DesignWare I2C controller
+define	dwiic
+device dwiic: dwiic, i2cbus
+file	dev/ic/dwiic.c			dwiic
+
 # ACPI power management timer (hardware access, independent of ACPI)
 #
 define acpipmtimer

Added files:

Index: src/sys/arch/x86/pci/dwiic_pci.c
diff -u /dev/null src/sys/arch/x86/pci/dwiic_pci.c:1.1
--- /dev/null	Sun Dec 10 17:12:54 2017
+++ src/sys/arch/x86/pci/dwiic_pci.c	Sun Dec 10 17:12:54 2017
@@ -0,0 +1,213 @@
+/* $NetBSD: dwiic_pci.c,v 1.1 2017/12/10 17:12:54 bouyer 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: dwiic_pci.c,v 1.1 2017/12/10 17:12:54 bouyer 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 *, bool);
+
+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");
+	}
+
+	dwiic_attach(&sc->sc_dwiic);
+
+	pmf_device_register(self, dwiic_suspend, dwiic_resume);
+
+out:
+	return;
+}
+
+static bool
+dwiic_pci_power(struct dwiic_softc *dwsc, bool power)
+{
+	struct pci_dwiic_softc *sc = (void *)dwsc;
+	pcireg_t pmreg;
+
+	printf("status 0x%x\n", pci_conf_read(sc->sc_pc, sc->sc_ptag, PCI_COMMAND_STATUS_REG));
+	printf("reset 0x%x\n", lpss_read(sc, LPSS_RESET));
+	printf("rlo 0x%x\n", lpss_read(sc, LPSS_REMAP_LO));
+	printf("rho 0x%x\n", lpss_read(sc, LPSS_REMAP_HI));
+
+	if (!power)
+		lpss_write(sc, LPSS_CLKGATE, LPSS_CLKGATE_CTRL_OFF);
+	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)));
+	}
+	if (power) {
+		lpss_write(sc, LPSS_CLKGATE, LPSS_CLKGATE_CTRL_ON);
+	}
+	return true;
+}
Index: src/sys/arch/x86/pci/lpssreg.h
diff -u /dev/null src/sys/arch/x86/pci/lpssreg.h:1.1
--- /dev/null	Sun Dec 10 17:12:54 2017
+++ src/sys/arch/x86/pci/lpssreg.h	Sun Dec 10 17:12:54 2017
@@ -0,0 +1,92 @@
+/* $NetBSD: lpssreg.h,v 1.1 2017/12/10 17:12:54 bouyer 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.
+ */
+
+/* LPSS registers, as found in several functions of Ivy Lake bridge */
+#define LPSS_RESET	0x204
+#define LPSS_RESET_DMA		__BIT(2)
+#define LPSS_RESET_CTRL		__BITS(1,0)
+#define LPSS_RESET_CTRL_ASSERT		0
+#define LPSS_RESET_CTRL_REL		0x3
+
+#define LPSS_ACTIVELTR	0x210
+#define LPSS_ACTIVELTR_SSCALE	__BITS(12,10)
+#define LPSS_ACTIVELTR_SSCALE_1		(0x2 << 10)
+#define LPSS_ACTIVELTR_SSCALE_32	(0x3 << 10)
+#define LPSS_ACTIVELTR_SVALUE	__BITS(9,0)
+
+#define LPSS_IDLELTR	0x214
+#define LPSS_IDLELTR_SSCALE	__BITS(12,10)
+#define LPSS_IDLELTR_SSCALE_1		(0x2 << 10)
+#define LPSS_IDLELTR_SSCALE_32		(0x3 << 10)
+#define LPSS_IDLELTR_SVALUE	__BITS(9,0)
+
+#define LPSS_TXACK	0x218
+#define LPSS_TXACK_OVF		__BIT(31)
+#define LPSS_TXACK_CNT		__BITS(23,0)
+
+#define LPSS_RXACK	0x21C
+#define LPSS_RXACK_OVF		__BIT(31)
+#define LPSS_RXACK_CNT		__BITS(23,0)
+
+#define LPSS_TX_IRQ	0x220
+#define LPSS_TX_IRQ_MSK		__BIT(1)
+#define LPSS_TX_IRQ_I		__BIT(0)
+
+#define LPSS_TX_IRQ_CLR	0x224
+#define LPSS_TX_IRQ_CLRI	__BIT(0)
+
+#define LPSS_CLKGATE	0x238
+#define LPSS_CLKGATE_DMA	__BITS(3,2)
+#define LPSS_CLKGATE_DMA_AUTO		(0x0 << 2)
+#define LPSS_CLKGATE_DMA_OFF		(0x2 << 2)
+#define LPSS_CLKGATE_DMA_ON		(0x3 << 2)
+#define LPSS_CLKGATE_CTRL	__BITS(1,0)
+#define LPSS_CLKGATE_CTRL_AUTO		(0x0 << 0)
+#define LPSS_CLKGATE_CTRL_OFF		(0x2 << 0)
+#define LPSS_CLKGATE_CTRL_ON		(0x3 << 0)
+
+#define LPSS_REMAP_LO	0x240
+
+#define LPSS_REMAP_HI	0x244
+
+#define LPSS_DEVIDLE	0x24c
+#define LPSS_DEVIDLE_IRQ	__BIT(4)
+#define LPSS_DEVIDLE_RESTORE	__BIT(3)
+#define LPSS_DEVIDLE_IDLE	__BIT(2)
+#define LPSS_DEVIDLE_INPROG	__BIT(0)
+
+#define LPSS_CAP	0x2fc
+#define LPSS_CAP_DMA		__BIT(8)
+#define LPSS_CAP_TYPE		__BITS(7,4)
+#define LPSS_CAP_TYPE_I2C		(0x0 << 4)
+#define LPSS_CAP_TYPE_UART		(0x1 << 4)
+#define LPSS_CAP_TYPE_SPI		(0x2 << 4)
+#define LPSS_CAP_INSTANCE	__BITS(3,0)

Index: src/sys/dev/ic/dwiic.c
diff -u /dev/null src/sys/dev/ic/dwiic.c:1.1
--- /dev/null	Sun Dec 10 17:12:54 2017
+++ src/sys/dev/ic/dwiic.c	Sun Dec 10 17:12:54 2017
@@ -0,0 +1,623 @@
+/* $NetBSD: dwiic.c,v 1.1 2017/12/10 17:12:54 bouyer Exp $ */
+
+/* $OpenBSD 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
+ *
+ * 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/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: dwiic.c,v 1.1 2017/12/10 17:12:54 bouyer Exp $");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/device.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+
+#include <dev/ic/dwiic_var.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
+
+static int	dwiic_init(struct dwiic_softc *);
+static void	dwiic_enable(struct dwiic_softc *, bool);
+static int	dwiic_i2c_acquire_bus(void *, int);
+static void	dwiic_i2c_release_bus(void *, int);
+static uint32_t	dwiic_read(struct dwiic_softc *, int);
+static void	dwiic_write(struct dwiic_softc *, int, uint32_t);
+static int	dwiic_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *,
+		    size_t, void *, size_t, int);
+
+bool
+dwiic_attach(struct dwiic_softc *sc)
+{
+	if (sc->sc_power != NULL) {
+		if (!sc->sc_power(sc, 1)) {
+			aprint_error_dev(sc->sc_dev, "failed to power up\n");
+			return 0;
+		}
+	}
+
+	/* fetch timing parameters */
+	if (sc->ss_hcnt == 0) 
+		sc->ss_hcnt = dwiic_read(sc, DW_IC_SS_SCL_HCNT);
+	if (sc->ss_lcnt == 0)
+		sc->ss_lcnt = dwiic_read(sc, DW_IC_SS_SCL_LCNT);
+	if (sc->fs_hcnt == 0)
+		sc->fs_hcnt = dwiic_read(sc, DW_IC_FS_SCL_HCNT);
+	if (sc->fs_lcnt == 0)
+		sc->fs_lcnt = dwiic_read(sc, DW_IC_FS_SCL_LCNT);
+	if (sc->sda_hold_time == 0)
+		sc->sda_hold_time = dwiic_read(sc, DW_IC_SDA_HOLD);
+
+	if (dwiic_init(sc)) {
+		aprint_error_dev(sc->sc_dev, "failed initializing\n");
+		return 0;
+	}
+
+	/* leave the controller disabled */
+	dwiic_write(sc, DW_IC_INTR_MASK, 0);
+	dwiic_enable(sc, 0);
+	dwiic_read(sc, DW_IC_CLR_INTR);
+
+	mutex_init(&sc->sc_i2c_lock, MUTEX_DEFAULT, IPL_NONE);
+	mutex_init(&sc->sc_int_lock, MUTEX_DEFAULT, IPL_VM);
+	cv_init(&sc->sc_int_readwait, "dwiicr");
+	cv_init(&sc->sc_int_writewait, "dwiicw");
+
+	/* 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_iba.iba_tag = &sc->sc_i2c_tag;
+
+	config_found_ia(sc->sc_dev, "i2cbus", &sc->sc_iba, iicbus_print);
+	return 1;
+}
+
+int
+dwiic_detach(device_t self, int flags)
+{
+	struct dwiic_softc *sc = device_private(self);
+
+	dwiic_enable(sc, 0);
+	if (sc->sc_ih != NULL) {
+		intr_disestablish(sc->sc_ih);
+		sc->sc_ih = NULL;
+	}
+
+	return 0;
+}
+
+bool
+dwiic_suspend(device_t self, const pmf_qual_t *qual)
+{
+	struct dwiic_softc *sc = device_private(self);
+
+	/* disable controller */
+	dwiic_enable(sc, 0);
+
+	/* disable interrupts */
+	dwiic_write(sc, DW_IC_INTR_MASK, 0);
+	dwiic_read(sc, DW_IC_CLR_INTR);
+	if (sc->sc_power != NULL) {
+		if (!sc->sc_power(sc, 0)) {
+			aprint_error_dev(sc->sc_dev, "failed to power off\n");
+		}
+	}
+	return true;
+}
+
+bool
+dwiic_resume(device_t self, const pmf_qual_t *qual)
+{
+	struct dwiic_softc *sc = device_private(self);
+	if (sc->sc_power != NULL) {
+		if (!sc->sc_power(sc, 1)) {
+			aprint_error_dev(sc->sc_dev, "failed to power up\n");
+			return false;
+		}
+	}
+	dwiic_init(sc);
+	return true;
+}
+
+static 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", device_xname(sc->sc_dev), offset, b));
+
+	return b;
+}
+
+static 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", device_xname(sc->sc_dev), offset,
+	    val));
+}
+
+static 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);
+
+	mutex_enter(&sc->sc_i2c_lock);
+	return 1;
+}
+
+void
+dwiic_i2c_release_bus(void *cookie, int flags)
+{
+	struct dwiic_softc *sc = cookie;
+
+	if (cold || sc->sc_poll || (flags & I2C_F_POLL))
+		return;
+
+	mutex_exit(&sc->sc_i2c_lock);
+}
+
+static 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",
+		    device_xname(sc->sc_dev), 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;
+}
+
+static void
+dwiic_enable(struct dwiic_softc *sc, bool 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);
+	}
+
+	aprint_error_dev(sc->sc_dev, "failed to %sable\n",
+	    (enable ? "en" : "dis"));
+}
+
+static 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;
+	const uint8_t *bcmd;
+	uint8_t *bdata;
+
+	if (cold || sc->sc_poll)
+		flags |= I2C_F_POLL;
+
+	DPRINTF(("%s: %s: op %d, addr 0x%02x, cmdlen %zu, len %zu, "
+	    "flags 0x%02x\n", device_xname(sc->sc_dev), __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", device_xname(sc->sc_dev), __func__, st));
+	if (st & DW_IC_STATUS_ACTIVITY) {
+		return (1);
+	}
+
+	/* 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 {
+		mutex_enter(&sc->sc_int_lock);
+		dwiic_read(sc, DW_IC_CLR_INTR);
+		dwiic_write(sc, DW_IC_INTR_MASK, DW_IC_INTR_TX_EMPTY);
+
+		if (cv_timedwait(&sc->sc_int_writewait,
+		    &sc->sc_int_lock, hz / 2) != 0)
+			aprint_error_dev(sc->sc_dev,
+			    "timed out waiting for tx_empty intr\n");
+		dwiic_write(sc, DW_IC_INTR_MASK, 0);
+		dwiic_read(sc, DW_IC_CLR_INTR);
+		mutex_exit(&sc->sc_int_lock);
+	}
+
+	/* send our command, one byte at a time */
+	if (cmdlen > 0) {
+		bcmd = (const void *)cmdbuf;
+
+		DPRINTF(("%s: %s: sending cmd (len %zu):", device_xname(sc->sc_dev),
+		    __func__, cmdlen));
+		for (x = 0; x < cmdlen; x++)
+			DPRINTF((" %02x", bcmd[x]));
+		DPRINTF(("\n"));
+
+		tx_limit = sc->tx_fifo_depth - dwiic_read(sc, DW_IC_TXFLR);
+		if (cmdlen > tx_limit) {
+			/* TODO */
+			aprint_error_dev(sc->sc_dev, "can't write %zu (> %d)\n",
+			    cmdlen, tx_limit);
+			sc->sc_i2c_xfer.error = 1;
+			return (1);
+		}
+
+		for (x = 0; x < cmdlen; x++) {
+			cmd = bcmd[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);
+		}
+	}
+
+	bdata = (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",
+		device_xname(sc->sc_dev), __func__, len, tx_limit));
+
+	while (x < len) {
+		if (I2C_OP_WRITE_P(op))
+			cmd = bdata[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",
+			    device_xname(sc->sc_dev), __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 {
+				mutex_enter(&sc->sc_int_lock);
+				dwiic_read(sc, DW_IC_CLR_INTR);
+				dwiic_write(sc, DW_IC_INTR_MASK,
+				    DW_IC_INTR_RX_FULL);
+
+				if (cv_timedwait(&sc->sc_int_readwait,
+				    &sc->sc_int_lock, hz / 2) != 0)
+					aprint_error_dev(sc->sc_dev,
+					    "timed out waiting for "
+					    "rx_full intr\n");
+
+				dwiic_write(sc, DW_IC_INTR_MASK, 0);
+				dwiic_read(sc, DW_IC_CLR_INTR);
+				mutex_exit(&sc->sc_int_lock);
+				rx_avail = dwiic_read(sc, DW_IC_RXFLR);
+			}
+
+			if (rx_avail == 0) {
+				aprint_error_dev(sc->sc_dev,
+				    "timed out reading remaining %d\n",
+				    (int)(len - 1 - readpos));
+				sc->sc_i2c_xfer.error = 1;
+				return (1);
+			}
+
+			DPRINTF(("%s: %s: %d avail to read (%zu remaining)\n",
+			    device_xname(sc->sc_dev), __func__, rx_avail,
+			    len - readpos));
+
+			while (rx_avail > 0) {
+				resp = dwiic_read(sc, DW_IC_DATA_CMD);
+				if (readpos < len) {
+					bdata[readpos] = resp;
+					readpos++;
+				}
+				rx_avail--;
+			}
+
+			if (readpos >= len)
+				break;
+
+			DPRINTF(("%s: still need to read %d bytes\n",
+			    device_xname(sc->sc_dev), (int)(len - readpos)));
+			tx_limit = sc->tx_fifo_depth -
+			    dwiic_read(sc, DW_IC_TXFLR);
+		}
+	}
+
+	return 0;
+}
+
+static 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", device_xname(sc->sc_dev),
+	    __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", device_xname(sc->sc_dev),
+		    __func__));
+	else {
+		mutex_enter(&sc->sc_int_lock);
+		if (stat & DW_IC_INTR_RX_FULL) {
+			dwiic_write(sc, DW_IC_INTR_MASK, 0);
+			DPRINTF(("%s: %s: waking up reader\n",
+			    device_xname(sc->sc_dev), __func__));
+			cv_signal(&sc->sc_int_readwait);
+		}
+		if (stat & DW_IC_INTR_TX_EMPTY) {
+			dwiic_write(sc, DW_IC_INTR_MASK, 0);
+			DPRINTF(("%s: %s: waking up writer\n",
+			    device_xname(sc->sc_dev), __func__));
+			cv_signal(&sc->sc_int_writewait);
+		}
+		mutex_exit(&sc->sc_int_lock);
+	}
+
+	return 1;
+}
Index: src/sys/dev/ic/dwiic_var.h
diff -u /dev/null src/sys/dev/ic/dwiic_var.h:1.1
--- /dev/null	Sun Dec 10 17:12:54 2017
+++ src/sys/dev/ic/dwiic_var.h	Sun Dec 10 17:12:54 2017
@@ -0,0 +1,80 @@
+/* $NetBSD: dwiic_var.h,v 1.1 2017/12/10 17:12:54 bouyer 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 <dev/i2c/i2cvar.h>
+
+enum dwiic_type {
+	dwiic_type_generic,
+	dwiic_type_sunrisepoint
+};
+
+struct dwiic_softc {
+	device_t		sc_dev;
+
+	bus_space_tag_t		sc_iot;
+	bus_space_handle_t	sc_ioh;
+	void			*sc_ih;
+
+	enum dwiic_type		sc_type;
+
+	bool			(*sc_power)(struct dwiic_softc *, bool);
+
+	struct i2cbus_attach_args sc_iba;
+	device_t		sc_iic;
+
+	int			sc_poll;
+	kmutex_t		sc_int_lock;
+	kcondvar_t		sc_int_readwait;
+	kcondvar_t		sc_int_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;
+	kmutex_t		sc_i2c_lock;
+	struct {
+		i2c_op_t	op;
+		void		*buf;
+		size_t		len;
+		int		flags;
+		volatile int	error;
+	} sc_i2c_xfer;
+};
+
+bool		dwiic_attach(struct dwiic_softc *);
+int		dwiic_detach(device_t, int);
+bool		dwiic_suspend(device_t, const pmf_qual_t *);
+bool		dwiic_resume(device_t, const pmf_qual_t *);
+
+int		dwiic_intr(void *);

Reply via email to