mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-15 10:17:20 +00:00
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:
parent
ca4a0f7b65
commit
71d51719ea
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=283766
@ -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
79
share/man/man4/ig4.4
Normal 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 .
|
@ -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
966
sys/dev/ichiic/ig4_iic.c
Normal 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
192
sys/dev/ichiic/ig4_pci.c
Normal 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
622
sys/dev/ichiic/ig4_reg.h
Normal 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
96
sys/dev/ichiic/ig4_var.h
Normal 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
|
@ -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>
|
||||
|
8
sys/modules/i2c/controllers/ichiic/Makefile
Normal file
8
sys/modules/i2c/controllers/ichiic/Makefile
Normal 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>
|
Loading…
Reference in New Issue
Block a user