Add support for Intel ICH SMBus controller.

This commit is contained in:
kiyohara 2007-07-28 10:51:56 +00:00
parent 33f2f6779a
commit 37b3e2d574
7 changed files with 584 additions and 5 deletions

View File

@ -1,4 +1,4 @@
# $NetBSD: mi,v 1.1014 2007/07/14 20:05:35 adrianp Exp $
# $NetBSD: mi,v 1.1015 2007/07/28 10:51:59 kiyohara Exp $
./etc/mtree/set.man man-sys-root
./usr/share/info/am-utils.info man-amd-info info
./usr/share/info/as.info man-computil-info bfd,info
@ -985,6 +985,7 @@
./usr/share/man/cat4/i4btrc.0 man-obsolete obsolete
./usr/share/man/cat4/i915drm.0 man-sys-catman .cat
./usr/share/man/cat4/iavc.0 man-sys-catman .cat
./usr/share/man/cat4/ichsmp.0 man-sys-catman .cat
./usr/share/man/cat4/icmp.0 man-sys-catman .cat
./usr/share/man/cat4/icmp6.0 man-sys-catman .cat
./usr/share/man/cat4/icp.0 man-sys-catman .cat
@ -3463,6 +3464,7 @@
./usr/share/man/man4/i4btrc.4 man-obsolete obsolete
./usr/share/man/man4/i915drm.4 man-sys-man .man
./usr/share/man/man4/iavc.4 man-sys-man .man
./usr/share/man/cat4/ichsmp.4 man-sys-man .man
./usr/share/man/man4/icmp.4 man-sys-man .man
./usr/share/man/man4/icmp6.4 man-sys-man .man
./usr/share/man/man4/icp.4 man-sys-man .man

View File

@ -1,4 +1,4 @@
LIST OF CHANGES FROM LAST RELEASE: <$Revision: 1.893 $>
LIST OF CHANGES FROM LAST RELEASE: <$Revision: 1.894 $>
[Note: This file does not mention every change made to the NetBSD source tree.
@ -171,3 +171,5 @@ Changes from NetBSD 4.0 to NetBSD 5.0:
with MI mc146818 driver. [tsutsui 20070721]
bind: import 9.4.1-P1 [christos 20070724]
mpt(4): Add support for newer SAS and similar devices. [tron 20070727]
ichsmb(4): Add support for Intel ICH SMBus controller.
[kiyohara 20070728]

View File

@ -1,4 +1,4 @@
# $NetBSD: Makefile,v 1.434 2007/07/11 07:53:30 kiyohara Exp $
# $NetBSD: Makefile,v 1.435 2007/07/28 10:51:58 kiyohara Exp $
# @(#)Makefile 8.1 (Berkeley) 6/18/93
MAN= aac.4 ac97.4 acardide.4 aceride.4 acphy.4 adbbt.4 adbkbd.4 adbms.4 \
@ -20,7 +20,7 @@ MAN= aac.4 ac97.4 acardide.4 aceride.4 acphy.4 adbbt.4 adbkbd.4 adbms.4 \
esa.4 esiop.4 esl.4 esm.4 eso.4 etherip.4 exphy.4 fast_ipsec.4 fd.4 \
fpa.4 fms.4 fss.4 fxp.4 gem.4 genfb.4 gentbi.4 \
glxtphy.4 gpib.4 gpio.4 gre.4 gphyter.4 gsip.4 hifn.4 hme.4 \
hptide.4 icmp.4 icp.4 icsphy.4 iee.4 ieee80211.4 ifmedia.4 \
hptide.4 ichsmb.4 icmp.4 icp.4 icsphy.4 iee.4 ieee80211.4 ifmedia.4 \
igsfb.4 iha.4 inet.4 ikphy.4 inphy.4 intersil7170.4 \
ioasic.4 ioat.4 iop.4 iophy.4 iopsp.4 ip.4 ipkdb.4 ipmi.4 ipw.4 \
iso.4 isp.4 it.4 iteide.4 iwi.4 ixpide.4 jmide.4 joy.4 kloader.4 kse.4 \

61
share/man/man4/ichsmb.4 Normal file
View File

@ -0,0 +1,61 @@
.\" $NetBSD: ichsmb.4,v 1.1 2007/07/28 10:51:58 kiyohara Exp $
.\" $OpenBSD: ichiic.4,v 1.10 2007/05/31 19:19:50 jmc Exp $
.\"
.\" Copyright (c) 2005 Alexander Yurchenko <grange@openbsd.org>
.\"
.\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
.\" copyright notice and this permission notice appear in all copies.
.\"
.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.Dd July 28, 2007
.Dt ICHSMB 4
.Os
.Sh NAME
.Nm ichsmb
.Nd Intel ICH SMBus controller
.Sh SYNOPSIS
.Cd "ichsmb* at pci?"
.Cd "iic* at ichsmb?"
.Sh DESCRIPTION
The
.Nm
driver provides support for the Intel ICH SMBus host interface to be
used with the
.Xr iic 4
framework.
.Pp
Supported chipsets:
.Pp
.Bl -bullet -compact -offset indent
.It
Intel ICH, ICH2, ICH3, ICH4, ICH4-M, ICH5, ICH5R, ICH6, ICH6-M, ICH6R, ICH7,
ICH8, C-ICH, 6300ESB and 6321ESB.
.El
.Sh SEE ALSO
.Xr iic 4 ,
.Xr intro 4 ,
.Xr pci 4
.Sh HISTORY
The
.Nm
driver first appeared in
.Ox 4.0 .
This driver imports from
.Ox 3.9 .
.Sh AUTHORS
.An -nosplit
The
.Nm
driver was written by
.An Alexander Yurchenko Aq grange@openbsd.org .
.Sh BUGS
The driver doesn't support I2C commands with a data buffer size of more
than 2 bytes.

View File

@ -1,4 +1,4 @@
# $NetBSD: files.pci,v 1.294 2007/07/11 07:53:29 kiyohara Exp $
# $NetBSD: files.pci,v 1.295 2007/07/28 10:51:57 kiyohara Exp $
#
# Config file and device description for machine-independent PCI code.
# Included by ports that need it. Requires that the SCSI files be
@ -902,3 +902,8 @@ attach nfsmbc at pci
device nfsmb: i2cbus
attach nfsmb at nfsmbc
file dev/pci/nfsmb.c nfsmbc | nfsmb
# Intel ICH SMBus controller
device ichsmb: i2cbus
attach ichsmb at pci
file dev/pci/ichsmb.c ichsmb

149
sys/dev/pci/ichreg.h Normal file
View File

@ -0,0 +1,149 @@
/* $OpenBSD: ichreg.h,v 1.7 2005/12/18 12:09:04 grange Exp $ */
/*
* Copyright (c) 2004, 2005 Alexander Yurchenko <grange@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef _DEV_PCI_ICHREG_H_
#define _DEV_PCI_ICHREG_H_
/*
* Intel I/O Controller Hub (ICH) register definitions.
*/
/*
* LPC interface bridge registers.
*/
/* PCI configuration registers */
#define ICH_PMBASE 0x40 /* ACPI base address */
#define ICH_ACPI_CNTL 0x44 /* ACPI control */
#define ICH_ACPI_CNTL_ACPI_EN (1 << 4) /* ACPI enable */
#define ICH_GEN_PMCON1 0xa0 /* general PM configuration */
/* ICHx-M only */
#define ICH_GEN_PMCON1_SS_EN 0x08 /* enable SpeedStep */
/* Power management I/O registers */
#define ICH_PM_TMR 0x08 /* PM timer */
/* ICHx-M only */
#define ICH_PM_CNTL 0x20 /* power management control */
#define ICH_PM_ARB_DIS 0x01 /* disable arbiter */
#define ICH_PM_SS_CNTL 0x50 /* SpeedStep control */
#define ICH_PM_SS_STATE_LOW 0x01 /* low power state */
#define ICH_PMSIZE 128 /* ACPI I/O space size */
/*
* SMBus controller registers.
*/
/* PCI configuration registers */
#define ICH_SMB_BASE 0x20 /* SMBus base address */
#define ICH_SMB_HOSTC 0x40 /* host configuration */
#define ICH_SMB_HOSTC_HSTEN (1 << 0) /* enable host controller */
#define ICH_SMB_HOSTC_SMIEN (1 << 1) /* generate SMI */
#define ICH_SMB_HOSTC_I2CEN (1 << 2) /* enable I2C commands */
/* SMBus I/O registers */
#define ICH_SMB_HS 0x00 /* host status */
#define ICH_SMB_HS_BUSY (1 << 0) /* running a command */
#define ICH_SMB_HS_INTR (1 << 1) /* command completed */
#define ICH_SMB_HS_DEVERR (1 << 2) /* command error */
#define ICH_SMB_HS_BUSERR (1 << 3) /* transaction collision */
#define ICH_SMB_HS_FAILED (1 << 4) /* failed bus transaction */
#define ICH_SMB_HS_SMBAL (1 << 5) /* SMBALERT# asserted */
#define ICH_SMB_HS_INUSE (1 << 6) /* bus semaphore */
#define ICH_SMB_HS_BDONE (1 << 7) /* byte received/transmitted */
#define ICH_SMB_HS_BITS "\020\001BUSY\002INTR\003DEVERR\004BUSERR\005FAILED\006SMBAL\007INUSE\010BDONE"
#define ICH_SMB_HC 0x02 /* host control */
#define ICH_SMB_HC_INTREN (1 << 0) /* enable interrupts */
#define ICH_SMB_HC_KILL (1 << 1) /* kill current transaction */
#define ICH_SMB_HC_CMD_QUICK (0 << 2) /* QUICK command */
#define ICH_SMB_HC_CMD_BYTE (1 << 2) /* BYTE command */
#define ICH_SMB_HC_CMD_BDATA (2 << 2) /* BYTE DATA command */
#define ICH_SMB_HC_CMD_WDATA (3 << 2) /* WORD DATA command */
#define ICH_SMB_HC_CMD_PCALL (4 << 2) /* PROCESS CALL command */
#define ICH_SMB_HC_CMD_BLOCK (5 << 2) /* BLOCK command */
#define ICH_SMB_HC_CMD_I2CREAD (6 << 2) /* I2C READ command */
#define ICH_SMB_HC_CMD_BLOCKP (7 << 2) /* BLOCK PROCESS command */
#define ICH_SMB_HC_LASTB (1 << 5) /* last byte in block */
#define ICH_SMB_HC_START (1 << 6) /* start transaction */
#define ICH_SMB_HC_PECEN (1 << 7) /* enable PEC */
#define ICH_SMB_HCMD 0x03 /* host command */
#define ICH_SMB_TXSLVA 0x04 /* transmit slave address */
#define ICH_SMB_TXSLVA_READ (1 << 0) /* read direction */
#define ICH_SMB_TXSLVA_ADDR(x) (((x) & 0x7f) << 1) /* 7-bit address */
#define ICH_SMB_HD0 0x05 /* host data 0 */
#define ICH_SMB_HD1 0x06 /* host data 1 */
#define ICH_SMB_HBDB 0x07 /* host block data byte */
#define ICH_SMB_PEC 0x08 /* PEC data */
#define ICH_SMB_RXSLVA 0x09 /* receive slave address */
#define ICH_SMB_SD 0x0a /* receive slave data */
#define ICH_SMB_SD_MSG0(x) ((x) & 0xff) /* data message byte 0 */
#define ICH_SMB_SD_MSG1(x) ((x) >> 8) /* data message byte 1 */
#define ICH_SMB_AS 0x0c /* auxiliary status */
#define ICH_SMB_AS_CRCE (1 << 0) /* CRC error */
#define ICH_SMB_AS_TCO (1 << 1) /* advanced TCO mode */
#define ICH_SMB_AC 0x0d /* auxiliary control */
#define ICH_SMB_AC_AAC (1 << 0) /* automatically append CRC */
#define ICH_SMB_AC_E32B (1 << 1) /* enable 32-byte buffer */
#define ICH_SMB_SMLPC 0x0e /* SMLink pin control */
#define ICH_SMB_SMLPC_LINK0 (1 << 0) /* SMLINK0 pin state */
#define ICH_SMB_SMLPC_LINK1 (1 << 1) /* SMLINK1 pin state */
#define ICH_SMB_SMLPC_CLKC (1 << 2) /* SMLINK0 pin is untouched */
#define ICH_SMB_SMBPC 0x0f /* SMBus pin control */
#define ICH_SMB_SMBPC_CLK (1 << 0) /* SMBCLK pin state */
#define ICH_SMB_SMBPC_DATA (1 << 1) /* SMBDATA pin state */
#define ICH_SMB_SMBPC_CLKC (1 << 2) /* SMBCLK pin is untouched */
#define ICH_SMB_SS 0x10 /* slave status */
#define ICH_SMB_SS_HN (1 << 0) /* Host Notify command */
#define ICH_SMB_SCMD 0x11 /* slave command */
#define ICH_SMB_SCMD_INTREN (1 << 0) /* enable interrupts on HN */
#define ICH_SMB_SCMD_WKEN (1 << 1) /* wake on HN */
#define ICH_SMB_SCMD_SMBALDS (1 << 2) /* disable SMBALERT# intr */
#define ICH_SMB_NDADDR 0x14 /* notify device address */
#define ICH_SMB_NDADDR_ADDR(x) ((x) >> 1) /* 7-bit address */
#define ICH_SMB_NDLOW 0x16 /* notify data low byte */
#define ICH_SMB_NDHIGH 0x17 /* notify data high byte */
/*
* 6300ESB watchdog timer registers.
*/
/* PCI configuration registers */
#define ICH_WDT_BASE 0x10 /* memory space base address */
#define ICH_WDT_CONF 0x60 /* configuration register */
#define ICH_WDT_CONF_MASK 0xffff /* 16-bit register */
#define ICH_WDT_CONF_INT_MASK 0x3 /* interrupt type */
#define ICH_WDT_CONF_INT_IRQ 0x0 /* IRQ (APIC 1, INT 10) */
#define ICH_WDT_CONF_INT_SMI 0x2 /* SMI */
#define ICH_WDT_CONF_INT_DIS 0x3 /* disabled */
#define ICH_WDT_CONF_PRE (1 << 2) /* 2^5 clock divisor */
#define ICH_WDT_CONF_OUTDIS (1 << 5) /* WDT_TOUT# output disabled */
#define ICH_WDT_LOCK 0x68 /* lock register */
#define ICH_WDT_LOCK_LOCKED (1 << 0) /* register locked */
#define ICH_WDT_LOCK_ENABLED (1 << 1) /* WDT enabled */
#define ICH_WDT_LOCK_FREERUN (1 << 2) /* free running mode */
/* Memory mapped registers */
#define ICH_WDT_PRE1 0x00 /* preload value 1 */
#define ICH_WDT_PRE2 0x04 /* preload value 2 */
#define ICH_WDT_GIS 0x08 /* general interrupt status */
#define ICH_WDT_GIS_ACTIVE (1 << 0) /* interrupt active */
#define ICH_WDT_RELOAD 0x0c /* reload register */
#define ICH_WDT_RELOAD_RLD (1 << 8) /* safe reload */
#define ICH_WDT_RELOAD_TIMEOUT (1 << 9) /* timeout occured */
#endif /* !_DEV_PCI_ICHREG_H_ */

360
sys/dev/pci/ichsmb.c Normal file
View File

@ -0,0 +1,360 @@
/* $NetBSD: ichsmb.c,v 1.1 2007/07/28 10:51:57 kiyohara Exp $ */
/* $OpenBSD: ichiic.c,v 1.18 2007/05/03 09:36:26 dlg Exp $ */
/*
* Copyright (c) 2005, 2006 Alexander Yurchenko <grange@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* Intel ICH SMBus controller driver.
*/
#include <sys/param.h>
#include <sys/device.h>
#include <sys/errno.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/proc.h>
#include <machine/bus.h>
#include <dev/pci/pcidevs.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/ichreg.h>
#include <dev/i2c/i2cvar.h>
#ifdef ICHIIC_DEBUG
#define DPRINTF(x) printf x
#else
#define DPRINTF(x)
#endif
#define ICHIIC_DELAY 100
#define ICHIIC_TIMEOUT 1
struct ichsmb_softc {
struct device sc_dev;
bus_space_tag_t sc_iot;
bus_space_handle_t sc_ioh;
void * sc_ih;
int sc_poll;
struct i2c_controller sc_i2c_tag;
struct lock sc_i2c_lock;
struct {
i2c_op_t op;
void * buf;
size_t len;
int flags;
volatile int error;
} sc_i2c_xfer;
};
static int ichsmb_match(struct device *, struct cfdata *, void *);
static void ichsmb_attach(struct device *, struct device *, void *);
static int ichsmb_i2c_acquire_bus(void *, int);
static void ichsmb_i2c_release_bus(void *, int);
static int ichsmb_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *,
size_t, void *, size_t, int);
static int ichsmb_intr(void *);
CFATTACH_DECL(ichsmb, sizeof(struct ichsmb_softc),
ichsmb_match, ichsmb_attach, NULL, NULL);
static int
ichsmb_match(struct device *parent, struct cfdata *match, void *aux)
{
struct pci_attach_args *pa = aux;
if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_INTEL) {
switch (PCI_PRODUCT(pa->pa_id)) {
case PCI_PRODUCT_INTEL_6300ESB_SMB:
case PCI_PRODUCT_INTEL_63XXESB_SMB:
case PCI_PRODUCT_INTEL_82801AA_SMB:
case PCI_PRODUCT_INTEL_82801AB_SMB:
case PCI_PRODUCT_INTEL_82801BA_SMB:
case PCI_PRODUCT_INTEL_82801CA_SMB:
case PCI_PRODUCT_INTEL_82801DB_SMB:
case PCI_PRODUCT_INTEL_82801E_SMB:
case PCI_PRODUCT_INTEL_82801EB_SMB:
case PCI_PRODUCT_INTEL_82801FB_SMB:
case PCI_PRODUCT_INTEL_82801G_SMB:
case PCI_PRODUCT_INTEL_82801H_SMB:
return 1;
}
}
return 0;
}
static void
ichsmb_attach(struct device *parent, struct device *self, void *aux)
{
struct ichsmb_softc *sc = (struct ichsmb_softc *)self;
struct pci_attach_args *pa = aux;
struct i2cbus_attach_args iba;
pcireg_t conf;
bus_size_t iosize;
pci_intr_handle_t ih;
const char *intrstr = NULL;
char devinfo[256];
aprint_naive("\n");
pci_devinfo(pa->pa_id, pa->pa_class, 0, devinfo, sizeof(devinfo));
aprint_normal(": %s (rev. 0x%02x)\n", devinfo,
PCI_REVISION(pa->pa_class));
/* Read configuration */
conf = pci_conf_read(pa->pa_pc, pa->pa_tag, ICH_SMB_HOSTC);
DPRINTF(("%s: conf 0x%08x", sc->sc_dev.dv_xname, conf));
if ((conf & ICH_SMB_HOSTC_HSTEN) == 0) {
aprint_error("%s: SMBus disabled\n", sc->sc_dev.dv_xname);
return;
}
/* Map I/O space */
if (pci_mapreg_map(pa, ICH_SMB_BASE, PCI_MAPREG_TYPE_IO, 0,
&sc->sc_iot, &sc->sc_ioh, NULL, &iosize)) {
aprint_error("%s: can't map I/O space\n", sc->sc_dev.dv_xname);
return;
}
sc->sc_poll = 1;
if (conf & ICH_SMB_HOSTC_SMIEN) {
/* No PCI IRQ */
aprint_normal("%s: SMI\n", sc->sc_dev.dv_xname);
} else {
/* Install interrupt handler */
if (pci_intr_map(pa, &ih) == 0) {
intrstr = pci_intr_string(pa->pa_pc, ih);
sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_BIO,
ichsmb_intr, sc);
if (sc->sc_ih != NULL) {
aprint_normal("%s: interrupting at %s\n",
sc->sc_dev.dv_xname, intrstr);
sc->sc_poll = 0;
}
}
if (sc->sc_poll)
aprint_normal("%s: polling\n", sc->sc_dev.dv_xname);
}
/* Attach I2C bus */
lockinit(&sc->sc_i2c_lock, PZERO, "smblk", 0, 0);
sc->sc_i2c_tag.ic_cookie = sc;
sc->sc_i2c_tag.ic_acquire_bus = ichsmb_i2c_acquire_bus;
sc->sc_i2c_tag.ic_release_bus = ichsmb_i2c_release_bus;
sc->sc_i2c_tag.ic_exec = ichsmb_i2c_exec;
bzero(&iba, sizeof(iba));
iba.iba_tag = &sc->sc_i2c_tag;
config_found(self, &iba, iicbus_print);
return;
}
static int
ichsmb_i2c_acquire_bus(void *cookie, int flags)
{
struct ichsmb_softc *sc = cookie;
if (cold || sc->sc_poll || (flags & I2C_F_POLL))
return (0);
return (lockmgr(&sc->sc_i2c_lock, LK_EXCLUSIVE, NULL));
}
static void
ichsmb_i2c_release_bus(void *cookie, int flags)
{
struct ichsmb_softc *sc = cookie;
if (cold || sc->sc_poll || (flags & I2C_F_POLL))
return;
lockmgr(&sc->sc_i2c_lock, LK_RELEASE, NULL);
}
static int
ichsmb_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr,
const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags)
{
struct ichsmb_softc *sc = cookie;
const uint8_t *b;
uint8_t ctl = 0, st;
int retries;
DPRINTF(("%s: exec: op %d, addr 0x%02x, cmdlen %zd, len %d, "
"flags 0x%02x\n", sc->sc_dev.dv_xname, op, addr, cmdlen,
len, flags));
/* Wait for bus to be idle */
for (retries = 100; retries > 0; retries--) {
st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HS);
if (!(st & ICH_SMB_HS_BUSY))
break;
DELAY(ICHIIC_DELAY);
}
DPRINTF(("%s: exec: st 0x%02x\n", sc->sc_dev.dv_xname, st));
if (st & ICH_SMB_HS_BUSY)
return (1);
if (cold || sc->sc_poll)
flags |= I2C_F_POLL;
if (!I2C_OP_STOP_P(op) || cmdlen > 1 || len > 2)
return (1);
/* Setup transfer */
sc->sc_i2c_xfer.op = op;
sc->sc_i2c_xfer.buf = buf;
sc->sc_i2c_xfer.len = len;
sc->sc_i2c_xfer.flags = flags;
sc->sc_i2c_xfer.error = 0;
/* Set slave address and transfer direction */
bus_space_write_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_TXSLVA,
ICH_SMB_TXSLVA_ADDR(addr) |
(I2C_OP_READ_P(op) ? ICH_SMB_TXSLVA_READ : 0));
b = (const uint8_t *)cmdbuf;
if (cmdlen > 0)
/* Set command byte */
bus_space_write_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HCMD, b[0]);
if (I2C_OP_WRITE_P(op)) {
/* Write data */
b = buf;
if (len > 0)
bus_space_write_1(sc->sc_iot, sc->sc_ioh,
ICH_SMB_HD0, b[0]);
if (len > 1)
bus_space_write_1(sc->sc_iot, sc->sc_ioh,
ICH_SMB_HD1, b[1]);
}
/* Set SMBus command */
if (len == 0)
ctl = ICH_SMB_HC_CMD_BYTE;
else if (len == 1)
ctl = ICH_SMB_HC_CMD_BDATA;
else if (len == 2)
ctl = ICH_SMB_HC_CMD_WDATA;
if ((flags & I2C_F_POLL) == 0)
ctl |= ICH_SMB_HC_INTREN;
/* Start transaction */
ctl |= ICH_SMB_HC_START;
bus_space_write_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HC, ctl);
if (flags & I2C_F_POLL) {
/* Poll for completion */
DELAY(ICHIIC_DELAY);
for (retries = 1000; retries > 0; retries--) {
st = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
ICH_SMB_HS);
if ((st & ICH_SMB_HS_BUSY) == 0)
break;
DELAY(ICHIIC_DELAY);
}
if (st & ICH_SMB_HS_BUSY)
goto timeout;
ichsmb_intr(sc);
} else {
/* Wait for interrupt */
if (tsleep(sc, PRIBIO, "iicexec", ICHIIC_TIMEOUT * hz))
goto timeout;
}
if (sc->sc_i2c_xfer.error)
return (1);
return (0);
timeout:
/*
* Transfer timeout. Kill the transaction and clear status bits.
*/
printf("%s: exec: op %d, addr 0x%02x, cmdlen %zd, len %d, "
"flags 0x%02x: timeout, status 0x%02x\n",
sc->sc_dev.dv_xname, op, addr, cmdlen, len, flags,
st);
bus_space_write_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HC,
ICH_SMB_HC_KILL);
DELAY(ICHIIC_DELAY);
st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HS);
if ((st & ICH_SMB_HS_FAILED) == 0)
printf("%s: abort failed, status 0x%02x\n",
sc->sc_dev.dv_xname, st);
bus_space_write_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HS, st);
return (1);
}
static int
ichsmb_intr(void *arg)
{
struct ichsmb_softc *sc = arg;
uint8_t st;
uint8_t *b;
size_t len;
/* Read status */
st = bus_space_read_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HS);
if ((st & ICH_SMB_HS_BUSY) != 0 || (st & (ICH_SMB_HS_INTR |
ICH_SMB_HS_DEVERR | ICH_SMB_HS_BUSERR | ICH_SMB_HS_FAILED |
ICH_SMB_HS_SMBAL | ICH_SMB_HS_BDONE)) == 0)
/* Interrupt was not for us */
return (0);
DPRINTF(("%s: intr st 0x%02x\n", sc->sc_dev.dv_xname, st));
/* Clear status bits */
bus_space_write_1(sc->sc_iot, sc->sc_ioh, ICH_SMB_HS, st);
/* Check for errors */
if (st & (ICH_SMB_HS_DEVERR | ICH_SMB_HS_BUSERR | ICH_SMB_HS_FAILED)) {
sc->sc_i2c_xfer.error = 1;
goto done;
}
if (st & ICH_SMB_HS_INTR) {
if (I2C_OP_WRITE_P(sc->sc_i2c_xfer.op))
goto done;
/* Read data */
b = sc->sc_i2c_xfer.buf;
len = sc->sc_i2c_xfer.len;
if (len > 0)
b[0] = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
ICH_SMB_HD0);
if (len > 1)
b[1] = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
ICH_SMB_HD1);
}
done:
if ((sc->sc_i2c_xfer.flags & I2C_F_POLL) == 0)
wakeup(sc);
return (1);
}