Index: share/man/man4/Makefile =================================================================== --- share/man/man4/Makefile +++ share/man/man4/Makefile @@ -192,6 +192,7 @@ icmp6.4 \ ida.4 \ ifmib.4 \ + ig4.4 \ igb.4 \ igmp.4 \ iic.4 \ Index: share/man/man4/ig4.4 =================================================================== --- /dev/null +++ share/man/man4/ig4.4 @@ -0,0 +1,79 @@ +.\" Copyright (c) 2015 Michael Gmelin +.\" All rights reserved. +.\" +.\" 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 AUTHOR 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 AUTHOR 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$ +.\" +.Dd April 28, 2015 +.Dt IG4 4 +.Os +.Sh NAME +.Nm ig4 +.Nd Intel(R) fourth generation mobile CPU integrated I2C SMBus driver +.Sh SYNOPSIS +To compile this driver into the kernel, place the following lines into +the kernel configuration file: +.Bd -ragged -offset indent +.Cd "device ig4" +.Cd "device smbus" +.Ed +.Pp +Alternatively, to load the driver as a module at boot time, place the following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +ig4_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +driver provides access to peripherals attached to an I2C SMB controller. +.Nm +supports the SMBus controller found in fourth generation Intel(R) Core(TM) +processors based on the mobile U-processor line for intelligent systems. +This includes the i7-4650U, i5-4300U, i3-4010U, and 2980U. +.Sh SYSCTL VARIABLES +These +.Xr sysctl 8 +variables are available: +.Bl -tag -width "debug.ig4_dump" +.It Va debug.ig4_dump +Setting this to a non-zero value dumps controller registers to console and +syslog once. +The sysctl resets to zero immediately. +.El +.Sh SEE ALSO +.Xr smb 4 , +.Xr smbus 4 +.Sh AUTHORS +.An -nosplit +The +.Nm +driver was written for DragonFly BSD by +.An Matthew Dillon +and subsequently ported to +.Fx +by +.An Michael Gmelin Aq Mt freebsd@grem.de . +.Pp +This manual page was written by +.An Michael Gmelin Aq Mt freebsd@grem.de . Index: sys/conf/files =================================================================== --- sys/conf/files +++ sys/conf/files @@ -1428,6 +1428,8 @@ dev/hwpmc/hwpmc_logging.c optional hwpmc dev/hwpmc/hwpmc_mod.c optional hwpmc dev/hwpmc/hwpmc_soft.c optional hwpmc +dev/ichiic/ig4_iic.c optional ichiic +dev/ichiic/ig4_pci.c optional ichiic pci dev/ichsmb/ichsmb.c optional ichsmb dev/ichsmb/ichsmb_pci.c optional ichsmb pci dev/ida/ida.c optional ida Index: sys/dev/ichiic/ig4_iic.c =================================================================== --- /dev/null +++ sys/dev/ichiic/ig4_iic.c @@ -0,0 +1,993 @@ +/* + * Copyright (c) 2014 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by Matthew Dillon + * + * 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. + */ +/* + * Intel fourth generation mobile cpus integrated I2C device, smbus driver. + * + * See ig4_reg.h for datasheet reference and notes. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + +#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_space_write_4(sc->regs_t, sc->regs_h, reg, value); + bus_space_barrier(sc->regs_t, sc->regs_h, reg, 4, + BUS_SPACE_BARRIER_WRITE); +} + +static __inline uint32_t +reg_read(ig4iic_softc_t *sc, uint32_t reg) +{ + uint32_t value; + + bus_space_barrier(sc->regs_t, sc->regs_h, reg, 4, + BUS_SPACE_BARRIER_READ); + value = bus_space_read_4(sc->regs_t, sc->regs_h, 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; + u_int limit; + + error = SMB_ETIMEOUT; + /* XXX ticks are not very precise, we should fix that */ + count = ticks; + limit = 5; /* XXX:: was 3 */ /* (25000 / tick) + 0.5; */ + + 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 = ticks; + } + } + + /* + * Stop if we've run out of time. + */ + if (ticks - count > limit) + 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", 10); /*(hz + 99) / 100);*/ + } else { + DELAY(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; + + mtx_lock(&sc->mutex); + + v = reg_read(sc, IG4_REG_COMP_TYPE); + device_printf(sc->dev, "type %08x", v); + v = reg_read(sc, IG4_REG_COMP_PARAM1); + device_printf(sc->dev, " params %08x", v); + v = reg_read(sc, IG4_REG_GENERAL); + device_printf(sc->dev, " general %08x", v); + if ((v & IG4_GENERAL_SWMODE) == 0) { + v |= IG4_GENERAL_SWMODE; + reg_write(sc, IG4_REG_GENERAL, v); + v = reg_read(sc, IG4_REG_GENERAL); + device_printf(sc->dev, " (updated %08x)", v); + } + + v = reg_read(sc, IG4_REG_SW_LTR_VALUE); + device_printf(sc->dev, " swltr %08x", v); + v = reg_read(sc, IG4_REG_AUTO_LTR_VALUE); + device_printf(sc->dev, " autoltr %08x", v); + + v = reg_read(sc, IG4_REG_COMP_VER); + device_printf(sc->dev, " version %08x\n", v); + if (v != IG4_COMP_VER) { + error = ENXIO; + goto done; + } + v = reg_read(sc, IG4_REG_SS_SCL_HCNT); + device_printf(sc->dev, "SS_SCL_HCNT=%08x", v); + v = reg_read(sc, IG4_REG_SS_SCL_LCNT); + device_printf(sc->dev, " LCNT=%08x", v); + v = reg_read(sc, IG4_REG_FS_SCL_HCNT); + device_printf(sc->dev, " FS_SCL_HCNT=%08x", v); + v = reg_read(sc, IG4_REG_FS_SCL_LCNT); + device_printf(sc->dev, " LCNT=%08x\n", v); + v = reg_read(sc, IG4_REG_SDA_HOLD); + device_printf(sc->dev, "HOLD %08x\n", v); + + 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); + 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); + mtx_lock(&sc->mutex); + if (error) { + device_printf(sc->dev, + "Unable to setup irq: error %d\n", error); + goto done; + } + + 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. + */ + mtx_unlock(&sc->mutex); + if (config_intrhook_establish(&sc->enum_hook) != 0) + error = ENOMEM; + else + error = 0; + + mtx_lock(&sc->mutex); + +done: + mtx_unlock(&sc->mutex); + 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); + mtx_lock(&sc->mutex); + if (error) { + device_printf(sc->dev, + "failed to attach child: error %d\n", error); + goto done; + } + sc->generic_attached = 1; + +done: + mtx_unlock(&sc->mutex); +} + + + +int +ig4iic_detach(ig4iic_softc_t *sc) +{ + int error; + + mtx_lock(&sc->mutex); + + reg_write(sc, IG4_REG_INTR_MASK, 0); + reg_read(sc, IG4_REG_CLR_INTR); + set_controller(sc, 0); + + if (sc->generic_attached) { + error = bus_generic_detach(sc->dev); + if (error) + goto done; + sc->generic_attached = 0; + } + if (sc->smb) { + device_delete_child(sc->dev, sc->smb); + sc->smb = NULL; + } + if (sc->intr_handle) { + bus_teardown_intr(sc->dev, sc->intr_res, sc->intr_handle); + sc->intr_handle = NULL; + } + + error = 0; +done: + mtx_unlock(&sc->mutex); + return (error); +} + +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); Index: sys/dev/ichiic/ig4_pci.c =================================================================== --- /dev/null +++ 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 + * + * 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. + */ +/* + * Intel fourth generation mobile cpus integrated I2C device, smbus driver. + * + * See ig4_reg.h for datasheet reference and notes. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "smbus_if.h" + +#include +#include + +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 msi_enable = 1; + 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 (msi_enable && 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->regs_t = rman_get_bustag(sc->regs_res); + sc->regs_h = rman_get_bushandle(sc->regs_res); + 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; + } + sc->regs_t = 0; + sc->regs_h = 0; + 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); Index: sys/dev/ichiic/ig4_reg.h =================================================================== --- /dev/null +++ sys/dev/ichiic/ig4_reg.h @@ -0,0 +1,619 @@ +/* + * Copyright (c) 2014 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by Matthew Dillon + * + * 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. + */ +/* + * 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 Index: sys/dev/ichiic/ig4_var.h =================================================================== --- /dev/null +++ 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 + * + * 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. + */ + +#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; + bus_space_tag_t regs_t; + bus_space_handle_t regs_h; + 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 generic_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 Index: sys/modules/i2c/controllers/Makefile =================================================================== --- sys/modules/i2c/controllers/Makefile +++ sys/modules/i2c/controllers/Makefile @@ -3,7 +3,7 @@ .if ${MACHINE} == "pc98" SUBDIR = lpbb .else -SUBDIR = alpm amdpm amdsmb ichsmb intpm ismt nfsmb viapm lpbb pcf +SUBDIR = alpm amdpm amdsmb ichiic ichsmb intpm ismt nfsmb viapm lpbb pcf .endif .include Index: sys/modules/i2c/controllers/ichiic/Makefile =================================================================== --- /dev/null +++ sys/modules/i2c/controllers/ichiic/Makefile @@ -0,0 +1,8 @@ +#$FreeBSD$ + +.PATH: ${.CURDIR}/../../../../dev/ichiic +KMOD = ig4 +SRCS = device_if.h bus_if.h iicbb_if.h pci_if.h smbus_if.h \ + ig4_iic.c ig4_pci.c ig4_reg.h ig4_var.h + +.include