summaryrefslogtreecommitdiffstats
path: root/sys/dev
diff options
context:
space:
mode:
authorgrembo <grembo@FreeBSD.org>2015-05-30 12:17:18 +0000
committergrembo <grembo@FreeBSD.org>2015-05-30 12:17:18 +0000
commit551209395dee553219c1523835556ac65906be46 (patch)
tree638adc01b6e50928c2552df1f7f1f4ff9b296c5a /sys/dev
parent9a1d54a1000576601d35cb26c877936608660cf6 (diff)
downloadFreeBSD-src-551209395dee553219c1523835556ac65906be46.zip
FreeBSD-src-551209395dee553219c1523835556ac65906be46.tar.gz
ig4 - Intel fourth gen integrated I2C SMBus driver.
Differential Revision: https://reviews.freebsd.org/D2372 Reviewed by: jhb, wblock, adrian Approved by: jhb, wblock Relnotes: yes
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/ichiic/ig4_iic.c966
-rw-r--r--sys/dev/ichiic/ig4_pci.c192
-rw-r--r--sys/dev/ichiic/ig4_reg.h622
-rw-r--r--sys/dev/ichiic/ig4_var.h96
4 files changed, 1876 insertions, 0 deletions
diff --git a/sys/dev/ichiic/ig4_iic.c b/sys/dev/ichiic/ig4_iic.c
new file mode 100644
index 0000000..e9081e3
--- /dev/null
+++ b/sys/dev/ichiic/ig4_iic.c
@@ -0,0 +1,966 @@
+/*
+ * Copyright (c) 2014 The DragonFly Project. All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Matthew Dillon <dillon@backplane.com> and was subsequently ported
+ * to FreeBSD by Michael Gmelin <freebsd@grem.de>
+ *
+ * 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.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Intel fourth generation mobile cpus integrated I2C device, smbus driver.
+ *
+ * See ig4_reg.h for datasheet reference and notes.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/errno.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/syslog.h>
+#include <sys/bus.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+#include <sys/rman.h>
+
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
+#include <dev/smbus/smbconf.h>
+
+#include <dev/ichiic/ig4_reg.h>
+#include <dev/ichiic/ig4_var.h>
+
+#define TRANS_NORMAL 1
+#define TRANS_PCALL 2
+#define TRANS_BLOCK 3
+
+static void ig4iic_start(void *xdev);
+static void ig4iic_intr(void *cookie);
+static void ig4iic_dump(ig4iic_softc_t *sc);
+
+static int ig4_dump;
+SYSCTL_INT(_debug, OID_AUTO, ig4_dump, CTLTYPE_INT | CTLFLAG_RW,
+ &ig4_dump, 0, "");
+
+/*
+ * Low-level inline support functions
+ */
+static __inline void
+reg_write(ig4iic_softc_t *sc, uint32_t reg, uint32_t value)
+{
+ bus_write_4(sc->regs_res, reg, value);
+ bus_barrier(sc->regs_res, reg, 4, BUS_SPACE_BARRIER_WRITE);
+}
+
+static __inline uint32_t
+reg_read(ig4iic_softc_t *sc, uint32_t reg)
+{
+ uint32_t value;
+
+ bus_barrier(sc->regs_res, reg, 4, BUS_SPACE_BARRIER_READ);
+ value = bus_read_4(sc->regs_res, reg);
+ return (value);
+}
+
+/*
+ * Enable or disable the controller and wait for the controller to acknowledge
+ * the state change.
+ */
+static int
+set_controller(ig4iic_softc_t *sc, uint32_t ctl)
+{
+ int retry;
+ int error;
+ uint32_t v;
+
+ reg_write(sc, IG4_REG_I2C_EN, ctl);
+ error = SMB_ETIMEOUT;
+
+ for (retry = 100; retry > 0; --retry) {
+ v = reg_read(sc, IG4_REG_ENABLE_STATUS);
+ if (((v ^ ctl) & IG4_I2C_ENABLE) == 0) {
+ error = 0;
+ break;
+ }
+ mtx_sleep(sc, &sc->mutex, 0, "i2cslv", 1);
+ }
+ return (error);
+}
+
+/*
+ * Wait up to 25ms for the requested status using a 25uS polling loop.
+ */
+static int
+wait_status(ig4iic_softc_t *sc, uint32_t status)
+{
+ uint32_t v;
+ int error;
+ int txlvl = -1;
+ u_int count_us = 0;
+ u_int limit_us = 25000; /* 25ms */
+
+ error = SMB_ETIMEOUT;
+
+ for (;;) {
+ /*
+ * Check requested status
+ */
+ v = reg_read(sc, IG4_REG_I2C_STA);
+ if (v & status) {
+ error = 0;
+ break;
+ }
+
+ /*
+ * When waiting for receive data break-out if the interrupt
+ * loaded data into the FIFO.
+ */
+ if (status & IG4_STATUS_RX_NOTEMPTY) {
+ if (sc->rpos != sc->rnext) {
+ error = 0;
+ break;
+ }
+ }
+
+ /*
+ * When waiting for the transmit FIFO to become empty,
+ * reset the timeout if we see a change in the transmit
+ * FIFO level as progress is being made.
+ */
+ if (status & IG4_STATUS_TX_EMPTY) {
+ v = reg_read(sc, IG4_REG_TXFLR) & IG4_FIFOLVL_MASK;
+ if (txlvl != v) {
+ txlvl = v;
+ count_us = 0;
+ }
+ }
+
+ /*
+ * Stop if we've run out of time.
+ */
+ if (count_us >= limit_us)
+ break;
+
+ /*
+ * When waiting for receive data let the interrupt do its
+ * work, otherwise poll with the lock held.
+ */
+ if (status & IG4_STATUS_RX_NOTEMPTY) {
+ mtx_sleep(sc, &sc->mutex, PZERO, "i2cwait",
+ (hz + 99) / 100); /* sleep up to 10ms */
+ count_us += 10000;
+ } else {
+ DELAY(25);
+ count_us += 25;
+ }
+ }
+
+ return (error);
+}
+
+/*
+ * Read I2C data. The data might have already been read by
+ * the interrupt code, otherwise it is sitting in the data
+ * register.
+ */
+static uint8_t
+data_read(ig4iic_softc_t *sc)
+{
+ uint8_t c;
+
+ if (sc->rpos == sc->rnext) {
+ c = (uint8_t)reg_read(sc, IG4_REG_DATA_CMD);
+ } else {
+ c = sc->rbuf[sc->rpos & IG4_RBUFMASK];
+ ++sc->rpos;
+ }
+ return (c);
+}
+
+/*
+ * Set the slave address. The controller must be disabled when
+ * changing the address.
+ *
+ * This operation does not issue anything to the I2C bus but sets
+ * the target address for when the controller later issues a START.
+ */
+static void
+set_slave_addr(ig4iic_softc_t *sc, uint8_t slave, int trans_op)
+{
+ uint32_t tar;
+ uint32_t ctl;
+ int use_10bit;
+
+ use_10bit = sc->use_10bit;
+ if (trans_op & SMB_TRANS_7BIT)
+ use_10bit = 0;
+ if (trans_op & SMB_TRANS_10BIT)
+ use_10bit = 1;
+
+ if (sc->slave_valid && sc->last_slave == slave &&
+ sc->use_10bit == use_10bit) {
+ return;
+ }
+ sc->use_10bit = use_10bit;
+
+ /*
+ * Wait for TXFIFO to drain before disabling the controller.
+ *
+ * If a write message has not been completed it's really a
+ * programming error, but for now in that case issue an extra
+ * byte + STOP.
+ *
+ * If a read message has not been completed it's also a programming
+ * error, for now just ignore it.
+ */
+ wait_status(sc, IG4_STATUS_TX_NOTFULL);
+ if (sc->write_started) {
+ reg_write(sc, IG4_REG_DATA_CMD, IG4_DATA_STOP);
+ sc->write_started = 0;
+ }
+ if (sc->read_started)
+ sc->read_started = 0;
+ wait_status(sc, IG4_STATUS_TX_EMPTY);
+
+ set_controller(sc, 0);
+ ctl = reg_read(sc, IG4_REG_CTL);
+ ctl &= ~IG4_CTL_10BIT;
+ ctl |= IG4_CTL_RESTARTEN;
+
+ tar = slave;
+ if (sc->use_10bit) {
+ tar |= IG4_TAR_10BIT;
+ ctl |= IG4_CTL_10BIT;
+ }
+ reg_write(sc, IG4_REG_CTL, ctl);
+ reg_write(sc, IG4_REG_TAR_ADD, tar);
+ set_controller(sc, IG4_I2C_ENABLE);
+ sc->slave_valid = 1;
+ sc->last_slave = slave;
+}
+
+/*
+ * Issue START with byte command, possible count, and a variable length
+ * read or write buffer, then possible turn-around read. The read also
+ * has a possible count received.
+ *
+ * For SMBUS -
+ *
+ * Quick: START+ADDR+RD/WR STOP
+ *
+ * Normal: START+ADDR+WR CMD DATA..DATA STOP
+ *
+ * START+ADDR+RD CMD
+ * RESTART+ADDR RDATA..RDATA STOP
+ * (can also be used for I2C transactions)
+ *
+ * Process Call: START+ADDR+WR CMD DATAL DATAH
+ * RESTART+ADDR+RD RDATAL RDATAH STOP
+ *
+ * Block: START+ADDR+RD CMD
+ * RESTART+ADDR+RD RCOUNT DATA... STOP
+ *
+ * START+ADDR+WR CMD
+ * RESTART+ADDR+WR WCOUNT DATA... STOP
+ *
+ * For I2C - basically, no *COUNT fields, possibly no *CMD field. If the
+ * sender needs to issue a 2-byte command it will incorporate it
+ * into the write buffer and also set NOCMD.
+ *
+ * Generally speaking, the START+ADDR / RESTART+ADDR is handled automatically
+ * by the controller at the beginning of a command sequence or on a data
+ * direction turn-around, and we only need to tell it when to issue the STOP.
+ */
+static int
+smb_transaction(ig4iic_softc_t *sc, char cmd, int op,
+ char *wbuf, int wcount, char *rbuf, int rcount, int *actualp)
+{
+ int error;
+ int unit;
+ uint32_t last;
+
+ /*
+ * Debugging - dump registers
+ */
+ if (ig4_dump) {
+ unit = device_get_unit(sc->dev);
+ if (ig4_dump & (1 << unit)) {
+ ig4_dump &= ~(1 << unit);
+ ig4iic_dump(sc);
+ }
+ }
+
+ /*
+ * Issue START or RESTART with next data byte, clear any previous
+ * abort condition that may have been holding the txfifo in reset.
+ */
+ last = IG4_DATA_RESTART;
+ reg_read(sc, IG4_REG_CLR_TX_ABORT);
+ if (actualp)
+ *actualp = 0;
+
+ /*
+ * Issue command if not told otherwise (smbus).
+ */
+ if ((op & SMB_TRANS_NOCMD) == 0) {
+ error = wait_status(sc, IG4_STATUS_TX_NOTFULL);
+ if (error)
+ goto done;
+ last |= (u_char)cmd;
+ if (wcount == 0 && rcount == 0 && (op & SMB_TRANS_NOSTOP) == 0)
+ last |= IG4_DATA_STOP;
+ reg_write(sc, IG4_REG_DATA_CMD, last);
+ last = 0;
+ }
+
+ /*
+ * Clean out any previously received data.
+ */
+ if (sc->rpos != sc->rnext &&
+ (op & SMB_TRANS_NOREPORT) == 0) {
+ device_printf(sc->dev,
+ "discarding %d bytes of spurious data\n",
+ sc->rnext - sc->rpos);
+ }
+ sc->rpos = 0;
+ sc->rnext = 0;
+
+ /*
+ * If writing and not told otherwise, issue the write count (smbus).
+ */
+ if (wcount && (op & SMB_TRANS_NOCNT) == 0) {
+ error = wait_status(sc, IG4_STATUS_TX_NOTFULL);
+ if (error)
+ goto done;
+ last |= (u_char)cmd;
+ reg_write(sc, IG4_REG_DATA_CMD, last);
+ last = 0;
+ }
+
+ /*
+ * Bulk write (i2c)
+ */
+ while (wcount) {
+ error = wait_status(sc, IG4_STATUS_TX_NOTFULL);
+ if (error)
+ goto done;
+ last |= (u_char)*wbuf;
+ if (wcount == 1 && rcount == 0 && (op & SMB_TRANS_NOSTOP) == 0)
+ last |= IG4_DATA_STOP;
+ reg_write(sc, IG4_REG_DATA_CMD, last);
+ --wcount;
+ ++wbuf;
+ last = 0;
+ }
+
+ /*
+ * Issue reads to xmit FIFO (strange, I know) to tell the controller
+ * to clock in data. At the moment just issue one read ahead to
+ * pipeline the incoming data.
+ *
+ * NOTE: In the case of NOCMD and wcount == 0 we still issue a
+ * RESTART here, even if the data direction has not changed
+ * from the previous CHAINing call. This we force the RESTART.
+ * (A new START is issued automatically by the controller in
+ * the other nominal cases such as a data direction change or
+ * a previous STOP was issued).
+ *
+ * If this will be the last byte read we must also issue the STOP
+ * at the end of the read.
+ */
+ if (rcount) {
+ last = IG4_DATA_RESTART | IG4_DATA_COMMAND_RD;
+ if (rcount == 1 &&
+ (op & (SMB_TRANS_NOSTOP | SMB_TRANS_NOCNT)) ==
+ SMB_TRANS_NOCNT) {
+ last |= IG4_DATA_STOP;
+ }
+ reg_write(sc, IG4_REG_DATA_CMD, last);
+ last = IG4_DATA_COMMAND_RD;
+ }
+
+ /*
+ * Bulk read (i2c) and count field handling (smbus)
+ */
+ while (rcount) {
+ /*
+ * Maintain a pipeline by queueing the allowance for the next
+ * read before waiting for the current read.
+ */
+ if (rcount > 1) {
+ if (op & SMB_TRANS_NOCNT)
+ last = (rcount == 2) ? IG4_DATA_STOP : 0;
+ else
+ last = 0;
+ reg_write(sc, IG4_REG_DATA_CMD, IG4_DATA_COMMAND_RD |
+ last);
+ }
+ error = wait_status(sc, IG4_STATUS_RX_NOTEMPTY);
+ if (error) {
+ if ((op & SMB_TRANS_NOREPORT) == 0) {
+ device_printf(sc->dev,
+ "rx timeout addr 0x%02x\n",
+ sc->last_slave);
+ }
+ goto done;
+ }
+ last = data_read(sc);
+
+ if (op & SMB_TRANS_NOCNT) {
+ *rbuf = (u_char)last;
+ ++rbuf;
+ --rcount;
+ if (actualp)
+ ++*actualp;
+ } else {
+ /*
+ * Handle count field (smbus), which is not part of
+ * the rcount'ed buffer. The first read data in a
+ * bulk transfer is the count.
+ *
+ * XXX if rcount is loaded as 0 how do I generate a
+ * STOP now without issuing another RD or WR?
+ */
+ if (rcount > (u_char)last)
+ rcount = (u_char)last;
+ op |= SMB_TRANS_NOCNT;
+ }
+ }
+ error = 0;
+done:
+ /* XXX wait for xmit buffer to become empty */
+ last = reg_read(sc, IG4_REG_TX_ABRT_SOURCE);
+
+ return (error);
+}
+
+/*
+ * SMBUS API FUNCTIONS
+ *
+ * Called from ig4iic_pci_attach/detach()
+ */
+int
+ig4iic_attach(ig4iic_softc_t *sc)
+{
+ int error;
+ uint32_t v;
+
+ v = reg_read(sc, IG4_REG_COMP_TYPE);
+ v = reg_read(sc, IG4_REG_COMP_PARAM1);
+ v = reg_read(sc, IG4_REG_GENERAL);
+ if ((v & IG4_GENERAL_SWMODE) == 0) {
+ v |= IG4_GENERAL_SWMODE;
+ reg_write(sc, IG4_REG_GENERAL, v);
+ v = reg_read(sc, IG4_REG_GENERAL);
+ }
+
+ v = reg_read(sc, IG4_REG_SW_LTR_VALUE);
+ v = reg_read(sc, IG4_REG_AUTO_LTR_VALUE);
+
+ v = reg_read(sc, IG4_REG_COMP_VER);
+ if (v != IG4_COMP_VER) {
+ error = ENXIO;
+ goto done;
+ }
+ v = reg_read(sc, IG4_REG_SS_SCL_HCNT);
+ v = reg_read(sc, IG4_REG_SS_SCL_LCNT);
+ v = reg_read(sc, IG4_REG_FS_SCL_HCNT);
+ v = reg_read(sc, IG4_REG_FS_SCL_LCNT);
+ v = reg_read(sc, IG4_REG_SDA_HOLD);
+
+ v = reg_read(sc, IG4_REG_SS_SCL_HCNT);
+ reg_write(sc, IG4_REG_FS_SCL_HCNT, v);
+ v = reg_read(sc, IG4_REG_SS_SCL_LCNT);
+ reg_write(sc, IG4_REG_FS_SCL_LCNT, v);
+
+ /*
+ * Program based on a 25000 Hz clock. This is a bit of a
+ * hack (obviously). The defaults are 400 and 470 for standard
+ * and 60 and 130 for fast. The defaults for standard fail
+ * utterly (presumably cause an abort) because the clock time
+ * is ~18.8ms by default. This brings it down to ~4ms (for now).
+ */
+ reg_write(sc, IG4_REG_SS_SCL_HCNT, 100);
+ reg_write(sc, IG4_REG_SS_SCL_LCNT, 125);
+ reg_write(sc, IG4_REG_FS_SCL_HCNT, 100);
+ reg_write(sc, IG4_REG_FS_SCL_LCNT, 125);
+
+ /*
+ * Use a threshold of 1 so we get interrupted on each character,
+ * allowing us to use mtx_sleep() in our poll code. Not perfect
+ * but this is better than using DELAY() for receiving data.
+ */
+ reg_write(sc, IG4_REG_RX_TL, 1);
+
+ reg_write(sc, IG4_REG_CTL,
+ IG4_CTL_MASTER |
+ IG4_CTL_SLAVE_DISABLE |
+ IG4_CTL_RESTARTEN |
+ IG4_CTL_SPEED_STD);
+
+ sc->smb = device_add_child(sc->dev, "smbus", -1);
+ if (sc->smb == NULL) {
+ device_printf(sc->dev, "smbus driver not found\n");
+ error = ENXIO;
+ goto done;
+ }
+
+#if 0
+ /*
+ * Don't do this, it blows up the PCI config
+ */
+ reg_write(sc, IG4_REG_RESETS, IG4_RESETS_ASSERT);
+ reg_write(sc, IG4_REG_RESETS, IG4_RESETS_DEASSERT);
+#endif
+
+ /*
+ * Interrupt on STOP detect or receive character ready
+ */
+ reg_write(sc, IG4_REG_INTR_MASK, IG4_INTR_STOP_DET |
+ IG4_INTR_RX_FULL);
+ mtx_lock(&sc->mutex);
+ if (set_controller(sc, 0))
+ device_printf(sc->dev, "controller error during attach-1\n");
+ if (set_controller(sc, IG4_I2C_ENABLE))
+ device_printf(sc->dev, "controller error during attach-2\n");
+ mtx_unlock(&sc->mutex);
+ error = bus_setup_intr(sc->dev, sc->intr_res, INTR_TYPE_MISC | INTR_MPSAFE,
+ NULL, ig4iic_intr, sc, &sc->intr_handle);
+ if (error) {
+ device_printf(sc->dev,
+ "Unable to setup irq: error %d\n", error);
+ }
+
+ sc->enum_hook.ich_func = ig4iic_start;
+ sc->enum_hook.ich_arg = sc->dev;
+
+ /* We have to wait until interrupts are enabled. I2C read and write
+ * only works if the interrupts are available.
+ */
+ if (config_intrhook_establish(&sc->enum_hook) != 0)
+ error = ENOMEM;
+ else
+ error = 0;
+
+done:
+ return (error);
+}
+
+void
+ig4iic_start(void *xdev)
+{
+ int error;
+ ig4iic_softc_t *sc;
+ device_t dev = (device_t)xdev;
+
+ sc = device_get_softc(dev);
+
+ config_intrhook_disestablish(&sc->enum_hook);
+
+ /* Attach us to the smbus */
+ error = bus_generic_attach(sc->dev);
+ if (error) {
+ device_printf(sc->dev,
+ "failed to attach child: error %d\n", error);
+ }
+}
+
+
+
+int
+ig4iic_detach(ig4iic_softc_t *sc)
+{
+ int error;
+
+ if (device_is_attached(sc->dev)) {
+ error = bus_generic_detach(sc->dev);
+ if (error)
+ return (error);
+ }
+ if (sc->smb)
+ device_delete_child(sc->dev, sc->smb);
+ if (sc->intr_handle)
+ bus_teardown_intr(sc->dev, sc->intr_res, sc->intr_handle);
+
+ mtx_lock(&sc->mutex);
+
+ sc->smb = NULL;
+ sc->intr_handle = NULL;
+ reg_write(sc, IG4_REG_INTR_MASK, 0);
+ reg_read(sc, IG4_REG_CLR_INTR);
+ set_controller(sc, 0);
+
+ mtx_unlock(&sc->mutex);
+ return (0);
+}
+
+int
+ig4iic_smb_callback(device_t dev, int index, void *data)
+{
+ ig4iic_softc_t *sc = device_get_softc(dev);
+ int error;
+
+ mtx_lock(&sc->mutex);
+
+ switch (index) {
+ case SMB_REQUEST_BUS:
+ error = 0;
+ break;
+ case SMB_RELEASE_BUS:
+ error = 0;
+ break;
+ default:
+ error = SMB_EABORT;
+ break;
+ }
+
+ mtx_unlock(&sc->mutex);
+
+ return (error);
+}
+
+/*
+ * Quick command. i.e. START + cmd + R/W + STOP and no data. It is
+ * unclear to me how I could implement this with the intel i2c controller
+ * because the controler sends STARTs and STOPs automatically with data.
+ */
+int
+ig4iic_smb_quick(device_t dev, u_char slave, int how)
+{
+ ig4iic_softc_t *sc = device_get_softc(dev);
+ int error;
+
+ mtx_lock(&sc->mutex);
+
+ switch (how) {
+ case SMB_QREAD:
+ error = SMB_ENOTSUPP;
+ break;
+ case SMB_QWRITE:
+ error = SMB_ENOTSUPP;
+ break;
+ default:
+ error = SMB_ENOTSUPP;
+ break;
+ }
+ mtx_unlock(&sc->mutex);
+
+ return (error);
+}
+
+/*
+ * Incremental send byte without stop (?). It is unclear why the slave
+ * address is specified if this presumably is used in combination with
+ * ig4iic_smb_quick().
+ *
+ * (Also, how would this work anyway? Issue the last byte with writeb()?)
+ */
+int
+ig4iic_smb_sendb(device_t dev, u_char slave, char byte)
+{
+ ig4iic_softc_t *sc = device_get_softc(dev);
+ uint32_t cmd;
+ int error;
+
+ mtx_lock(&sc->mutex);
+
+ set_slave_addr(sc, slave, 0);
+ cmd = byte;
+ if (wait_status(sc, IG4_STATUS_TX_NOTFULL) == 0) {
+ reg_write(sc, IG4_REG_DATA_CMD, cmd);
+ error = 0;
+ } else {
+ error = SMB_ETIMEOUT;
+ }
+
+ mtx_unlock(&sc->mutex);
+ return (error);
+}
+
+/*
+ * Incremental receive byte without stop (?). It is unclear why the slave
+ * address is specified if this presumably is used in combination with
+ * ig4iic_smb_quick().
+ */
+int
+ig4iic_smb_recvb(device_t dev, u_char slave, char *byte)
+{
+ ig4iic_softc_t *sc = device_get_softc(dev);
+ int error;
+
+ mtx_lock(&sc->mutex);
+
+ set_slave_addr(sc, slave, 0);
+ reg_write(sc, IG4_REG_DATA_CMD, IG4_DATA_COMMAND_RD);
+ if (wait_status(sc, IG4_STATUS_RX_NOTEMPTY) == 0) {
+ *byte = data_read(sc);
+ error = 0;
+ } else {
+ *byte = 0;
+ error = SMB_ETIMEOUT;
+ }
+
+ mtx_unlock(&sc->mutex);
+ return (error);
+}
+
+/*
+ * Write command and single byte in transaction.
+ */
+int
+ig4iic_smb_writeb(device_t dev, u_char slave, char cmd, char byte)
+{
+ ig4iic_softc_t *sc = device_get_softc(dev);
+ int error;
+
+ mtx_lock(&sc->mutex);
+
+ set_slave_addr(sc, slave, 0);
+ error = smb_transaction(sc, cmd, SMB_TRANS_NOCNT,
+ &byte, 1, NULL, 0, NULL);
+
+ mtx_unlock(&sc->mutex);
+ return (error);
+}
+
+/*
+ * Write command and single word in transaction.
+ */
+int
+ig4iic_smb_writew(device_t dev, u_char slave, char cmd, short word)
+{
+ ig4iic_softc_t *sc = device_get_softc(dev);
+ char buf[2];
+ int error;
+
+ mtx_lock(&sc->mutex);
+
+ set_slave_addr(sc, slave, 0);
+ buf[0] = word & 0xFF;
+ buf[1] = word >> 8;
+ error = smb_transaction(sc, cmd, SMB_TRANS_NOCNT,
+ buf, 2, NULL, 0, NULL);
+
+ mtx_unlock(&sc->mutex);
+ return (error);
+}
+
+/*
+ * write command and read single byte in transaction.
+ */
+int
+ig4iic_smb_readb(device_t dev, u_char slave, char cmd, char *byte)
+{
+ ig4iic_softc_t *sc = device_get_softc(dev);
+ int error;
+
+ mtx_lock(&sc->mutex);
+
+ set_slave_addr(sc, slave, 0);
+ error = smb_transaction(sc, cmd, SMB_TRANS_NOCNT,
+ NULL, 0, byte, 1, NULL);
+
+ mtx_unlock(&sc->mutex);
+ return (error);
+}
+
+/*
+ * write command and read word in transaction.
+ */
+int
+ig4iic_smb_readw(device_t dev, u_char slave, char cmd, short *word)
+{
+ ig4iic_softc_t *sc = device_get_softc(dev);
+ char buf[2];
+ int error;
+
+ mtx_lock(&sc->mutex);
+
+ set_slave_addr(sc, slave, 0);
+ if ((error = smb_transaction(sc, cmd, SMB_TRANS_NOCNT,
+ NULL, 0, buf, 2, NULL)) == 0) {
+ *word = (u_char)buf[0] | ((u_char)buf[1] << 8);
+ }
+
+ mtx_unlock(&sc->mutex);
+ return (error);
+}
+
+/*
+ * write command and word and read word in transaction
+ */
+int
+ig4iic_smb_pcall(device_t dev, u_char slave, char cmd,
+ short sdata, short *rdata)
+{
+ ig4iic_softc_t *sc = device_get_softc(dev);
+ char rbuf[2];
+ char wbuf[2];
+ int error;
+
+ mtx_lock(&sc->mutex);
+
+ set_slave_addr(sc, slave, 0);
+ wbuf[0] = sdata & 0xFF;
+ wbuf[1] = sdata >> 8;
+ if ((error = smb_transaction(sc, cmd, SMB_TRANS_NOCNT,
+ wbuf, 2, rbuf, 2, NULL)) == 0) {
+ *rdata = (u_char)rbuf[0] | ((u_char)rbuf[1] << 8);
+ }
+
+ mtx_unlock(&sc->mutex);
+ return (error);
+}
+
+int
+ig4iic_smb_bwrite(device_t dev, u_char slave, char cmd,
+ u_char wcount, char *buf)
+{
+ ig4iic_softc_t *sc = device_get_softc(dev);
+ int error;
+
+ mtx_lock(&sc->mutex);
+
+ set_slave_addr(sc, slave, 0);
+ error = smb_transaction(sc, cmd, 0,
+ buf, wcount, NULL, 0, NULL);
+
+ mtx_unlock(&sc->mutex);
+ return (error);
+}
+
+int
+ig4iic_smb_bread(device_t dev, u_char slave, char cmd,
+ u_char *countp_char, char *buf)
+{
+ ig4iic_softc_t *sc = device_get_softc(dev);
+ int rcount = *countp_char;
+ int error;
+
+ mtx_lock(&sc->mutex);
+
+ set_slave_addr(sc, slave, 0);
+ error = smb_transaction(sc, cmd, 0,
+ NULL, 0, buf, rcount, &rcount);
+ *countp_char = rcount;
+
+ mtx_unlock(&sc->mutex);
+ return (error);
+}
+
+int
+ig4iic_smb_trans(device_t dev, int slave, char cmd, int op,
+ char *wbuf, int wcount, char *rbuf, int rcount,
+ int *actualp)
+{
+ ig4iic_softc_t *sc = device_get_softc(dev);
+ int error;
+
+ mtx_lock(&sc->mutex);
+
+ set_slave_addr(sc, slave, op);
+ error = smb_transaction(sc, cmd, op,
+ wbuf, wcount, rbuf, rcount, actualp);
+
+ mtx_unlock(&sc->mutex);
+ return (error);
+}
+
+/*
+ * Interrupt Operation
+ */
+static void
+ig4iic_intr(void *cookie)
+{
+ ig4iic_softc_t *sc = cookie;
+ uint32_t status;
+
+ mtx_lock(&sc->mutex);
+/* reg_write(sc, IG4_REG_INTR_MASK, IG4_INTR_STOP_DET);*/
+ status = reg_read(sc, IG4_REG_I2C_STA);
+ while (status & IG4_STATUS_RX_NOTEMPTY) {
+ sc->rbuf[sc->rnext & IG4_RBUFMASK] =
+ (uint8_t)reg_read(sc, IG4_REG_DATA_CMD);
+ ++sc->rnext;
+ status = reg_read(sc, IG4_REG_I2C_STA);
+ }
+ reg_read(sc, IG4_REG_CLR_INTR);
+ wakeup(sc);
+ mtx_unlock(&sc->mutex);
+}
+
+#define REGDUMP(sc, reg) \
+ device_printf(sc->dev, " %-23s %08x\n", #reg, reg_read(sc, reg))
+
+static void
+ig4iic_dump(ig4iic_softc_t *sc)
+{
+ device_printf(sc->dev, "ig4iic register dump:\n");
+ REGDUMP(sc, IG4_REG_CTL);
+ REGDUMP(sc, IG4_REG_TAR_ADD);
+ REGDUMP(sc, IG4_REG_SS_SCL_HCNT);
+ REGDUMP(sc, IG4_REG_SS_SCL_LCNT);
+ REGDUMP(sc, IG4_REG_FS_SCL_HCNT);
+ REGDUMP(sc, IG4_REG_FS_SCL_LCNT);
+ REGDUMP(sc, IG4_REG_INTR_STAT);
+ REGDUMP(sc, IG4_REG_INTR_MASK);
+ REGDUMP(sc, IG4_REG_RAW_INTR_STAT);
+ REGDUMP(sc, IG4_REG_RX_TL);
+ REGDUMP(sc, IG4_REG_TX_TL);
+ REGDUMP(sc, IG4_REG_I2C_EN);
+ REGDUMP(sc, IG4_REG_I2C_STA);
+ REGDUMP(sc, IG4_REG_TXFLR);
+ REGDUMP(sc, IG4_REG_RXFLR);
+ REGDUMP(sc, IG4_REG_SDA_HOLD);
+ REGDUMP(sc, IG4_REG_TX_ABRT_SOURCE);
+ REGDUMP(sc, IG4_REG_SLV_DATA_NACK);
+ REGDUMP(sc, IG4_REG_DMA_CTRL);
+ REGDUMP(sc, IG4_REG_DMA_TDLR);
+ REGDUMP(sc, IG4_REG_DMA_RDLR);
+ REGDUMP(sc, IG4_REG_SDA_SETUP);
+ REGDUMP(sc, IG4_REG_ENABLE_STATUS);
+ REGDUMP(sc, IG4_REG_COMP_PARAM1);
+ REGDUMP(sc, IG4_REG_COMP_VER);
+ REGDUMP(sc, IG4_REG_COMP_TYPE);
+ REGDUMP(sc, IG4_REG_CLK_PARMS);
+ REGDUMP(sc, IG4_REG_RESETS);
+ REGDUMP(sc, IG4_REG_GENERAL);
+ REGDUMP(sc, IG4_REG_SW_LTR_VALUE);
+ REGDUMP(sc, IG4_REG_AUTO_LTR_VALUE);
+}
+#undef REGDUMP
+
+DRIVER_MODULE(smbus, ig4iic, smbus_driver, smbus_devclass, NULL, NULL);
diff --git a/sys/dev/ichiic/ig4_pci.c b/sys/dev/ichiic/ig4_pci.c
new file mode 100644
index 0000000..42c3664
--- /dev/null
+++ b/sys/dev/ichiic/ig4_pci.c
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2014 The DragonFly Project. All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Matthew Dillon <dillon@backplane.com> and was subsequently ported
+ * to FreeBSD by Michael Gmelin <freebsd@grem.de>
+ *
+ * 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.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Intel fourth generation mobile cpus integrated I2C device, smbus driver.
+ *
+ * See ig4_reg.h for datasheet reference and notes.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/errno.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/syslog.h>
+#include <sys/bus.h>
+
+#include <machine/bus.h>
+#include <sys/rman.h>
+#include <machine/resource.h>
+
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
+#include <dev/smbus/smbconf.h>
+
+#include "smbus_if.h"
+
+#include <dev/ichiic/ig4_reg.h>
+#include <dev/ichiic/ig4_var.h>
+
+static int ig4iic_pci_detach(device_t dev);
+
+#define PCI_CHIP_LYNXPT_LP_I2C_1 0x9c618086
+#define PCI_CHIP_LYNXPT_LP_I2C_2 0x9c628086
+
+static int
+ig4iic_pci_probe(device_t dev)
+{
+ switch(pci_get_devid(dev)) {
+ case PCI_CHIP_LYNXPT_LP_I2C_1:
+ device_set_desc(dev, "Intel Lynx Point-LP I2C Controller-1");
+ break;
+ case PCI_CHIP_LYNXPT_LP_I2C_2:
+ device_set_desc(dev, "Intel Lynx Point-LP I2C Controller-2");
+ break;
+ default:
+ return (ENXIO);
+ }
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+ig4iic_pci_attach(device_t dev)
+{
+ ig4iic_softc_t *sc = device_get_softc(dev);
+ int error;
+
+ bzero(sc, sizeof(*sc));
+
+ mtx_init(&sc->mutex, device_get_nameunit(dev), "ig4iic", MTX_DEF);
+
+ sc->dev = dev;
+ sc->regs_rid = PCIR_BAR(0);
+ sc->regs_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &sc->regs_rid, RF_ACTIVE);
+ if (sc->regs_res == NULL) {
+ device_printf(dev, "unable to map registers\n");
+ ig4iic_pci_detach(dev);
+ return (ENXIO);
+ }
+ sc->intr_rid = 0;
+ if (pci_alloc_msi(dev, &sc->intr_rid)) {
+ device_printf(dev, "Using MSI\n");
+ }
+ sc->intr_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
+ &sc->intr_rid, RF_SHAREABLE | RF_ACTIVE);
+ if (sc->intr_res == NULL) {
+ device_printf(dev, "unable to map interrupt\n");
+ ig4iic_pci_detach(dev);
+ return (ENXIO);
+ }
+ sc->pci_attached = 1;
+
+ error = ig4iic_attach(sc);
+ if (error)
+ ig4iic_pci_detach(dev);
+
+ return (error);
+}
+
+static int
+ig4iic_pci_detach(device_t dev)
+{
+ ig4iic_softc_t *sc = device_get_softc(dev);
+ int error;
+
+ if (sc->pci_attached) {
+ error = ig4iic_detach(sc);
+ if (error)
+ return (error);
+ sc->pci_attached = 0;
+ }
+
+ if (sc->intr_res) {
+ bus_release_resource(dev, SYS_RES_IRQ,
+ sc->intr_rid, sc->intr_res);
+ sc->intr_res = NULL;
+ }
+ if (sc->intr_rid != 0)
+ pci_release_msi(dev);
+ if (sc->regs_res) {
+ bus_release_resource(dev, SYS_RES_MEMORY,
+ sc->regs_rid, sc->regs_res);
+ sc->regs_res = NULL;
+ }
+ mtx_destroy(&sc->mutex);
+
+ return (0);
+}
+
+static device_method_t ig4iic_pci_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, ig4iic_pci_probe),
+ DEVMETHOD(device_attach, ig4iic_pci_attach),
+ DEVMETHOD(device_detach, ig4iic_pci_detach),
+
+ /* SMBus methods from ig4_smb.c */
+ DEVMETHOD(smbus_callback, ig4iic_smb_callback),
+ DEVMETHOD(smbus_quick, ig4iic_smb_quick),
+ DEVMETHOD(smbus_sendb, ig4iic_smb_sendb),
+ DEVMETHOD(smbus_recvb, ig4iic_smb_recvb),
+ DEVMETHOD(smbus_writeb, ig4iic_smb_writeb),
+ DEVMETHOD(smbus_writew, ig4iic_smb_writew),
+ DEVMETHOD(smbus_readb, ig4iic_smb_readb),
+ DEVMETHOD(smbus_readw, ig4iic_smb_readw),
+ DEVMETHOD(smbus_pcall, ig4iic_smb_pcall),
+ DEVMETHOD(smbus_bwrite, ig4iic_smb_bwrite),
+ DEVMETHOD(smbus_bread, ig4iic_smb_bread),
+ DEVMETHOD(smbus_trans, ig4iic_smb_trans),
+
+ DEVMETHOD_END
+};
+
+static driver_t ig4iic_pci_driver = {
+ "ig4iic",
+ ig4iic_pci_methods,
+ sizeof(struct ig4iic_softc)
+};
+
+static devclass_t ig4iic_pci_devclass;
+
+DRIVER_MODULE(ig4iic, pci, ig4iic_pci_driver, ig4iic_pci_devclass, 0, 0);
+MODULE_DEPEND(ig4iic, pci, 1, 1, 1);
+MODULE_DEPEND(ig4iic, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
+MODULE_VERSION(ig4iic, 1);
diff --git a/sys/dev/ichiic/ig4_reg.h b/sys/dev/ichiic/ig4_reg.h
new file mode 100644
index 0000000..c87d1d1
--- /dev/null
+++ b/sys/dev/ichiic/ig4_reg.h
@@ -0,0 +1,622 @@
+/*
+ * Copyright (c) 2014 The DragonFly Project. All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Matthew Dillon <dillon@backplane.com> and was subsequently ported
+ * to FreeBSD by Michael Gmelin <freebsd@grem.de>
+ *
+ * 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.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT HOLDERS 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.
+ *
+ * $FreeBSD$
+ */
+/*
+ * Intel fourth generation mobile cpus integrated I2C device.
+ *
+ * Datasheet reference: Section 22.
+ *
+ * http://www.intel.com/content/www/us/en/processors/core/4th-gen-core-family-mobile-i-o-datasheet.html?wapkw=datasheets+4th+generation
+ *
+ * This is a from-scratch driver under the BSD license using the Intel data
+ * sheet and the linux driver for reference. All code is freshly written
+ * without referencing the linux driver code. However, during testing
+ * I am also using the linux driver code as a reference to help resolve any
+ * issues that come. These will be specifically documented in the code.
+ *
+ * Please see protocol notes in section 5.21. This controller is an I2C
+ * master only and cannot act as a slave. The IO voltage should be set by
+ * the BIOS. Standard (100Kb/s) and Fast (400Kb/s) and fast mode plus
+ * (1MB/s) is supported. High speed mode (3.4 MB/s) is NOT supported.
+ */
+
+#ifndef _BUS_SMBUS_INTELGEN4_IG4_REG_H_
+#define _BUS_SMBUS_INTELGEN4_IG4_REG_H_
+
+/*
+ * 22.2 MMIO registers can be accessed through BAR0 in PCI mode or through
+ * BAR1 when in ACPI mode.
+ *
+ * Register width is 32-bits
+ *
+ * 22.2 Default Values on device reset are 0 except as specified here:
+ * TAR_ADD 0x00000055
+ * SS_SCL_HCNT 0x00000264
+ * SS_SCL_LCNT 0x000002C2
+ * FS_SCL_HCNT 0x0000006E
+ * FS_SCL_LCNT 0x000000CF
+ * INTR_MASK 0x000008FF
+ * I2C_STA 0x00000006
+ * SDA_HOLD 0x00000001
+ * SDA_SETUP 0x00000064
+ * COMP_PARAM1 0x00FFFF6E
+ * COMP_VER 0x3131352A
+ */
+
+#define IG4_REG_CTL 0x0000 /* RW Control Register */
+#define IG4_REG_TAR_ADD 0x0004 /* RW Target Address */
+#define IG4_REG_DATA_CMD 0x0010 /* RW Data Buffer and Command */
+#define IG4_REG_SS_SCL_HCNT 0x0014 /* RW Std Speed clock High Count */
+#define IG4_REG_SS_SCL_LCNT 0x0018 /* RW Std Speed clock Low Count */
+#define IG4_REG_FS_SCL_HCNT 0x001C /* RW Fast Speed clock High Count */
+#define IG4_REG_FS_SCL_LCNT 0x0020 /* RW Fast Speed clock Low Count */
+#define IG4_REG_INTR_STAT 0x002C /* RO Interrupt Status */
+#define IG4_REG_INTR_MASK 0x0030 /* RW Interrupt Mask */
+#define IG4_REG_RAW_INTR_STAT 0x0034 /* RO Raw Interrupt Status */
+#define IG4_REG_RX_TL 0x0038 /* RW Receive FIFO Threshold */
+#define IG4_REG_TX_TL 0x003C /* RW Transmit FIFO Threshold */
+#define IG4_REG_CLR_INTR 0x0040 /* RO Clear Interrupt */
+#define IG4_REG_CLR_RX_UNDER 0x0044 /* RO Clear RX_Under Interrupt */
+#define IG4_REG_CLR_RX_OVER 0x0048 /* RO Clear RX_Over Interrupt */
+#define IG4_REG_CLR_TX_OVER 0x004C /* RO Clear TX_Over Interrupt */
+#define IG4_REG_CLR_TX_ABORT 0x0054 /* RO Clear TX_Abort Interrupt */
+#define IG4_REG_CLR_ACTIVITY 0x005C /* RO Clear Activity Interrupt */
+#define IG4_REG_CLR_STOP_DET 0x0060 /* RO Clear STOP Detection Int */
+#define IG4_REG_CLR_START_DET 0x0064 /* RO Clear START Detection Int */
+#define IG4_REG_CLR_GEN_CALL 0x0068 /* RO Clear General Call Interrupt */
+#define IG4_REG_I2C_EN 0x006C /* RW I2C Enable */
+#define IG4_REG_I2C_STA 0x0070 /* RO I2C Status */
+#define IG4_REG_TXFLR 0x0074 /* RO Transmit FIFO Level */
+#define IG4_REG_RXFLR 0x0078 /* RO Receive FIFO Level */
+#define IG4_REG_SDA_HOLD 0x007C /* RW SDA Hold Time Length */
+#define IG4_REG_TX_ABRT_SOURCE 0x0080 /* RO Transmit Abort Source */
+#define IG4_REG_SLV_DATA_NACK 0x0084 /* RW General Slave Data NACK */
+#define IG4_REG_DMA_CTRL 0x0088 /* RW DMA Control */
+#define IG4_REG_DMA_TDLR 0x008C /* RW DMA Transmit Data Level */
+#define IG4_REG_DMA_RDLR 0x0090 /* RW DMA Receive Data Level */
+#define IG4_REG_SDA_SETUP 0x0094 /* RW SDA Setup */
+#define IG4_REG_ENABLE_STATUS 0x009C /* RO Enable Status */
+#define IG4_REG_COMP_PARAM1 0x00F4 /* RO Component Parameter */
+#define IG4_REG_COMP_VER 0x00F8 /* RO Component Version */
+#define IG4_REG_COMP_TYPE 0x00FC /* RO Probe width/endian? (linux) */
+#define IG4_REG_CLK_PARMS 0x0800 /* RW Clock Parameters */
+#define IG4_REG_RESETS 0x0804 /* RW Reset Register */
+#define IG4_REG_GENERAL 0x0808 /* RW General Register */
+#define IG4_REG_SW_LTR_VALUE 0x0810 /* RW SW LTR Value */
+#define IG4_REG_AUTO_LTR_VALUE 0x0814 /* RW Auto LTR Value */
+
+/*
+ * CTL - Control Register 22.2.1
+ * Default Value: 0x0000007F.
+ *
+ * RESTARTEN - RW Restart Enable
+ * 10BIT - RW Controller operates in 10-bit mode, else 7-bit
+ *
+ * NOTE: When restart is disabled the controller is incapable of
+ * performing the following functions:
+ *
+ * Sending a START Byte
+ * Performing any high-speed mode op
+ * Performing direction changes in combined format mode
+ * Performing a read operation with a 10-bit address
+ *
+ * Attempting to perform the above operations will result in the
+ * TX_ABORT bit being set in RAW_INTR_STAT.
+ */
+#define IG4_CTL_SLAVE_DISABLE 0x0040 /* snarfed from linux */
+#define IG4_CTL_RESTARTEN 0x0020 /* Allow Restart when master */
+#define IG4_CTL_10BIT 0x0010 /* ctlr accepts 10-bit addresses */
+#define IG4_CTL_SPEED_FAST 0x0004 /* snarfed from linux */
+#define IG4_CTL_SPEED_STD 0x0002 /* snarfed from linux */
+#define IG4_CTL_MASTER 0x0001 /* snarfed from linux */
+
+/*
+ * TAR_ADD - Target Address Register 22.2.2
+ * Default Value: 0x00000055F
+ *
+ * 10BIT - RW controller starts its transfers in 10-bit
+ * address mode, else 7-bit.
+ *
+ * SPECIAL - RW Indicates whether software performs a General Call
+ * or START BYTE command.
+ *
+ * 0 Ignore GC_OR_START and use TAR address.
+ *
+ * 1 Perform special I2C Command based on GC_OR_START.
+ *
+ * GC_OR_START - RW (only if SPECIAL is set)
+ *
+ * 0 General Call Address. After issuing a General Call,
+ * only writes may be performed. Attempting to issue
+ * a read command results in IX_ABRT in RAW_INTR_STAT.
+ * The controller remains in General Call mode until
+ * bit 11 (SPECIAL) is cleared.
+ *
+ * 1 START BYTE.
+ *
+ *
+ * IC_TAR - RW when transmitting a general call, these bits are
+ * ignored. To generate a START BYTE, the address
+ * needs to be written into these bits once.
+ *
+ * This register should only be updated when the IIC is disabled (I2C_ENABLE=0)
+ */
+#define IG4_TAR_10BIT 0x1000 /* start xfer in 10-bit mode */
+#define IG4_TAR_SPECIAL 0x0800 /* Perform special command */
+#define IG4_TAR_GC_OR_START 0x0400 /* General Call or Start */
+#define IG4_TAR_ADDR_MASK 0x03FF /* Target address */
+
+/*
+ * TAR_DATA_CMD - Data Buffer and Command Register 22.2.3
+ *
+ * RESTART - RW This bit controls whether a forced RESTART is
+ * issued before the byte is sent or received.
+ *
+ * 0 If not set a RESTART is only issued if the tranfer
+ * direction is changing from the previous command.
+ *
+ * 1 A RESTART is issued before the byte is sent or
+ * received, regardless of whether or not the transfer
+ * direction is changing from the previous command.
+ *
+ * STOP - RW This bit controls whether a STOP is issued after
+ * the byte is sent or received.
+ *
+ * 0 STOP is not issued after this byte, regardless
+ * of whether or not the Tx FIFO is empty.
+ *
+ * 1 STOP is issued after this byte, regardless of
+ * whether or not the Tx FIFO is empty. If the
+ * Tx FIFO is not empty the master immediately tries
+ * to start a new transfer by issuing a START and
+ * arbitrating for the bus.
+ *
+ * i.e. the STOP is issued along with this byte,
+ * within the write stream.
+ *
+ * COMMAND - RW Control whether a read or write is performed.
+ *
+ * 0 WRITE
+ *
+ * 1 READ
+ *
+ * DATA (7:0) - RW Contains the data to be transmitted or received
+ * on the I2C bus.
+ *
+ * NOTE: Writing to this register causes a START + slave + RW to be
+ * issued if the direction has changed or the last data byte was
+ * sent with a STOP.
+ *
+ * NOTE: We control termination? so this register must be written
+ * for each byte we wish to receive. We can then drain the
+ * receive FIFO.
+ */
+
+#define IG4_DATA_RESTART 0x0400 /* Force RESTART */
+#define IG4_DATA_STOP 0x0200 /* Force STOP[+START] */
+#define IG4_DATA_COMMAND_RD 0x0100 /* bus direction 0=write 1=read */
+#define IG4_DATA_MASK 0x00FF
+
+/*
+ * SS_SCL_HCNT - Standard Speed Clock High Count Register 22.2.4
+ * SS_SCL_LCNT - Standard Speed Clock Low Count Register 22.2.5
+ * FS_SCL_HCNT - Fast Speed Clock High Count Register 22.2.6
+ * FS_SCL_LCNT - Fast Speed Clock Low Count Register 22.2.7
+ *
+ * COUNT (15:0) - Set the period count to a value between 6 and
+ * 65525.
+ */
+#define IG4_SCL_CLOCK_MASK 0xFFFFU /* count bits in register */
+
+/*
+ * INTR_STAT - (RO) Interrupt Status Register 22.2.8
+ * INTR_MASK - (RW) Interrupt Mask Register 22.2.9
+ * RAW_INTR_STAT- (RO) Raw Interrupt Status Register 22.2.10
+ *
+ * GEN_CALL Set only when a general call (broadcast) address
+ * is received and acknowleged, stays set until
+ * cleared by reading CLR_GEN_CALL.
+ *
+ * START_DET Set when a START or RESTART condition has occurred
+ * on the interface.
+ *
+ * STOP_DET Set when a STOP condition has occurred on the
+ * interface.
+ *
+ * ACTIVITY Set by any activity on the interface. Cleared
+ * by reading CLR_ACTIVITY or CLR_INTR.
+ *
+ * TX_ABRT Indicates the controller as a transmitter is
+ * unable to complete the intended action. When set,
+ * the controller will hold the TX FIFO in a reset
+ * state (flushed) until CLR_TX_ABORT is read to
+ * clear the condition. Once cleared, the TX FIFO
+ * will be available again.
+ *
+ * TX_EMPTY Indicates that the transmitter is at or below
+ * the specified TX_TL threshold. Automatically
+ * cleared by HW when the buffer level goes above
+ * the threshold.
+ *
+ * TX_OVER Indicates that the processer attempted to write
+ * to the TX FIFO while the TX FIFO was full. Cleared
+ * by reading CLR_TX_OVER.
+ *
+ * RX_FULL Indicates that the receive FIFO has reached or
+ * exceeded the specified RX_TL threshold. Cleared
+ * by HW when the cpu drains the FIFO to below the
+ * threshold.
+ *
+ * RX_OVER Indicates that the receive FIFO was unable to
+ * accept new data and data was lost. Cleared by
+ * reading CLR_RX_OVER.
+ *
+ * RX_UNDER Indicates that the cpu attempted to read data
+ * from the receive buffer while the RX FIFO was
+ * empty. Cleared by reading CLR_RX_UNDER.
+ *
+ * NOTES ON RAW_INTR_STAT:
+ *
+ * This register can be used to monitor the GEN_CALL, START_DET,
+ * STOP_DET, ACTIVITY, TX_ABRT, TX_EMPTY, TX_OVER, RX_FULL, RX_OVER,
+ * and RX_UNDER bits. The documentation is a bit unclear but presumably
+ * this is the unlatched version.
+ *
+ * Code should test FIFO conditions using the I2C_STA (status) register,
+ * not the interrupt status registers.
+ */
+
+#define IG4_INTR_GEN_CALL 0x0800
+#define IG4_INTR_START_DET 0x0400
+#define IG4_INTR_STOP_DET 0x0200
+#define IG4_INTR_ACTIVITY 0x0100
+#define IG4_INTR_TX_ABRT 0x0040
+#define IG4_INTR_TX_EMPTY 0x0010
+#define IG4_INTR_TX_OVER 0x0008
+#define IG4_INTR_RX_FULL 0x0004
+#define IG4_INTR_RX_OVER 0x0002
+#define IG4_INTR_RX_UNDER 0x0001
+
+/*
+ * RX_TL - (RW) Receive FIFO Threshold Register 22.2.11
+ * TX_TL - (RW) Transmit FIFO Threshold Register 22.2.12
+ *
+ * Specify the receive and transmit FIFO threshold register. The
+ * FIFOs have 16 elements. The valid range is 0-15. Setting a
+ * value greater than 15 causes the actual value to be the maximum
+ * depth of the FIFO.
+ *
+ * Generally speaking since everything is messaged, we can use a
+ * mid-level setting for both parameters and (e.g.) fully drain the
+ * receive FIFO on the STOP_DET condition to handle loose ends.
+ */
+#define IG4_FIFO_MASK 0x00FF
+#define IG4_FIFO_LIMIT 16
+
+/*
+ * CLR_INTR - (RO) Clear Interrupt Register 22.2.13
+ * CLR_RX_UNDER - (RO) Clear Interrupt Register (specific) 22.2.14
+ * CLR_RX_OVER - (RO) Clear Interrupt Register (specific) 22.2.15
+ * CLR_TX_OVER - (RO) Clear Interrupt Register (specific) 22.2.16
+ * CLR_TX_ABORT - (RO) Clear Interrupt Register (specific) 22.2.17
+ * CLR_ACTIVITY - (RO) Clear Interrupt Register (specific) 22.2.18
+ * CLR_STOP_DET - (RO) Clear Interrupt Register (specific) 22.2.19
+ * CLR_START_DET- (RO) Clear Interrupt Register (specific) 22.2.20
+ * CLR_GEN_CALL - (RO) Clear Interrupt Register (specific) 22.2.21
+ *
+ * CLR_* specific operations clear the appropriate bit in the
+ * RAW_INTR_STAT register. Intel does not really document whether
+ * these operations clear the normal interrupt status register.
+ *
+ * CLR_INTR clears bits in the normal interrupt status register and
+ * presumably also the raw(?) register? Intel is again unclear.
+ *
+ * NOTE: CLR_INTR only clears software-clearable interrupts. Hardware
+ * clearable interrupts are controlled entirely by the hardware.
+ * CLR_INTR also clears the TX_ABRT_SOURCE register.
+ *
+ * NOTE: CLR_TX_ABORT also clears the TX_ABRT_SOURCE register and releases
+ * the TX FIFO from its flushed/reset state, allowing more writes
+ * to the TX FIFO.
+ *
+ * NOTE: CLR_ACTIVITY has no effect if the I2C bus is still active.
+ * Intel documents that the bit is automatically cleared when
+ * there is no further activity on the bus.
+ */
+#define IG4_CLR_BIT 0x0001 /* Reflects source */
+
+/*
+ * I2C_EN - (RW) I2C Enable Register 22.2.22
+ *
+ * ABORT Software can abort an I2C transfer by setting this
+ * bit. Hardware will clear the bit once the STOP has
+ * been detected. This bit can only be set while the
+ * I2C interface is enabled.
+ *
+ * I2C_ENABLE Enable the controller, else disable it.
+ * (Use I2C_ENABLE_STATUS to poll enable status
+ * & wait for changes)
+ */
+#define IG4_I2C_ABORT 0x0002
+#define IG4_I2C_ENABLE 0x0001
+
+/*
+ * I2C_STA - (RO) I2C Status Register 22.2.23
+ */
+#define IG4_STATUS_ACTIVITY 0x0020 /* Controller is active */
+#define IG4_STATUS_RX_FULL 0x0010 /* RX FIFO completely full */
+#define IG4_STATUS_RX_NOTEMPTY 0x0008 /* RX FIFO not empty */
+#define IG4_STATUS_TX_EMPTY 0x0004 /* TX FIFO completely empty */
+#define IG4_STATUS_TX_NOTFULL 0x0002 /* TX FIFO not full */
+#define IG4_STATUS_I2C_ACTIVE 0x0001 /* I2C bus is active */
+
+/*
+ * TXFLR - (RO) Transmit FIFO Level Register 22.2.24
+ * RXFLR - (RO) Receive FIFO Level Register 22.2.25
+ *
+ * Read the number of entries currently in the Transmit or Receive
+ * FIFOs. Note that for some reason the mask is 9 bits instead of
+ * the 8 bits the fill level controls.
+ */
+#define IG4_FIFOLVL_MASK 0x001F
+
+/*
+ * SDA_HOLD - (RW) SDA Hold Time Length Register 22.2.26
+ *
+ * Set the SDA hold time length register in I2C clocks.
+ */
+#define IG4_SDA_HOLD_MASK 0x00FF
+
+/*
+ * TX_ABRT_SOURCE- (RO) Transmit Abort Source Register 22.2.27
+ *
+ * Indicates the cause of a transmit abort. This can indicate a
+ * software programming error or a device expected address width
+ * mismatch or other issues. The NORESTART conditions and GENCALL_NOACK
+ * can only occur if a programming error was made in the driver software.
+ *
+ * In particular, it should be possible to detect whether any devices
+ * are on the bus by observing the GENCALL_READ status, and it might
+ * be possible to detect ADDR7 vs ADDR10 mismatches.
+ */
+#define IG4_ABRTSRC_TRANSFER 0x00010000 /* Abort initiated by user */
+#define IG4_ABRTSRC_ARBLOST 0x00001000 /* Arbitration lost */
+#define IG4_ABRTSRC_NORESTART_10 0x00000400 /* RESTART disabled */
+#define IG4_ABRTSRC_NORESTART_START 0x00000200 /* RESTART disabled */
+#define IG4_ABRTSRC_ACKED_START 0x00000080 /* Improper acked START */
+#define IG4_ABRTSRC_GENCALL_NOACK 0x00000020 /* Improper GENCALL */
+#define IG4_ABRTSRC_GENCALL_READ 0x00000010 /* Nobody acked GENCALL */
+#define IG4_ABRTSRC_TXNOACK_DATA 0x00000008 /* data phase no ACK */
+#define IG4_ABRTSRC_TXNOACK_ADDR10_2 0x00000004 /* addr10/1 phase no ACK */
+#define IG4_ABRTSRC_TXNOACK_ADDR10_1 0x00000002 /* addr10/2 phase no ACK */
+#define IG4_ABRTSRC_TXNOACK_ADDR7 0x00000001 /* addr7 phase no ACK */
+
+/*
+ * SLV_DATA_NACK - (RW) Generate Slave DATA NACK Register 22.2.28
+ *
+ * When the controller is a receiver a NACK can be generated on
+ * receipt of data.
+ *
+ * NACK_GENERATE Set to 0 for normal NACK/ACK generation.
+ * Set to 1 to generate a NACK after next data
+ * byte received.
+ *
+ */
+#define IG4_NACK_GENERATE 0x0001
+
+/*
+ * DMA_CTRL - (RW) DMA Control Register 22.2.29
+ *
+ * Enables DMA on the transmit and/or receive DMA channel.
+ */
+#define IG4_TX_DMA_ENABLE 0x0002
+#define IG4_RX_DMA_ENABLE 0x0001
+
+/*
+ * DMA_TDLR - (RW) DMA Transmit Data Level Register 22.2.30
+ * DMA_RDLR - (RW) DMA Receive Data Level Register 22.2.31
+ *
+ * Similar to RX_TL and TX_TL but controls when a DMA burst occurs
+ * to empty or fill the FIFOs. Use the same IG4_FIFO_MASK and
+ * IG4_FIFO_LIMIT defines for RX_RL and TX_TL.
+ */
+/* empty */
+
+/*
+ * SDA_SETUP - (RW) SDA Setup Time Length Register 22.2.32
+ *
+ * Set the SDA setup time length register in I2C clocks.
+ * The register must be programmed with a value >=2.
+ * (Defaults to 0x64).
+ */
+#define IG4_SDA_SETUP_MASK 0x00FF
+
+/*
+ * ACK_GEN_CALL - (RW) ACK General Call Register 22.2.33
+ *
+ * Control whether the controller responds with a ACK or NACK when
+ * it receives an I2C General Call address.
+ *
+ * If set to 0 a NACK is generated and a General Call interrupt is
+ * NOT generated. Otherwise an ACK + interrupt is generated.
+ */
+#define IG4_ACKGC_ACK 0x0001
+
+/*
+ * ENABLE_STATUS - (RO) Enable Status Registger 22.2.34
+ *
+ * DATA_LOST - Indicates that a slave receiver operation has
+ * been aborted with at least one data byte received
+ * from a transfer due to the I2C controller being
+ * disabled (IG4_I2C_ENABLE -> 0)
+ *
+ * ENABLED - Intel documentation is lacking but I assume this
+ * is a reflection of the IG4_I2C_ENABLE bit in the
+ * I2C_EN register.
+ *
+ */
+#define IG4_ENASTAT_DATA_LOST 0x0004
+#define IG4_ENASTAT_ENABLED 0x0001
+
+/*
+ * COMP_PARAM1 - (RO) Component Parameter Register 22.2.35
+ * Default Value 0x00FFFF6E
+ *
+ * VALID - Intel documentation is unclear but I believe this
+ * must be read as a 1 to indicate that the rest of
+ * the bits in the register are valid.
+ *
+ * HASDMA - Indicates that the chip is DMA-capable. Presumably
+ * in certain virtualization cases the chip might be
+ * set to not be DMA-capable.
+ *
+ * INTR_IO - Indicates that all interrupts are combined to
+ * generate one interrupt. If not set, interrupts
+ * are individual (more virtualization stuff?)
+ *
+ * HCCNT_RO - Indicates that the clock timing registers are
+ * RW. If not set, the registers are RO.
+ * (more virtualization stuff).
+ *
+ * MAXSPEED - Indicates the maximum speed supported.
+ *
+ * DATAW - Indicates the internal bus width in bits.
+ */
+#define IG4_PARAM1_TXFIFO_DEPTH(v) (((v) >> 16) & 0xFF)
+#define IG4_PARAM1_RXFIFO_DEPTH(v) (((v) >> 8) & 0xFF)
+#define IG4_PARAM1_CONFIG_VALID 0x00000080
+#define IG4_PARAM1_CONFIG_HASDMA 0x00000040
+#define IG4_PARAM1_CONFIG_INTR_IO 0x00000020
+#define IG4_PARAM1_CONFIG_HCCNT_RO 0x00000010
+#define IG4_PARAM1_CONFIG_MAXSPEED_MASK 0x0000000C
+#define IG4_PARAM1_CONFIG_DATAW_MASK 0x00000003
+
+#define IG4_CONFIG_MAXSPEED_RESERVED00 0x00000000
+#define IG4_CONFIG_MAXSPEED_STANDARD 0x00000004
+#define IG4_CONFIG_MAXSPEED_FAST 0x00000008
+#define IG4_CONFIG_MAXSPEED_HIGH 0x0000000C
+
+#define IG4_CONFIG_DATAW_8 0x00000000
+#define IG4_CONFIG_DATAW_16 0x00000001
+#define IG4_CONFIG_DATAW_32 0x00000002
+#define IG4_CONFIG_DATAW_RESERVED11 0x00000003
+
+/*
+ * COMP_VER - (RO) Component Version Register 22.2.36
+ * Default Value 0x3131352A
+ *
+ * Contains the chip version number. All 32 bits.
+ */
+#define IG4_COMP_VER 0x3131352A
+
+/*
+ * COMP_TYPE - (RO) (linux) Endian and bus width probe
+ *
+ * Read32 from this register and test against IG4_COMP_TYPE
+ * to determine the bus width. e.g. 01404457 = endian-reversed,
+ * and 00000140 or 00004457 means internal 16-bit bus (?).
+ *
+ * This register is not in the intel documentation, I pulled it
+ * from the linux driver i2c-designware-core.c.
+ */
+#define IG4_COMP_TYPE 0x44570140
+
+/*
+ * RESETS - (RW) Resets Register 22.2.37
+ *
+ * Used to reset the I2C host controller by SW. There is no timing
+ * requirement, software can assert and de-assert in back-to-back
+ * transactions.
+ *
+ * 00 I2C host controller is NOT in reset.
+ * 01 (reserved)
+ * 10 (reserved)
+ * 11 I2C host controller is in reset.
+ */
+#define IG4_RESETS_ASSERT 0x0003
+#define IG4_RESETS_DEASSERT 0x0000
+
+/*
+ * GENERAL - (RW) General Reigster 22.2.38
+ *
+ * IOVOLT 0=1.8V 1=3.3V
+ *
+ * LTR 0=Auto 1=SW
+ *
+ * In Auto mode the BIOS will write to the host controller's
+ * AUTO LTR Value register (offset 0x0814) with the active
+ * state LTR value, and will write to the SW LTR Value register
+ * (offset 0x0810) with the idle state LTR value.
+ *
+ * In SW mode the SW will write to the host controller SW LTR
+ * value (offset 0x0810). It is the SW responsibility to update
+ * the LTR with the appropriate value.
+ */
+#define IG4_GENERAL_IOVOLT3_3 0x0008
+#define IG4_GENERAL_SWMODE 0x0004
+
+/*
+ * SW_LTR_VALUE - (RW) SW LTR Value Register 22.2.39
+ * AUTO_LTR_VALUE - (RW) SW LTR Value Register 22.2.40
+ *
+ * Default value is 0x00000800 which means the best possible
+ * service/response time.
+ *
+ * It isn't quite clear how the snooping works. There are two scale
+ * bits for both sets but two of the four codes are reserved. The
+ * *SNOOP_VALUE() is specified as a 10-bit latency value. If 0, it
+ * indicates that the device cannot tolerate any delay and needs the
+ * best possible service/response time.
+ *
+ * I think this is for snooping (testing) the I2C bus. The lowest
+ * delay (0) probably runs the controller polling at a high, power hungry
+ * rate. But I dunno.
+ */
+#define IG4_SWLTR_NSNOOP_REQ 0x80000000 /* (ro) */
+#define IG4_SWLTR_NSNOOP_SCALE_MASK 0x1C000000 /* (ro) */
+#define IG4_SWLTR_NSNOOP_SCALE_1US 0x08000000 /* (ro) */
+#define IG4_SWLTR_NSNOOP_SCALE_32US 0x0C000000 /* (ro) */
+#define IG4_SWLTR_NSNOOP_VALUE_DECODE(v) (((v) >> 16) & 0x3F)
+#define IG4_SWLTR_NSNOOP_VALUE_ENCODE(v) (((v) & 0x3F) << 16)
+
+#define IG4_SWLTR_SNOOP_REQ 0x00008000 /* (rw) */
+#define IG4_SWLTR_SNOOP_SCALE_MASK 0x00001C00 /* (rw) */
+#define IG4_SWLTR_SNOOP_SCALE_1US 0x00000800 /* (rw) */
+#define IG4_SWLTR_SNOOP_SCALE_32US 0x00000C00 /* (rw) */
+#define IG4_SWLTR_SNOOP_VALUE_DECODE(v) ((v) & 0x3F)
+#define IG4_SWLTR_SNOOP_VALUE_ENCODE(v) ((v) & 0x3F)
+
+#endif
diff --git a/sys/dev/ichiic/ig4_var.h b/sys/dev/ichiic/ig4_var.h
new file mode 100644
index 0000000..6c91055
--- /dev/null
+++ b/sys/dev/ichiic/ig4_var.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2014 The DragonFly Project. All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Matthew Dillon <dillon@backplane.com> and was subsequently ported
+ * to FreeBSD by Michael Gmelin <freebsd@grem.de>
+ *
+ * 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.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT HOLDERS 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _BUS_SMBUS_INTELGEN4_IG4_VAR_H_
+#define _BUS_SMBUS_INTELGEN4_IG4_VAR_H_
+
+#include "bus_if.h"
+#include "device_if.h"
+#include "pci_if.h"
+#include "smbus_if.h"
+
+#define IG4_RBUFSIZE 128
+#define IG4_RBUFMASK (IG4_RBUFSIZE - 1)
+
+enum ig4_op { IG4_IDLE, IG4_READ, IG4_WRITE };
+
+struct ig4iic_softc {
+ device_t dev;
+ struct intr_config_hook enum_hook;
+ device_t smb;
+ struct resource *regs_res;
+ int regs_rid;
+ struct resource *intr_res;
+ int intr_rid;
+ void *intr_handle;
+ int intr_type;
+ enum ig4_op op;
+ int cmd;
+ int rnext;
+ int rpos;
+ char rbuf[IG4_RBUFSIZE];
+ int error;
+ uint8_t last_slave;
+ int pci_attached : 1;
+ int use_10bit : 1;
+ int slave_valid : 1;
+ int read_started : 1;
+ int write_started : 1;
+ struct mtx mutex;
+};
+
+typedef struct ig4iic_softc ig4iic_softc_t;
+
+/* Attach/Detach called from ig4iic_pci_*() */
+int ig4iic_attach(ig4iic_softc_t *sc);
+int ig4iic_detach(ig4iic_softc_t *sc);
+
+/* SMBus methods */
+extern smbus_callback_t ig4iic_smb_callback;
+extern smbus_quick_t ig4iic_smb_quick;
+extern smbus_sendb_t ig4iic_smb_sendb;
+extern smbus_recvb_t ig4iic_smb_recvb;
+extern smbus_writeb_t ig4iic_smb_writeb;
+extern smbus_writew_t ig4iic_smb_writew;
+extern smbus_readb_t ig4iic_smb_readb;
+extern smbus_readw_t ig4iic_smb_readw;
+extern smbus_pcall_t ig4iic_smb_pcall;
+extern smbus_bwrite_t ig4iic_smb_bwrite;
+extern smbus_bread_t ig4iic_smb_bread;
+extern smbus_trans_t ig4iic_smb_trans;
+
+#endif
OpenPOWER on IntegriCloud