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
This commit is contained in:
Michael Gmelin 2015-05-30 12:17:18 +00:00
parent ca4a0f7b65
commit 71d51719ea
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=283766
9 changed files with 1967 additions and 1 deletions

View File

@ -192,6 +192,7 @@ MAN= aac.4 \
icmp6.4 \
ida.4 \
ifmib.4 \
ig4.4 \
igb.4 \
igmp.4 \
iic.4 \

79
share/man/man4/ig4.4 Normal file
View File

@ -0,0 +1,79 @@
.\" Copyright (c) 2015 Michael Gmelin <freebsd@grem.de>
.\" 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 May 30, 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 .

View File

@ -1427,6 +1427,8 @@ dev/hptiop/hptiop.c optional hptiop scbus
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

966
sys/dev/ichiic/ig4_iic.c Normal file
View File

@ -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);

192
sys/dev/ichiic/ig4_pci.c Normal file
View File

@ -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);

622
sys/dev/ichiic/ig4_reg.h Normal file
View File

@ -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

96
sys/dev/ichiic/ig4_var.h Normal file
View File

@ -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

View File

@ -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 <bsd.subdir.mk>

View File

@ -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 <bsd.kmod.mk>