summaryrefslogtreecommitdiff
path: root/sys/dev/ic
diff options
context:
space:
mode:
authorJoshua Stein <jcs@cvs.openbsd.org>2017-11-16 18:12:28 +0000
committerJoshua Stein <jcs@cvs.openbsd.org>2017-11-16 18:12:28 +0000
commit64379728770a0c634858b536d00579854bc187fc (patch)
tree5f878bfbcc055b5715284bdd1dfbe77afaca52c9 /sys/dev/ic
parent05b4526a081b6d4e14559f8419f88b4af377d3a8 (diff)
Add PCI attachment for dwiic(4) needed by Intel 100 Series machines
ok kettenis
Diffstat (limited to 'sys/dev/ic')
-rw-r--r--sys/dev/ic/dwiic.c490
-rw-r--r--sys/dev/ic/dwiicreg.h94
-rw-r--r--sys/dev/ic/dwiicvar.h103
3 files changed, 687 insertions, 0 deletions
diff --git a/sys/dev/ic/dwiic.c b/sys/dev/ic/dwiic.c
new file mode 100644
index 00000000000..02d36e2a9f2
--- /dev/null
+++ b/sys/dev/ic/dwiic.c
@@ -0,0 +1,490 @@
+/* $OpenBSD: dwiic.c,v 1.1 2017/11/16 18:12:27 jcs Exp $ */
+/*
+ * Synopsys DesignWare I2C controller
+ *
+ * Copyright (c) 2015-2017 joshua stein <jcs@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;
+}
diff --git a/sys/dev/ic/dwiicreg.h b/sys/dev/ic/dwiicreg.h
new file mode 100644
index 00000000000..78b6076cec8
--- /dev/null
+++ b/sys/dev/ic/dwiicreg.h
@@ -0,0 +1,94 @@
+/* $OpenBSD: dwiicreg.h,v 1.1 2017/11/16 18:12:27 jcs Exp $ */
+/*
+ * Synopsys DesignWare I2C controller
+ *
+ * Copyright (c) 2015, 2016 joshua stein <jcs@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
diff --git a/sys/dev/ic/dwiicvar.h b/sys/dev/ic/dwiicvar.h
new file mode 100644
index 00000000000..afbbd24bea3
--- /dev/null
+++ b/sys/dev/ic/dwiicvar.h
@@ -0,0 +1,103 @@
+/* $OpenBSD: dwiicvar.h,v 1.1 2017/11/16 18:12:27 jcs Exp $ */
+/*
+ * Synopsys DesignWare I2C controller
+ *
+ * Copyright (c) 2015, 2016 joshua stein <jcs@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);